Merge branch 'next' into feat-db-ssl
This commit is contained in:
@@ -12,19 +12,24 @@ class StartService
|
||||
|
||||
public string $jobQueue = 'high';
|
||||
|
||||
public function handle(Service $service)
|
||||
public function handle(Service $service, bool $pullLatestImages = false, bool $stopBeforeStart = false)
|
||||
{
|
||||
$service->parse();
|
||||
if ($stopBeforeStart) {
|
||||
StopService::run(service: $service, dockerCleanup: false);
|
||||
}
|
||||
$service->saveComposeConfigs();
|
||||
$commands[] = 'cd '.$service->workdir();
|
||||
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
||||
if ($pullLatestImages) {
|
||||
$commands[] = "echo 'Pulling images.'";
|
||||
$commands[] = 'docker compose pull';
|
||||
}
|
||||
if ($service->networks()->count() > 0) {
|
||||
$commands[] = "echo 'Creating Docker network.'";
|
||||
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid";
|
||||
}
|
||||
$commands[] = 'echo Starting service.';
|
||||
$commands[] = "echo 'Pulling images.'";
|
||||
$commands[] = 'docker compose pull';
|
||||
$commands[] = "echo 'Starting containers.'";
|
||||
$commands[] = 'docker compose up -d --remove-orphans --force-recreate --build';
|
||||
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
||||
if (data_get($service, 'connect_to_docker_network')) {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Shared;
|
||||
|
||||
use App\Models\Service;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class PullImage
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(Service $resource)
|
||||
{
|
||||
$resource->saveComposeConfigs();
|
||||
|
||||
$commands[] = 'cd '.$resource->workdir();
|
||||
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
||||
$commands[] = 'docker compose pull';
|
||||
|
||||
$server = data_get($resource, 'server');
|
||||
|
||||
if (! $server) {
|
||||
return;
|
||||
}
|
||||
|
||||
instant_remote_process($commands, $resource->server);
|
||||
}
|
||||
}
|
||||
@@ -1388,6 +1388,108 @@ class ApplicationsController extends Controller
|
||||
return response()->json($this->removeSensitiveData($application));
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
summary: 'Get application logs.',
|
||||
description: 'Get application logs by UUID.',
|
||||
path: '/applications/{uuid}/logs',
|
||||
operationId: 'get-application-logs-by-uuid',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Applications'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'uuid',
|
||||
in: 'path',
|
||||
description: 'UUID of the application.',
|
||||
required: true,
|
||||
schema: new OA\Schema(
|
||||
type: 'string',
|
||||
format: 'uuid',
|
||||
)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'lines',
|
||||
in: 'query',
|
||||
description: 'Number of lines to show from the end of the logs.',
|
||||
required: false,
|
||||
schema: new OA\Schema(
|
||||
type: 'integer',
|
||||
format: 'int32',
|
||||
default: 100,
|
||||
)
|
||||
),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Get application logs by UUID.',
|
||||
content: [
|
||||
new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
properties: [
|
||||
'logs' => ['type' => 'string'],
|
||||
]
|
||||
)
|
||||
),
|
||||
]
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 404,
|
||||
ref: '#/components/responses/404',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function logs_by_uuid(Request $request)
|
||||
{
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
}
|
||||
$uuid = $request->route('uuid');
|
||||
if (! $uuid) {
|
||||
return response()->json(['message' => 'UUID is required.'], 400);
|
||||
}
|
||||
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
|
||||
if (! $application) {
|
||||
return response()->json(['message' => 'Application not found.'], 404);
|
||||
}
|
||||
|
||||
$containers = getCurrentApplicationContainerStatus($application->destination->server, $application->id);
|
||||
|
||||
if ($containers->count() == 0) {
|
||||
return response()->json([
|
||||
'message' => 'Application is not running.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
$container = $containers->first();
|
||||
|
||||
$status = getContainerStatus($application->destination->server, $container['Names']);
|
||||
if ($status !== 'running') {
|
||||
return response()->json([
|
||||
'message' => 'Application is not running.',
|
||||
], 400);
|
||||
}
|
||||
|
||||
$lines = $request->query->get('lines', 100) ?: 100;
|
||||
$logs = getContainerLogs($application->destination->server, $container['ID'], $lines);
|
||||
|
||||
return response()->json([
|
||||
'logs' => $logs,
|
||||
]);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
summary: 'Delete',
|
||||
description: 'Delete application by UUID.',
|
||||
|
||||
@@ -37,7 +37,7 @@ class Bitbucket extends Controller
|
||||
$headers = $request->headers->all();
|
||||
$x_bitbucket_token = data_get($headers, 'x-hub-signature.0', '');
|
||||
$x_bitbucket_event = data_get($headers, 'x-event-key.0', '');
|
||||
$handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']);
|
||||
$handled_events = collect(['repo:push', 'pullrequest:updated', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']);
|
||||
if (! $handled_events->contains($x_bitbucket_event)) {
|
||||
return response([
|
||||
'status' => 'failed',
|
||||
@@ -48,14 +48,15 @@ class Bitbucket extends Controller
|
||||
$branch = data_get($payload, 'push.changes.0.new.name');
|
||||
$full_name = data_get($payload, 'repository.full_name');
|
||||
$commit = data_get($payload, 'push.changes.0.new.target.hash');
|
||||
if (! $branch) {
|
||||
|
||||
if (!$branch) {
|
||||
return response([
|
||||
'status' => 'failed',
|
||||
'message' => 'Nothing to do. No branch found in the request.',
|
||||
]);
|
||||
}
|
||||
}
|
||||
if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
|
||||
if ($x_bitbucket_event === 'pullrequest:updated' || $x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') {
|
||||
$branch = data_get($payload, 'pullrequest.destination.branch.name');
|
||||
$base_branch = data_get($payload, 'pullrequest.source.branch.name');
|
||||
$full_name = data_get($payload, 'repository.full_name');
|
||||
@@ -119,7 +120,7 @@ class Bitbucket extends Controller
|
||||
]);
|
||||
}
|
||||
}
|
||||
if ($x_bitbucket_event === 'pullrequest:created') {
|
||||
if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:updated') {
|
||||
if ($application->isPRDeployable()) {
|
||||
$deployment_uuid = new Cuid2;
|
||||
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
|
||||
|
||||
@@ -253,6 +253,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Make sure the private key is stored in the filesystem
|
||||
$this->server->privateKey->storeInFileSystem();
|
||||
|
||||
// Generate custom host<->ip mapping
|
||||
$allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server);
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ class Select extends Component
|
||||
|
||||
public function loadServices()
|
||||
{
|
||||
$services = get_service_templates(true);
|
||||
$services = get_service_templates();
|
||||
$services = collect($services)->map(function ($service, $key) {
|
||||
$default_logo = 'images/default.webp';
|
||||
$logo = data_get($service, 'logo', $default_logo);
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Actions\Service\StartService;
|
||||
use App\Actions\Service\StopService;
|
||||
use App\Actions\Shared\PullImage;
|
||||
use App\Enums\ProcessStatus;
|
||||
use App\Events\ServiceStatusChanged;
|
||||
use App\Models\Service;
|
||||
@@ -85,8 +84,7 @@ class Navbar extends Component
|
||||
|
||||
public function start()
|
||||
{
|
||||
$this->service->parse();
|
||||
$activity = StartService::run($this->service);
|
||||
$activity = StartService::run($this->service, pullLatestImages: true);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
}
|
||||
|
||||
@@ -98,8 +96,7 @@ class Navbar extends Component
|
||||
$activity->properties->status = ProcessStatus::ERROR->value;
|
||||
$activity->save();
|
||||
}
|
||||
$this->service->parse();
|
||||
$activity = StartService::run($this->service);
|
||||
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
} catch (\Exception $e) {
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
@@ -129,10 +126,7 @@ class Navbar extends Component
|
||||
|
||||
return;
|
||||
}
|
||||
StopService::run(service: $this->service, dockerCleanup: false);
|
||||
$this->service->parse();
|
||||
$this->dispatch('imagePulled');
|
||||
$activity = StartService::run($this->service);
|
||||
$activity = StartService::run($this->service, stopBeforeStart: true);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
}
|
||||
|
||||
@@ -144,11 +138,7 @@ class Navbar extends Component
|
||||
|
||||
return;
|
||||
}
|
||||
PullImage::run($this->service);
|
||||
StopService::run(service: $this->service, dockerCleanup: false);
|
||||
$this->service->parse();
|
||||
$this->dispatch('imagePulled');
|
||||
$activity = StartService::run($this->service);
|
||||
$activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true);
|
||||
$this->dispatch('activityMonitor', $activity->id);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
||||
}
|
||||
$this->deployment_url = base_url()."/project/{$this->project_uuid}/environments/{$this->environment_uuid}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||
$this->deployment_url = base_url()."/project/{$this->project_uuid}/environment/{$this->environment_uuid}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}";
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
@@ -175,9 +175,9 @@ class DeploymentFailed extends CustomEmailNotification
|
||||
}
|
||||
}
|
||||
|
||||
$description .= "\n\n**Project:** ".data_get($this->application, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Deployment Logs:** {$this->deployment_url}";
|
||||
$description .= "\n\n*Project:* ".data_get($this->application, 'environment.project.name');
|
||||
$description .= "\n*Environment:* {$this->environment_name}";
|
||||
$description .= "\n*<{$this->deployment_url}|Deployment Logs>*";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
|
||||
@@ -195,9 +195,9 @@ class DeploymentSuccess extends CustomEmailNotification
|
||||
}
|
||||
}
|
||||
|
||||
$description .= "\n\n**Project:** ".data_get($this->application, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Deployment Logs:** {$this->deployment_url}";
|
||||
$description .= "\n\n*Project:* ".data_get($this->application, 'environment.project.name');
|
||||
$description .= "\n*Environment:* {$this->environment_name}";
|
||||
$description .= "\n*<{$this->deployment_url}|Deployment Logs>*";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
|
||||
@@ -103,9 +103,9 @@ class StatusChanged extends CustomEmailNotification
|
||||
$title = 'Application stopped';
|
||||
$description = "{$this->resource_name} has been stopped";
|
||||
|
||||
$description .= "\n\n**Project:** ".data_get($this->resource, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Application URL:** {$this->resource_url}";
|
||||
$description .= "\n\n*Project:* ".data_get($this->resource, 'environment.project.name');
|
||||
$description .= "\n*Environment:* {$this->environment_name}";
|
||||
$description .= "\n*Application URL:* {$this->resource_url}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
|
||||
@@ -93,7 +93,7 @@ class ContainerRestarted extends CustomEmailNotification
|
||||
$description = "A resource ({$this->name}) has been restarted automatically on {$this->server->name}";
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n**Resource URL:** {$this->url}";
|
||||
$description .= "\n*Resource URL:* {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
|
||||
@@ -93,7 +93,7 @@ class ContainerStopped extends CustomEmailNotification
|
||||
$description = "A resource ({$this->name}) has been stopped unexpectedly on {$this->server->name}";
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n**Resource URL:** {$this->url}";
|
||||
$description .= "\n*Resource URL:* {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
|
||||
@@ -79,8 +79,8 @@ class BackupFailed extends CustomEmailNotification
|
||||
$title = 'Database backup failed';
|
||||
$description = "Database backup for {$this->name} (db:{$this->database_name}) has FAILED.";
|
||||
|
||||
$description .= "\n\n**Frequency:** {$this->frequency}";
|
||||
$description .= "\n\n**Error Output:**\n{$this->output}";
|
||||
$description .= "\n\n*Frequency:* {$this->frequency}";
|
||||
$description .= "\n\n*Error Output:* {$this->output}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
|
||||
@@ -77,7 +77,7 @@ class BackupSuccess extends CustomEmailNotification
|
||||
$title = 'Database backup successful';
|
||||
$description = "Database backup for {$this->name} (db:{$this->database_name}) was successful.";
|
||||
|
||||
$description .= "\n\n**Frequency:** {$this->frequency}";
|
||||
$description .= "\n\n*Frequency:* {$this->frequency}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
|
||||
@@ -101,11 +101,11 @@ class TaskFailed extends CustomEmailNotification
|
||||
$description = "Scheduled task ({$this->task->name}) failed.";
|
||||
|
||||
if ($this->output) {
|
||||
$description .= "\n\n**Error Output:**\n{$this->output}";
|
||||
$description .= "\n\n*Error Output:* {$this->output}";
|
||||
}
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n\n**Task URL:** {$this->url}";
|
||||
$description .= "\n\n*Task URL:* {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
|
||||
@@ -96,7 +96,7 @@ class TaskSuccess extends CustomEmailNotification
|
||||
$description = "Scheduled task ({$this->task->name}) succeeded.";
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n\n**Task URL:** {$this->url}";
|
||||
$description .= "\n\n*Task URL:* {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
|
||||
Reference in New Issue
Block a user