diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 2d354057e..4f12f436c 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -4,5 +4,5 @@ contact_links:
url: https://coollabs.io/discord
about: Reach out to us on Discord.
- name: 🙋♂️ Feature Requests
- url: https://github.com/coollabsio/coolify/discussions/categories/feature-requests-ideas
+ url: https://github.com/coollabsio/coolify/discussions/categories/new-features
about: All feature requests will be discussed here.
diff --git a/README.md b/README.md
index dc55c99da..ce6289d48 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,7 @@ Special thanks to our biggest sponsors!
+
## Github Sponsors ($40+)
diff --git a/app/Actions/Proxy/CheckConfiguration.php b/app/Actions/Proxy/CheckConfiguration.php
index 3cbd5669d..f4fe650c5 100644
--- a/app/Actions/Proxy/CheckConfiguration.php
+++ b/app/Actions/Proxy/CheckConfiguration.php
@@ -21,7 +21,6 @@ class CheckConfiguration
"cat $proxy_path/docker-compose.yml",
];
$proxy_configuration = instant_remote_process($payload, $server, false);
-
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value;
}
diff --git a/app/Actions/Server/ValidateServer.php b/app/Actions/Server/ValidateServer.php
new file mode 100644
index 000000000..d0a4cd6be
--- /dev/null
+++ b/app/Actions/Server/ValidateServer.php
@@ -0,0 +1,67 @@
+update([
+ 'validation_logs' => null,
+ ]);
+ ['uptime' => $this->uptime, 'error' => $error] = $server->validateConnection();
+ if (! $this->uptime) {
+ $this->error = 'Server is not reachable. Please validate your configuration and connection. Check this documentation for further help.
Error: '.$error.'
';
+ $server->update([
+ 'validation_logs' => $this->error,
+ ]);
+ throw new \Exception($this->error);
+ }
+ $this->supported_os_type = $server->validateOS();
+ if (! $this->supported_os_type) {
+ $this->error = 'Server OS type is not supported. Please install Docker manually before continuing: documentation .';
+ $server->update([
+ 'validation_logs' => $this->error,
+ ]);
+ throw new \Exception($this->error);
+ }
+
+ $this->docker_installed = $server->validateDockerEngine();
+ $this->docker_compose_installed = $server->validateDockerCompose();
+ if (! $this->docker_installed || ! $this->docker_compose_installed) {
+ $this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: documentation .';
+ $server->update([
+ 'validation_logs' => $this->error,
+ ]);
+ throw new \Exception($this->error);
+ }
+ $this->docker_version = $server->validateDockerEngineVersion();
+
+ if ($this->docker_version) {
+ return 'OK';
+ } else {
+ $this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: documentation .';
+ $server->update([
+ 'validation_logs' => $this->error,
+ ]);
+ throw new \Exception($this->error);
+ }
+ }
+}
diff --git a/app/Console/Commands/Emails.php b/app/Console/Commands/Emails.php
index 8ad0d458f..36722564c 100644
--- a/app/Console/Commands/Emails.php
+++ b/app/Console/Commands/Emails.php
@@ -81,7 +81,7 @@ class Emails extends Command
}
set_transanctional_email_settings();
- $this->mail = new MailMessage();
+ $this->mail = new MailMessage;
$this->mail->subject('Test Email');
switch ($type) {
case 'updates':
@@ -107,7 +107,7 @@ class Emails extends Command
$confirmed = confirm('Are you sure?');
if ($confirmed) {
foreach ($emails as $email) {
- $this->mail = new MailMessage();
+ $this->mail = new MailMessage;
$this->mail->subject('One-click Services, Docker Compose support');
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
'token' => encrypt($email),
@@ -118,7 +118,7 @@ class Emails extends Command
}
break;
case 'emails-test':
- $this->mail = (new Test())->toMail();
+ $this->mail = (new Test)->toMail();
$this->sendEmail();
break;
case 'database-backup-statuses-daily':
@@ -224,7 +224,7 @@ class Emails extends Command
// $this->sendEmail();
// break;
case 'waitlist-invitation-link':
- $this->mail = new MailMessage();
+ $this->mail = new MailMessage;
$this->mail->view('emails.waitlist-invitation', [
'loginLink' => 'https://coolify.io',
]);
@@ -241,7 +241,7 @@ class Emails extends Command
break;
case 'realusers-before-trial':
- $this->mail = new MailMessage();
+ $this->mail = new MailMessage;
$this->mail->view('emails.before-trial-conversion');
$this->mail->subject('Trial period has been added for all subscription plans.');
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
@@ -287,7 +287,7 @@ class Emails extends Command
foreach ($admins as $admin) {
$this->info($admin);
}
- $this->mail = new MailMessage();
+ $this->mail = new MailMessage;
$this->mail->view('emails.server-lost-connection', [
'name' => $server->name,
]);
diff --git a/app/Console/Commands/WaitlistInvite.php b/app/Console/Commands/WaitlistInvite.php
index ff501cc1d..2e330068c 100644
--- a/app/Console/Commands/WaitlistInvite.php
+++ b/app/Console/Commands/WaitlistInvite.php
@@ -103,7 +103,7 @@ class WaitlistInvite extends Command
{
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
$loginLink = route('auth.link', ['token' => $token]);
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->view('emails.waitlist-invitation', [
'loginLink' => $loginLink,
]);
diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php
index e9280a8ad..cd4d724b4 100644
--- a/app/Http/Controllers/Api/ApplicationsController.php
+++ b/app/Http/Controllers/Api/ApplicationsController.php
@@ -708,7 +708,7 @@ class ApplicationsController extends Controller
if ($return instanceof \Illuminate\Http\JsonResponse) {
return $return;
}
- $application = new Application();
+ $application = new Application;
removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all());
@@ -739,7 +739,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -796,7 +796,7 @@ class ApplicationsController extends Controller
if (str($gitRepository)->startsWith('http') || str($gitRepository)->contains('github.com')) {
$gitRepository = str($gitRepository)->replace('https://', '')->replace('http://', '')->replace('github.com/', '');
}
- $application = new Application();
+ $application = new Application;
removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all());
@@ -835,7 +835,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -890,7 +890,7 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Private Key not found.'], 404);
}
- $application = new Application();
+ $application = new Application;
removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all());
@@ -927,7 +927,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -947,7 +947,7 @@ class ApplicationsController extends Controller
]));
} elseif ($type === 'dockerfile') {
if (! $request->has('name')) {
- $request->offsetSet('name', 'dockerfile-'.new Cuid2(7));
+ $request->offsetSet('name', 'dockerfile-'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
@@ -988,7 +988,7 @@ class ApplicationsController extends Controller
$port = 80;
}
- $application = new Application();
+ $application = new Application;
$application->fill($request->all());
$application->fqdn = $fqdn;
$application->ports_exposes = $port;
@@ -1009,7 +1009,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -1025,7 +1025,7 @@ class ApplicationsController extends Controller
]));
} elseif ($type === 'dockerimage') {
if (! $request->has('name')) {
- $request->offsetSet('name', 'docker-image-'.new Cuid2(7));
+ $request->offsetSet('name', 'docker-image-'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
@@ -1046,7 +1046,7 @@ class ApplicationsController extends Controller
if (! $request->docker_registry_image_tag) {
$request->offsetSet('docker_registry_image_tag', 'latest');
}
- $application = new Application();
+ $application = new Application;
removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all());
@@ -1067,7 +1067,7 @@ class ApplicationsController extends Controller
$application->isConfigurationChanged(true);
if ($instantDeploy) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -1099,7 +1099,7 @@ class ApplicationsController extends Controller
], 422);
}
if (! $request->has('name')) {
- $request->offsetSet('name', 'service'.new Cuid2(7));
+ $request->offsetSet('name', 'service'.new Cuid2);
}
$validator = customApiValidator($request->all(), [
sharedDataApplications(),
@@ -1140,7 +1140,7 @@ class ApplicationsController extends Controller
// return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
// }
- $service = new Service();
+ $service = new Service;
removeUnnecessaryFieldsFromRequest($request);
$service->fill($request->all());
@@ -1320,7 +1320,7 @@ class ApplicationsController extends Controller
#[OA\Patch(
summary: 'Update',
description: 'Update application by UUID.',
- path: '/applications',
+ path: '/applications/{uuid}',
security: [
['bearerAuth' => []],
],
@@ -2322,7 +2322,7 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Application not found.'], 404);
}
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
@@ -2479,7 +2479,7 @@ class ApplicationsController extends Controller
return response()->json(['message' => 'Application not found.'], 404);
}
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
diff --git a/app/Http/Controllers/Api/DeployController.php b/app/Http/Controllers/Api/DeployController.php
index 2ee56d0cd..437162058 100644
--- a/app/Http/Controllers/Api/DeployController.php
+++ b/app/Http/Controllers/Api/DeployController.php
@@ -84,7 +84,7 @@ class DeployController extends Controller
],
tags: ['Deployments'],
parameters: [
- new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment Uuid', schema: new OA\Schema(type: 'integer')),
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -290,7 +290,7 @@ class DeployController extends Controller
}
switch ($resource?->getMorphClass()) {
case 'App\Models\Application':
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $resource,
deployment_uuid: $deployment_uuid,
diff --git a/app/Http/Controllers/Api/OpenApi.php b/app/Http/Controllers/Api/OpenApi.php
index 59731ef40..60337a76c 100644
--- a/app/Http/Controllers/Api/OpenApi.php
+++ b/app/Http/Controllers/Api/OpenApi.php
@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Api;
use OpenApi\Attributes as OA;
#[OA\Info(title: 'Coolify', version: '0.1')]
-#[OA\Server(url: 'https://app.coolify.io/api/v1')]
+#[OA\Server(url: 'https://app.coolify.io/api/v1', description: 'Coolify Cloud API. Change the host to your own instance if you are self-hosting.')]
#[OA\SecurityScheme(
type: 'http',
scheme: 'bearer',
diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php
index b7c3d115d..6aec31e9b 100644
--- a/app/Http/Controllers/Api/ProjectController.php
+++ b/app/Http/Controllers/Api/ProjectController.php
@@ -61,7 +61,7 @@ class ProjectController extends Controller
],
tags: ['Projects'],
parameters: [
- new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Project UUID', schema: new OA\Schema(type: 'integer')),
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Project UUID', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -107,7 +107,7 @@ class ProjectController extends Controller
],
tags: ['Projects'],
parameters: [
- new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Project UUID', schema: new OA\Schema(type: 'integer')),
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Project UUID', schema: new OA\Schema(type: 'string')),
new OA\Parameter(name: 'environment_name', in: 'path', required: true, description: 'Environment name', schema: new OA\Schema(type: 'string')),
],
responses: [
@@ -135,8 +135,14 @@ class ProjectController extends Controller
if (is_null($teamId)) {
return invalidTokenResponse();
}
- $project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
- $environment = $project->environments()->whereName(request()->environment_name)->first();
+ if (! $request->uuid) {
+ return response()->json(['message' => 'Uuid is required.'], 422);
+ }
+ if (! $request->environment_name) {
+ return response()->json(['message' => 'Environment name is required.'], 422);
+ }
+ $project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first();
+ $environment = $project->environments()->whereName($request->environment_name)->first();
if (! $environment) {
return response()->json(['message' => 'Environment not found.'], 404);
}
@@ -144,4 +150,276 @@ class ProjectController extends Controller
return response()->json(serializeApiResponse($environment));
}
+
+ #[OA\Post(
+ summary: 'Create',
+ description: 'Create Project.',
+ path: '/projects',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Projects'],
+ requestBody: new OA\RequestBody(
+ required: true,
+ description: 'Project created.',
+ content: new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'uuid' => ['type' => 'string', 'description' => 'The name of the project.'],
+ 'description' => ['type' => 'string', 'description' => 'The description of the project.'],
+ ],
+ ),
+ ),
+ ),
+ responses: [
+ new OA\Response(
+ response: 201,
+ description: 'Project created.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the project.'],
+ ]
+ )
+ ),
+ ]),
+ 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 create_project(Request $request)
+ {
+ $allowedFields = ['name', 'description'];
+
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+ $validator = customApiValidator($request->all(), [
+ 'name' => 'string|max:255|required',
+ 'description' => 'string|nullable',
+ ]);
+
+ $extraFields = array_diff(array_keys($request->all()), $allowedFields);
+ if ($validator->fails() || ! empty($extraFields)) {
+ $errors = $validator->errors();
+ if (! empty($extraFields)) {
+ foreach ($extraFields as $field) {
+ $errors->add($field, 'This field is not allowed.');
+ }
+ }
+
+ return response()->json([
+ 'message' => 'Validation failed.',
+ 'errors' => $errors,
+ ], 422);
+ }
+
+ $project = Project::create([
+ 'name' => $request->name,
+ 'description' => $request->description,
+ 'team_id' => $teamId,
+ ]);
+
+ return response()->json([
+ 'uuid' => $project->uuid,
+ ])->setStatusCode(201);
+ }
+
+ #[OA\Patch(
+ summary: 'Update',
+ description: 'Update Project.',
+ path: '/projects/{uuid}',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Projects'],
+ requestBody: new OA\RequestBody(
+ required: true,
+ description: 'Project updated.',
+ content: new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'name' => ['type' => 'string', 'description' => 'The name of the project.'],
+ 'description' => ['type' => 'string', 'description' => 'The description of the project.'],
+ ],
+ ),
+ ),
+ ),
+ responses: [
+ new OA\Response(
+ response: 201,
+ description: 'Project updated.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'uuid' => ['type' => 'string', 'example' => 'og888os'],
+ 'name' => ['type' => 'string', 'example' => 'Project Name'],
+ 'description' => ['type' => 'string', 'example' => 'Project Description'],
+ ]
+ )
+ ),
+ ]),
+ 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 update_project(Request $request)
+ {
+ $allowedFields = ['name', 'description'];
+
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+ $validator = customApiValidator($request->all(), [
+ 'name' => 'string|max:255|nullable',
+ 'description' => 'string|nullable',
+ ]);
+
+ $extraFields = array_diff(array_keys($request->all()), $allowedFields);
+ if ($validator->fails() || ! empty($extraFields)) {
+ $errors = $validator->errors();
+ if (! empty($extraFields)) {
+ foreach ($extraFields as $field) {
+ $errors->add($field, 'This field is not allowed.');
+ }
+ }
+
+ return response()->json([
+ 'message' => 'Validation failed.',
+ 'errors' => $errors,
+ ], 422);
+ }
+ $uuid = $request->uuid;
+ if (! $uuid) {
+ return response()->json(['message' => 'Uuid is required.'], 422);
+ }
+
+ $project = Project::whereTeamId($teamId)->whereUuid($uuid)->first();
+ if (! $project) {
+ return response()->json(['message' => 'Project not found.'], 404);
+ }
+
+ $project->update($request->only($allowedFields));
+
+ return response()->json([
+ 'uuid' => $project->uuid,
+ 'name' => $project->name,
+ 'description' => $project->description,
+ ])->setStatusCode(201);
+
+ }
+
+ #[OA\Delete(
+ summary: 'Delete',
+ description: 'Delete project by UUID.',
+ path: '/projects/{uuid}',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Projects'],
+ parameters: [
+ new OA\Parameter(
+ name: 'uuid',
+ in: 'path',
+ description: 'UUID of the application.',
+ required: true,
+ schema: new OA\Schema(
+ type: 'string',
+ format: 'uuid',
+ )
+ ),
+ ],
+ responses: [
+ new OA\Response(
+ response: 200,
+ description: 'Project deleted.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'message' => ['type' => 'string', 'example' => 'Project deleted.'],
+ ]
+ )
+ ),
+ ]),
+ 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 delete_project(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ if (! $request->uuid) {
+ return response()->json(['message' => 'Uuid is required.'], 422);
+ }
+ $project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first();
+ if (! $project) {
+ return response()->json(['message' => 'Project not found.'], 404);
+ }
+ if ($project->resource_count() > 0) {
+ return response()->json(['message' => 'Project has resources, so it cannot be deleted.'], 400);
+ }
+
+ $project->delete();
+
+ return response()->json(['message' => 'Project deleted.']);
+ }
}
diff --git a/app/Http/Controllers/Api/SecurityController.php b/app/Http/Controllers/Api/SecurityController.php
index 11e8e27ca..67128234e 100644
--- a/app/Http/Controllers/Api/SecurityController.php
+++ b/app/Http/Controllers/Api/SecurityController.php
@@ -73,7 +73,7 @@ class SecurityController extends Controller
],
tags: ['Private Keys'],
parameters: [
- new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'integer')),
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -318,7 +318,7 @@ class SecurityController extends Controller
],
tags: ['Private Keys'],
parameters: [
- new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'integer')),
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Private Key Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
diff --git a/app/Http/Controllers/Api/ServersController.php b/app/Http/Controllers/Api/ServersController.php
index da9c3b2d8..5d4b56988 100644
--- a/app/Http/Controllers/Api/ServersController.php
+++ b/app/Http/Controllers/Api/ServersController.php
@@ -2,8 +2,12 @@
namespace App\Http\Controllers\Api;
+use App\Actions\Server\ValidateServer;
+use App\Enums\ProxyStatus;
+use App\Enums\ProxyTypes;
use App\Http\Controllers\Controller;
use App\Models\Application;
+use App\Models\PrivateKey;
use App\Models\Project;
use App\Models\Server as ModelsServer;
use Illuminate\Http\Request;
@@ -75,7 +79,7 @@ class ServersController extends Controller
if (is_null($teamId)) {
return invalidTokenResponse();
}
- $servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) {
+ $servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port', 'description')->get()->load(['settings'])->map(function ($server) {
$server['is_reachable'] = $server->settings->is_reachable;
$server['is_usable'] = $server->settings->is_usable;
@@ -101,7 +105,7 @@ class ServersController extends Controller
],
tags: ['Servers'],
parameters: [
- new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'integer')),
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -178,7 +182,7 @@ class ServersController extends Controller
],
tags: ['Servers'],
parameters: [
- new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'integer')),
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -255,7 +259,7 @@ class ServersController extends Controller
],
tags: ['Servers'],
parameters: [
- new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'integer')),
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server\'s Uuid', schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
@@ -392,4 +396,390 @@ class ServersController extends Controller
return response()->json(serializeApiResponse($domains));
}
+
+ #[OA\Post(
+ summary: 'Create',
+ description: 'Create Server.',
+ path: '/servers',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Servers'],
+ requestBody: new OA\RequestBody(
+ required: true,
+ description: 'Server created.',
+ content: new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'name' => ['type' => 'string', 'example' => 'My Server', 'description' => 'The name of the server.'],
+ 'description' => ['type' => 'string', 'example' => 'My Server Description', 'description' => 'The description of the server.'],
+ 'ip' => ['type' => 'string', 'example' => '127.0.0.1', 'description' => 'The IP of the server.'],
+ 'port' => ['type' => 'integer', 'example' => 22, 'description' => 'The port of the server.'],
+ 'user' => ['type' => 'string', 'example' => 'root', 'description' => 'The user of the server.'],
+ 'private_key_uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the private key.'],
+ 'is_build_server' => ['type' => 'boolean', 'example' => false, 'description' => 'Is build server.'],
+ 'instant_validate' => ['type' => 'boolean', 'example' => false, 'description' => 'Instant validate.'],
+ ],
+ ),
+ ),
+ ),
+ responses: [
+ new OA\Response(
+ response: 201,
+ description: 'Server created.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the server.'],
+ ]
+ )
+ ),
+ ]),
+ 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 create_server(Request $request)
+ {
+ $allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate'];
+
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+ $validator = customApiValidator($request->all(), [
+ 'name' => 'string|max:255',
+ 'description' => 'string|nullable',
+ 'ip' => 'string|required',
+ 'port' => 'integer|nullable',
+ 'private_key_uuid' => 'string|required',
+ 'user' => 'string|nullable',
+ 'is_build_server' => 'boolean|nullable',
+ 'instant_validate' => 'boolean|nullable',
+ ]);
+
+ $extraFields = array_diff(array_keys($request->all()), $allowedFields);
+ if ($validator->fails() || ! empty($extraFields)) {
+ $errors = $validator->errors();
+ if (! empty($extraFields)) {
+ foreach ($extraFields as $field) {
+ $errors->add($field, 'This field is not allowed.');
+ }
+ }
+
+ return response()->json([
+ 'message' => 'Validation failed.',
+ 'errors' => $errors,
+ ], 422);
+ }
+ if (! $request->name) {
+ $request->offsetSet('name', generate_random_name());
+ }
+ if (! $request->user) {
+ $request->offsetSet('user', 'root');
+ }
+ if (is_null($request->port)) {
+ $request->offsetSet('port', 22);
+ }
+ if (is_null($request->is_build_server)) {
+ $request->offsetSet('is_build_server', false);
+ }
+ if (is_null($request->instant_validate)) {
+ $request->offsetSet('instant_validate', false);
+ }
+ $privateKey = PrivateKey::whereTeamId($teamId)->whereUuid($request->private_key_uuid)->first();
+ if (! $privateKey) {
+ return response()->json(['message' => 'Private key not found.'], 404);
+ }
+ $allServers = ModelsServer::whereIp($request->ip)->get();
+ if ($allServers->count() > 0) {
+ return response()->json(['message' => 'Server with this IP already exists.'], 400);
+ }
+
+ $server = ModelsServer::create([
+ 'name' => $request->name,
+ 'description' => $request->description,
+ 'ip' => $request->ip,
+ 'port' => $request->port,
+ 'user' => $request->user,
+ 'private_key_id' => $privateKey->id,
+ 'team_id' => $teamId,
+ 'proxy' => [
+ 'type' => ProxyTypes::TRAEFIK_V2->value,
+ 'status' => ProxyStatus::EXITED->value,
+ ],
+ ]);
+ $server->settings()->update([
+ 'is_build_server' => $request->is_build_server,
+ ]);
+ if ($request->instant_validate) {
+ ValidateServer::dispatch($server);
+ }
+
+ return response()->json([
+ 'uuid' => $server->uuid,
+ ])->setStatusCode(201);
+ }
+
+ #[OA\Patch(
+ summary: 'Update',
+ description: 'Update Server.',
+ path: '/servers/{uuid}',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Servers'],
+ requestBody: new OA\RequestBody(
+ required: true,
+ description: 'Server updated.',
+ content: new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'name' => ['type' => 'string', 'description' => 'The name of the server.'],
+ 'description' => ['type' => 'string', 'description' => 'The description of the server.'],
+ 'ip' => ['type' => 'string', 'description' => 'The IP of the server.'],
+ 'port' => ['type' => 'integer', 'description' => 'The port of the server.'],
+ 'user' => ['type' => 'string', 'description' => 'The user of the server.'],
+ 'private_key_uuid' => ['type' => 'string', 'description' => 'The UUID of the private key.'],
+ 'is_build_server' => ['type' => 'boolean', 'description' => 'Is build server.'],
+ 'instant_validate' => ['type' => 'boolean', 'description' => 'Instant validate.'],
+ ],
+ ),
+ ),
+ ),
+ responses: [
+ new OA\Response(
+ response: 201,
+ description: 'Server updated.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'array',
+ items: new OA\Items(ref: '#/components/schemas/Server')
+ )
+ ),
+ ]),
+ 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 update_server(Request $request)
+ {
+ $allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate'];
+
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+ $validator = customApiValidator($request->all(), [
+ 'name' => 'string|max:255|nullable',
+ 'description' => 'string|nullable',
+ 'ip' => 'string|nullable',
+ 'port' => 'integer|nullable',
+ 'private_key_uuid' => 'string|nullable',
+ 'user' => 'string|nullable',
+ 'is_build_server' => 'boolean|nullable',
+ 'instant_validate' => 'boolean|nullable',
+ ]);
+
+ $extraFields = array_diff(array_keys($request->all()), $allowedFields);
+ if ($validator->fails() || ! empty($extraFields)) {
+ $errors = $validator->errors();
+ if (! empty($extraFields)) {
+ foreach ($extraFields as $field) {
+ $errors->add($field, 'This field is not allowed.');
+ }
+ }
+
+ return response()->json([
+ 'message' => 'Validation failed.',
+ 'errors' => $errors,
+ ], 422);
+ }
+ $server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first();
+ if (! $server) {
+ return response()->json(['message' => 'Server not found.'], 404);
+ }
+ $server->update($request->only(['name', 'description', 'ip', 'port', 'user']));
+ if ($request->is_build_server) {
+ $server->settings()->update([
+ 'is_build_server' => $request->is_build_server,
+ ]);
+ }
+ if ($request->instant_validate) {
+ ValidateServer::dispatch($server);
+ }
+
+ return response()->json(serializeApiResponse($server))->setStatusCode(201);
+ }
+
+ #[OA\Delete(
+ summary: 'Delete',
+ description: 'Delete server by UUID.',
+ path: '/servers/{uuid}',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Servers'],
+ parameters: [
+ new OA\Parameter(
+ name: 'uuid',
+ in: 'path',
+ description: 'UUID of the server.',
+ required: true,
+ schema: new OA\Schema(
+ type: 'string',
+ format: 'uuid',
+ )
+ ),
+ ],
+ responses: [
+ new OA\Response(
+ response: 200,
+ description: 'Server deleted.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'message' => ['type' => 'string', 'example' => 'Server deleted.'],
+ ]
+ )
+ ),
+ ]),
+ 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 delete_server(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ if (! $request->uuid) {
+ return response()->json(['message' => 'Uuid is required.'], 422);
+ }
+ $server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first();
+
+ if (! $server) {
+ return response()->json(['message' => 'Server not found.'], 404);
+ }
+ if ($server->definedResources()->count() > 0) {
+ return response()->json(['message' => 'Server has resources, so you need to delete them before.'], 400);
+ }
+ $server->delete();
+
+ return response()->json(['message' => 'Server deleted.']);
+ }
+
+ #[OA\Get(
+ summary: 'Validate',
+ description: 'Validate server by UUID.',
+ path: '/servers/{uuid}/validate',
+ security: [
+ ['bearerAuth' => []],
+ ],
+ tags: ['Servers'],
+ parameters: [
+ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server UUID', schema: new OA\Schema(type: 'string')),
+ ],
+ responses: [
+ new OA\Response(
+ response: 201,
+ description: 'Server validation started.',
+ content: [
+ new OA\MediaType(
+ mediaType: 'application/json',
+ schema: new OA\Schema(
+ type: 'object',
+ properties: [
+ 'message' => ['type' => 'string', 'example' => 'Validation started.'],
+ ]
+ )
+ ),
+ ]),
+ 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 validate_server(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ if (! $request->uuid) {
+ return response()->json(['message' => 'Uuid is required.'], 422);
+ }
+ $server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first();
+
+ if (! $server) {
+ return response()->json(['message' => 'Server not found.'], 404);
+ }
+ ValidateServer::dispatch($server);
+
+ return response()->json(['message' => 'Validation started.']);
+ }
}
diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php
index 8e52fda32..21fdd2ef8 100644
--- a/app/Http/Controllers/UploadController.php
+++ b/app/Http/Controllers/UploadController.php
@@ -21,7 +21,7 @@ class UploadController extends BaseController
$receiver = new FileReceiver('file', $request, HandlerFactory::classFromRequest($request));
if ($receiver->isUploaded() === false) {
- throw new UploadMissingFileException();
+ throw new UploadMissingFileException;
}
$save = $receiver->receive();
diff --git a/app/Http/Controllers/Webhook/Bitbucket.php b/app/Http/Controllers/Webhook/Bitbucket.php
index 059438ff4..ef85d59e3 100644
--- a/app/Http/Controllers/Webhook/Bitbucket.php
+++ b/app/Http/Controllers/Webhook/Bitbucket.php
@@ -103,7 +103,7 @@ class Bitbucket extends Controller
if ($x_bitbucket_event === 'repo:push') {
if ($application->isDeployable()) {
ray('Deploying '.$application->name.' with branch '.$branch);
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -127,7 +127,7 @@ class Bitbucket extends Controller
if ($x_bitbucket_event === 'pullrequest:created') {
if ($application->isPRDeployable()) {
ray('Deploying preview for '.$application->name.' with branch '.$branch.' and base branch '.$base_branch.' and pull request id '.$pull_request_id);
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
if ($application->build_pack === 'dockercompose') {
diff --git a/app/Http/Controllers/Webhook/Gitea.php b/app/Http/Controllers/Webhook/Gitea.php
index e6d91efd6..e042b74c9 100644
--- a/app/Http/Controllers/Webhook/Gitea.php
+++ b/app/Http/Controllers/Webhook/Gitea.php
@@ -123,7 +123,7 @@ class Gitea extends Controller
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying '.$application->name.' with branch '.$branch);
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -162,7 +162,7 @@ class Gitea extends Controller
if ($x_gitea_event === 'pull_request') {
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
if ($application->build_pack === 'dockercompose') {
diff --git a/app/Http/Controllers/Webhook/Github.php b/app/Http/Controllers/Webhook/Github.php
index ee51b6e0d..5f3ba933b 100644
--- a/app/Http/Controllers/Webhook/Github.php
+++ b/app/Http/Controllers/Webhook/Github.php
@@ -128,7 +128,7 @@ class Github extends Controller
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying '.$application->name.' with branch '.$branch);
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -167,7 +167,7 @@ class Github extends Controller
if ($x_github_event === 'pull_request') {
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
if ($application->build_pack === 'dockercompose') {
@@ -357,7 +357,7 @@ class Github extends Controller
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying '.$application->name.' with branch '.$branch);
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -396,7 +396,7 @@ class Github extends Controller
if ($x_github_event === 'pull_request') {
if ($action === 'opened' || $action === 'synchronize' || $action === 'reopened') {
if ($application->isPRDeployable()) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
ApplicationPreview::create([
diff --git a/app/Http/Controllers/Webhook/Gitlab.php b/app/Http/Controllers/Webhook/Gitlab.php
index f6e6cf7e7..ec7f51a0d 100644
--- a/app/Http/Controllers/Webhook/Gitlab.php
+++ b/app/Http/Controllers/Webhook/Gitlab.php
@@ -137,7 +137,7 @@ class Gitlab extends Controller
$is_watch_path_triggered = $application->isWatchPathsTriggered($changed_files);
if ($is_watch_path_triggered || is_null($application->watch_paths)) {
ray('Deploying '.$application->name.' with branch '.$branch);
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $application,
deployment_uuid: $deployment_uuid,
@@ -177,7 +177,7 @@ class Gitlab extends Controller
if ($x_gitlab_event === 'merge_request') {
if ($action === 'open' || $action === 'opened' || $action === 'synchronize' || $action === 'reopened' || $action === 'reopen' || $action === 'update') {
if ($application->isPRDeployable()) {
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
$found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first();
if (! $found) {
if ($application->build_pack === 'dockercompose') {
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index b3ca3185f..d7b1a57da 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -307,14 +307,6 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
]
);
- // $this->execute_remote_command(
- // [
- // "docker image prune -f >/dev/null 2>&1",
- // "hidden" => true,
- // "ignore_errors" => true,
- // ]
- // );
-
ApplicationStatusChanged::dispatch(data_get($this->application, 'environment.project.team.id'));
}
}
@@ -497,13 +489,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
} else {
$this->write_deployment_configurations();
$server_workdir = $this->application->workdir();
+ $this->docker_compose_location = '/docker-compose.yaml';
$command = "{$this->coolify_variables} docker compose";
if ($this->env_filename) {
- $command .= " --env-file {$this->workdir}/{$this->env_filename}";
+ $command .= " --env-file {$server_workdir}/{$this->env_filename}";
}
$command .= " --project-directory {$server_workdir} -f {$server_workdir}{$this->docker_compose_location} up -d";
-
$this->execute_remote_command(
['command' => $command, 'hidden' => true],
);
@@ -636,21 +628,26 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->server = $this->original_server;
}
$readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at);
+
+ $mainDir = $this->configuration_dir;
+ if ($this->application->settings->is_raw_compose_deployment_enabled) {
+ $mainDir = $this->application->workdir();
+ }
if ($this->pull_request_id === 0) {
- $composeFileName = "$this->configuration_dir/docker-compose.yaml";
+ $composeFileName = "$mainDir/docker-compose.yaml";
} else {
- $composeFileName = "$this->configuration_dir/docker-compose-pr-{$this->pull_request_id}.yaml";
+ $composeFileName = "$mainDir/docker-compose-pr-{$this->pull_request_id}.yaml";
$this->docker_compose_location = "/docker-compose-pr-{$this->pull_request_id}.yaml";
}
$this->execute_remote_command(
[
- "mkdir -p $this->configuration_dir",
+ "mkdir -p $mainDir",
],
[
"echo '{$this->docker_compose_base64}' | base64 -d | tee $composeFileName > /dev/null",
],
[
- "echo '{$readme}' > $this->configuration_dir/README.md",
+ "echo '{$readme}' > $mainDir/README.md",
]
);
if ($this->use_build_server) {
@@ -991,7 +988,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$nixpacks_php_root_dir = $this->application->environment_variables_preview->where('key', 'NIXPACKS_PHP_ROOT_DIR')->first();
}
if (! $nixpacks_php_fallback_path) {
- $nixpacks_php_fallback_path = new EnvironmentVariable();
+ $nixpacks_php_fallback_path = new EnvironmentVariable;
$nixpacks_php_fallback_path->key = 'NIXPACKS_PHP_FALLBACK_PATH';
$nixpacks_php_fallback_path->value = '/index.php';
$nixpacks_php_fallback_path->is_build_time = false;
@@ -999,7 +996,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$nixpacks_php_fallback_path->save();
}
if (! $nixpacks_php_root_dir) {
- $nixpacks_php_root_dir = new EnvironmentVariable();
+ $nixpacks_php_root_dir = new EnvironmentVariable;
$nixpacks_php_root_dir->key = 'NIXPACKS_PHP_ROOT_DIR';
$nixpacks_php_root_dir->value = '/app/public';
$nixpacks_php_root_dir->is_build_time = false;
@@ -1273,7 +1270,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
continue;
}
// ray('Deploying to additional destination: ', $server->name);
- $deployment_uuid = new Cuid2();
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
deployment_uuid: $deployment_uuid,
application: $this->application,
diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php
index 4afe50d53..79b00e9cd 100644
--- a/app/Jobs/DatabaseBackupJob.php
+++ b/app/Jobs/DatabaseBackupJob.php
@@ -90,6 +90,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
{
try {
BackupCreated::dispatch($this->team->id);
+
// Check if team is exists
if (is_null($this->team)) {
$this->backup->update(['status' => 'failed']);
@@ -476,7 +477,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
} else {
$network = $this->database->destination->network;
}
- $commands[] = "docker run --pull=always -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
+ $commands[] = "docker run -d --network {$network} --name backup-of-{$this->backup->uuid} --rm -v $this->backup_location:$this->backup_location:ro ghcr.io/coollabsio/coolify-helper";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc config host add temporary {$endpoint} $key $secret";
$commands[] = "docker exec backup-of-{$this->backup->uuid} mc cp $this->backup_location temporary/$bucket{$this->backup_dir}/";
instant_remote_process($commands, $this->server);
diff --git a/app/Jobs/SendConfirmationForWaitlistJob.php b/app/Jobs/SendConfirmationForWaitlistJob.php
index 73e8658ee..070598e71 100755
--- a/app/Jobs/SendConfirmationForWaitlistJob.php
+++ b/app/Jobs/SendConfirmationForWaitlistJob.php
@@ -19,7 +19,7 @@ class SendConfirmationForWaitlistJob implements ShouldBeEncrypted, ShouldQueue
public function handle()
{
try {
- $mail = new MailMessage();
+ $mail = new MailMessage;
$confirmation_url = base_url().'/webhooks/waitlist/confirm?email='.$this->email.'&confirmation_code='.$this->uuid;
$cancel_url = base_url().'/webhooks/waitlist/cancel?email='.$this->email.'&confirmation_code='.$this->uuid;
$mail->view('emails.waitlist-confirmation',
diff --git a/app/Jobs/ServerCheckJob.php b/app/Jobs/ServerCheckJob.php
new file mode 100644
index 000000000..adade3194
--- /dev/null
+++ b/app/Jobs/ServerCheckJob.php
@@ -0,0 +1,486 @@
+server->uuid))];
+ }
+
+ public function uniqueId(): int
+ {
+ return $this->server->uuid;
+ }
+
+ public function handle()
+ {
+
+ try {
+ $up = $this->serverStatus();
+ if (! $up) {
+ ray('Server is not reachable.');
+
+ return 'Server is not reachable.';
+ }
+ if (! $this->server->isFunctional()) {
+ ray('Server is not ready.');
+
+ return 'Server is not ready.';
+ }
+ $this->checkSentinel();
+ $this->getContainers();
+
+ if (is_null($this->containers)) {
+ return 'No containers found.';
+ }
+ $this->checkLogDrainContainer();
+ $this->containerStatus();
+
+ } catch (\Throwable $e) {
+ ray($e->getMessage());
+
+ return handleError($e);
+ }
+
+ }
+
+ private function checkSentinel()
+ {
+ if ($this->server->isSentinelEnabled()) {
+ $sentinelContainerFound = $this->containers->filter(function ($value, $key) {
+ return data_get($value, 'Name') === '/coolify-sentinel';
+ })->first();
+ if ($sentinelContainerFound) {
+ $status = data_get($sentinelContainerFound, 'State.Status');
+ if ($status !== 'running') {
+ PullSentinelImageJob::dispatch($this);
+ }
+ }
+ }
+ }
+
+ private function serverStatus()
+ {
+ $this->removeUnnevessaryCoolifyYaml();
+ ['uptime' => $uptime] = $this->server->validateConnection();
+ if ($uptime) {
+ if ($this->server->unreachable_notification_sent === true) {
+ $this->server->update(['unreachable_notification_sent' => false]);
+ }
+ } else {
+ foreach ($this->applications as $application) {
+ $application->update(['status' => 'exited']);
+ }
+ foreach ($this->databases as $database) {
+ $database->update(['status' => 'exited']);
+ }
+ foreach ($this->services as $service) {
+ $apps = $service->applications()->get();
+ $dbs = $service->databases()->get();
+ foreach ($apps as $app) {
+ $app->update(['status' => 'exited']);
+ }
+ foreach ($dbs as $db) {
+ $db->update(['status' => 'exited']);
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+
+ }
+
+ private function removeUnnevessaryCoolifyYaml()
+ {
+ // This will remote the coolify.yaml file from the server as it is not needed on cloud servers
+ if (isCloud() && $this->server->id !== 0) {
+ $file = $this->server->proxyPath().'/dynamic/coolify.yaml';
+
+ return instant_remote_process([
+ "rm -f $file",
+ ], $this->server, false);
+ }
+ }
+
+ private function checkLogDrainContainer()
+ {
+ $foundLogDrainContainer = $this->containers->filter(function ($value, $key) {
+ return data_get($value, 'Name') === '/coolify-log-drain';
+ })->first();
+ if ($foundLogDrainContainer) {
+ $status = data_get($foundLogDrainContainer, 'State.Status');
+ if ($status !== 'running') {
+ InstallLogDrain::dispatch($this->server);
+ }
+ } else {
+ InstallLogDrain::dispatch($this->server);
+ }
+ }
+
+ private function getContainers()
+ {
+ if ($this->server->isSwarm()) {
+ $this->containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
+ $this->containers = format_docker_command_output_to_json($this->containers);
+ $containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
+ if ($containerReplicates) {
+ $containerReplicates = format_docker_command_output_to_json($containerReplicates);
+ foreach ($containerReplicates as $containerReplica) {
+ $name = data_get($containerReplica, 'Name');
+ $this->containers = $this->containers->map(function ($container) use ($name, $containerReplica) {
+ if (data_get($container, 'Spec.Name') === $name) {
+ $replicas = data_get($containerReplica, 'Replicas');
+ $running = str($replicas)->explode('/')[0];
+ $total = str($replicas)->explode('/')[1];
+ if ($running === $total) {
+ data_set($container, 'State.Status', 'running');
+ data_set($container, 'State.Health.Status', 'healthy');
+ } else {
+ data_set($container, 'State.Status', 'starting');
+ data_set($container, 'State.Health.Status', 'unhealthy');
+ }
+ }
+
+ return $container;
+ });
+ }
+ }
+ } else {
+ $this->containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
+ $this->containers = format_docker_command_output_to_json($this->containers);
+ }
+
+ }
+
+ private function containerStatus()
+ {
+
+ $this->applications = $this->server->applications();
+ $this->databases = $this->server->databases();
+ $this->services = $this->server->services()->get();
+ $this->previews = $this->server->previews();
+
+ $foundApplications = [];
+ $foundApplicationPreviews = [];
+ $foundDatabases = [];
+ $foundServices = [];
+
+ foreach ($this->containers as $container) {
+ if ($this->server->isSwarm()) {
+ $labels = data_get($container, 'Spec.Labels');
+ $uuid = data_get($labels, 'coolify.name');
+ } else {
+ $labels = data_get($container, 'Config.Labels');
+ }
+ $containerStatus = data_get($container, 'State.Status');
+ $containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
+ $containerStatus = "$containerStatus ($containerHealth)";
+ $labels = Arr::undot(format_docker_labels_to_json($labels));
+ $applicationId = data_get($labels, 'coolify.applicationId');
+ if ($applicationId) {
+ $pullRequestId = data_get($labels, 'coolify.pullRequestId');
+ if ($pullRequestId) {
+ if (str($applicationId)->contains('-')) {
+ $applicationId = str($applicationId)->before('-');
+ }
+ $preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
+ if ($preview) {
+ $foundApplicationPreviews[] = $preview->id;
+ $statusFromDb = $preview->status;
+ if ($statusFromDb !== $containerStatus) {
+ $preview->update(['status' => $containerStatus]);
+ }
+ } else {
+ //Notify user that this container should not be there.
+ }
+ } else {
+ $application = $this->applications->where('id', $applicationId)->first();
+ if ($application) {
+ $foundApplications[] = $application->id;
+ $statusFromDb = $application->status;
+ if ($statusFromDb !== $containerStatus) {
+ $application->update(['status' => $containerStatus]);
+ }
+ } else {
+ //Notify user that this container should not be there.
+ }
+ }
+ } else {
+ $uuid = data_get($labels, 'com.docker.compose.service');
+ $type = data_get($labels, 'coolify.type');
+
+ if ($uuid) {
+ if ($type === 'service') {
+ $database_id = data_get($labels, 'coolify.service.subId');
+ if ($database_id) {
+ $service_db = ServiceDatabase::where('id', $database_id)->first();
+ if ($service_db) {
+ $uuid = data_get($service_db, 'service.uuid');
+ if ($uuid) {
+ $isPublic = data_get($service_db, 'is_public');
+ if ($isPublic) {
+ $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
+ if ($this->server->isSwarm()) {
+ return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
+ } else {
+ return data_get($value, 'Name') === "/$uuid-proxy";
+ }
+ })->first();
+ if (! $foundTcpProxy) {
+ StartDatabaseProxy::run($service_db);
+ // $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
+ }
+ }
+ }
+ }
+ }
+ } else {
+ $database = $this->databases->where('uuid', $uuid)->first();
+ if ($database) {
+ $isPublic = data_get($database, 'is_public');
+ $foundDatabases[] = $database->id;
+ $statusFromDb = $database->status;
+ if ($statusFromDb !== $containerStatus) {
+ $database->update(['status' => $containerStatus]);
+ }
+ if ($isPublic) {
+ $foundTcpProxy = $this->containers->filter(function ($value, $key) use ($uuid) {
+ if ($this->server->isSwarm()) {
+ return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
+ } else {
+ return data_get($value, 'Name') === "/$uuid-proxy";
+ }
+ })->first();
+ if (! $foundTcpProxy) {
+ StartDatabaseProxy::run($database);
+ $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
+ }
+ }
+ } else {
+ // Notify user that this container should not be there.
+ }
+ }
+ }
+ if (data_get($container, 'Name') === '/coolify-db') {
+ $foundDatabases[] = 0;
+ }
+ }
+ $serviceLabelId = data_get($labels, 'coolify.serviceId');
+ if ($serviceLabelId) {
+ $subType = data_get($labels, 'coolify.service.subType');
+ $subId = data_get($labels, 'coolify.service.subId');
+ $service = $this->services->where('id', $serviceLabelId)->first();
+ if (! $service) {
+ continue;
+ }
+ if ($subType === 'application') {
+ $service = $service->applications()->where('id', $subId)->first();
+ } else {
+ $service = $service->databases()->where('id', $subId)->first();
+ }
+ if ($service) {
+ $foundServices[] = "$service->id-$service->name";
+ $statusFromDb = $service->status;
+ if ($statusFromDb !== $containerStatus) {
+ // ray('Updating status: ' . $containerStatus);
+ $service->update(['status' => $containerStatus]);
+ }
+ }
+ }
+ }
+ $exitedServices = collect([]);
+ foreach ($this->services as $service) {
+ $apps = $service->applications()->get();
+ $dbs = $service->databases()->get();
+ foreach ($apps as $app) {
+ if (in_array("$app->id-$app->name", $foundServices)) {
+ continue;
+ } else {
+ $exitedServices->push($app);
+ }
+ }
+ foreach ($dbs as $db) {
+ if (in_array("$db->id-$db->name", $foundServices)) {
+ continue;
+ } else {
+ $exitedServices->push($db);
+ }
+ }
+ }
+ $exitedServices = $exitedServices->unique('id');
+ foreach ($exitedServices as $exitedService) {
+ if (str($exitedService->status)->startsWith('exited')) {
+ continue;
+ }
+ $name = data_get($exitedService, 'name');
+ $fqdn = data_get($exitedService, 'fqdn');
+ if ($name) {
+ if ($fqdn) {
+ $containerName = "$name, available at $fqdn";
+ } else {
+ $containerName = $name;
+ }
+ } else {
+ if ($fqdn) {
+ $containerName = $fqdn;
+ } else {
+ $containerName = null;
+ }
+ }
+ $projectUuid = data_get($service, 'environment.project.uuid');
+ $serviceUuid = data_get($service, 'uuid');
+ $environmentName = data_get($service, 'environment.name');
+
+ if ($projectUuid && $serviceUuid && $environmentName) {
+ $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
+ } else {
+ $url = null;
+ }
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ $exitedService->update(['status' => 'exited']);
+ }
+
+ $notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
+ foreach ($notRunningApplications as $applicationId) {
+ $application = $this->applications->where('id', $applicationId)->first();
+ if (str($application->status)->startsWith('exited')) {
+ continue;
+ }
+ $application->update(['status' => 'exited']);
+
+ $name = data_get($application, 'name');
+ $fqdn = data_get($application, 'fqdn');
+
+ $containerName = $name ? "$name ($fqdn)" : $fqdn;
+
+ $projectUuid = data_get($application, 'environment.project.uuid');
+ $applicationUuid = data_get($application, 'uuid');
+ $environment = data_get($application, 'environment.name');
+
+ if ($projectUuid && $applicationUuid && $environment) {
+ $url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
+ } else {
+ $url = null;
+ }
+
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ }
+ $notRunningApplicationPreviews = $this->previews->pluck('id')->diff($foundApplicationPreviews);
+ foreach ($notRunningApplicationPreviews as $previewId) {
+ $preview = $this->previews->where('id', $previewId)->first();
+ if (str($preview->status)->startsWith('exited')) {
+ continue;
+ }
+ $preview->update(['status' => 'exited']);
+
+ $name = data_get($preview, 'name');
+ $fqdn = data_get($preview, 'fqdn');
+
+ $containerName = $name ? "$name ($fqdn)" : $fqdn;
+
+ $projectUuid = data_get($preview, 'application.environment.project.uuid');
+ $environmentName = data_get($preview, 'application.environment.name');
+ $applicationUuid = data_get($preview, 'application.uuid');
+
+ if ($projectUuid && $applicationUuid && $environmentName) {
+ $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
+ } else {
+ $url = null;
+ }
+
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ }
+ $notRunningDatabases = $this->databases->pluck('id')->diff($foundDatabases);
+ foreach ($notRunningDatabases as $database) {
+ $database = $this->databases->where('id', $database)->first();
+ if (str($database->status)->startsWith('exited')) {
+ continue;
+ }
+ $database->update(['status' => 'exited']);
+
+ $name = data_get($database, 'name');
+ $fqdn = data_get($database, 'fqdn');
+
+ $containerName = $name;
+
+ $projectUuid = data_get($database, 'environment.project.uuid');
+ $environmentName = data_get($database, 'environment.name');
+ $databaseUuid = data_get($database, 'uuid');
+
+ if ($projectUuid && $databaseUuid && $environmentName) {
+ $url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
+ } else {
+ $url = null;
+ }
+ // $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
+ }
+
+ // Check if proxy is running
+ $this->server->proxyType();
+ $foundProxyContainer = $this->containers->filter(function ($value, $key) {
+ if ($this->server->isSwarm()) {
+ return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
+ } else {
+ return data_get($value, 'Name') === '/coolify-proxy';
+ }
+ })->first();
+ if (! $foundProxyContainer) {
+ try {
+ $shouldStart = CheckProxy::run($this->server);
+ if ($shouldStart) {
+ StartProxy::run($this->server, false);
+ $this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
+ }
+ } catch (\Throwable $e) {
+ ray($e);
+ }
+ } else {
+ $this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
+ $this->server->save();
+ $connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
+ instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
+ }
+ }
+}
diff --git a/app/Jobs/SubscriptionInvoiceFailedJob.php b/app/Jobs/SubscriptionInvoiceFailedJob.php
index 64a75671f..b4ef7baa0 100755
--- a/app/Jobs/SubscriptionInvoiceFailedJob.php
+++ b/app/Jobs/SubscriptionInvoiceFailedJob.php
@@ -21,7 +21,7 @@ class SubscriptionInvoiceFailedJob implements ShouldBeEncrypted, ShouldQueue
{
try {
$session = getStripeCustomerPortalSession($this->team);
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->view('emails.subscription-invoice-failed', [
'stripeCustomerPortal' => $session->url,
]);
diff --git a/app/Jobs/SubscriptionTrialEndedJob.php b/app/Jobs/SubscriptionTrialEndedJob.php
index dd2250dd7..8635b439c 100755
--- a/app/Jobs/SubscriptionTrialEndedJob.php
+++ b/app/Jobs/SubscriptionTrialEndedJob.php
@@ -23,7 +23,7 @@ class SubscriptionTrialEndedJob implements ShouldBeEncrypted, ShouldQueue
{
try {
$session = getStripeCustomerPortalSession($this->team);
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject('Action required: You trial in Coolify Cloud ended.');
$mail->view('emails.trial-ended', [
'stripeCustomerPortal' => $session->url,
diff --git a/app/Jobs/SubscriptionTrialEndsSoonJob.php b/app/Jobs/SubscriptionTrialEndsSoonJob.php
index 80e232a3e..244624749 100755
--- a/app/Jobs/SubscriptionTrialEndsSoonJob.php
+++ b/app/Jobs/SubscriptionTrialEndsSoonJob.php
@@ -23,7 +23,7 @@ class SubscriptionTrialEndsSoonJob implements ShouldBeEncrypted, ShouldQueue
{
try {
$session = getStripeCustomerPortalSession($this->team);
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject('You trial in Coolify Cloud ends soon.');
$mail->view('emails.trial-ends-soon', [
'stripeCustomerPortal' => $session->url,
diff --git a/app/Listeners/MaintenanceModeDisabledNotification.php b/app/Listeners/MaintenanceModeDisabledNotification.php
index ded53ccee..c7cd1bcde 100644
--- a/app/Listeners/MaintenanceModeDisabledNotification.php
+++ b/app/Listeners/MaintenanceModeDisabledNotification.php
@@ -38,7 +38,7 @@ class MaintenanceModeDisabledNotification
$class = "App\Http\Controllers\Webhook\\".ucfirst(str($endpoint)->before('::')->value());
$method = str($endpoint)->after('::')->value();
try {
- $instance = new $class();
+ $instance = new $class;
$instance->$method($request);
} catch (\Throwable $th) {
ray($th);
diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php
index 7acf5ed87..8127ca009 100644
--- a/app/Livewire/Boarding/Index.php
+++ b/app/Livewire/Boarding/Index.php
@@ -257,7 +257,6 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->createdServer->settings->is_swarm_manager = $this->isSwarmManager;
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
$this->createdServer->settings->save();
- $this->createdServer->addInitialNetwork();
$this->selectedExistingServer = $this->createdServer->id;
$this->currentState = 'validate-server';
}
diff --git a/app/Livewire/Destination/New/Docker.php b/app/Livewire/Destination/New/Docker.php
index f822cfa5f..4fc938df8 100644
--- a/app/Livewire/Destination/New/Docker.php
+++ b/app/Livewire/Destination/New/Docker.php
@@ -52,7 +52,7 @@ class Docker extends Component
if (request()->query('network_name')) {
$this->network = request()->query('network_name');
} else {
- $this->network = new Cuid2(7);
+ $this->network = new Cuid2;
}
if ($this->servers->count() > 0) {
$this->name = str("{$this->servers->first()->name}-{$this->network}")->kebab();
diff --git a/app/Livewire/Help.php b/app/Livewire/Help.php
index 0289f5f36..d6dc0d521 100644
--- a/app/Livewire/Help.php
+++ b/app/Livewire/Help.php
@@ -38,7 +38,7 @@ class Help extends Component
$this->rateLimit(3, 30);
$this->validate();
$debug = "Route: {$this->path}";
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->view(
'emails.help',
[
diff --git a/app/Livewire/MonacoEditor.php b/app/Livewire/MonacoEditor.php
index 156c63d3a..42d276e64 100644
--- a/app/Livewire/MonacoEditor.php
+++ b/app/Livewire/MonacoEditor.php
@@ -39,7 +39,7 @@ class MonacoEditor extends Component
public function render()
{
if (is_null($this->id)) {
- $this->id = new Cuid2(7);
+ $this->id = new Cuid2;
}
if (is_null($this->name)) {
diff --git a/app/Livewire/Notifications/Discord.php b/app/Livewire/Notifications/Discord.php
index f2219bbc6..65c202b7d 100644
--- a/app/Livewire/Notifications/Discord.php
+++ b/app/Livewire/Notifications/Discord.php
@@ -56,7 +56,7 @@ class Discord extends Component
public function sendTestNotification()
{
- $this->team?->notify(new Test());
+ $this->team?->notify(new Test);
$this->dispatch('success', 'Test notification sent.');
}
diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php
index 16123f123..e163a25e0 100644
--- a/app/Livewire/Notifications/Telegram.php
+++ b/app/Livewire/Notifications/Telegram.php
@@ -63,7 +63,7 @@ class Telegram extends Component
public function sendTestNotification()
{
- $this->team?->notify(new Test());
+ $this->team?->notify(new Test);
$this->dispatch('success', 'Test notification sent.');
}
diff --git a/app/Livewire/Project/Application/Advanced.php b/app/Livewire/Project/Application/Advanced.php
index 3b402b3ec..2bc28026f 100644
--- a/app/Livewire/Project/Application/Advanced.php
+++ b/app/Livewire/Project/Application/Advanced.php
@@ -96,6 +96,20 @@ class Advanced extends Component
} else {
$this->application->settings->custom_internal_name = null;
}
+ $customInternalName = $this->application->settings->custom_internal_name;
+ $server = $this->application->destination->server;
+ $allApplications = $server->applications();
+
+ $foundSameInternalName = $allApplications->filter(function ($application) {
+ return $application->id !== $this->application->id && $application->settings->custom_internal_name === $this->application->settings->custom_internal_name;
+ });
+ if ($foundSameInternalName->isNotEmpty()) {
+ $this->dispatch('error', 'This custom container name is already in use by another application on this server.');
+ $this->application->settings->custom_internal_name = $customInternalName;
+ $this->application->settings->refresh();
+
+ return;
+ }
$this->application->settings->save();
$this->dispatch('success', 'Custom name saved.');
}
diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php
index 7dfd9bad4..395c45524 100644
--- a/app/Livewire/Project/Application/General.php
+++ b/app/Livewire/Project/Application/General.php
@@ -228,7 +228,7 @@ class General extends Component
public function generateDomain(string $serviceName)
{
- $uuid = new Cuid2(7);
+ $uuid = new Cuid2;
$domain = generateFqdn($this->application->destination->server, $uuid);
$this->parsedServiceDomains[$serviceName]['domain'] = $domain;
$this->application->docker_compose_domains = json_encode($this->parsedServiceDomains);
diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php
index feb54c7f0..b5f01587a 100644
--- a/app/Livewire/Project/Application/Heading.php
+++ b/app/Livewire/Project/Application/Heading.php
@@ -102,7 +102,7 @@ class Heading extends Component
protected function setDeploymentUuid()
{
- $this->deploymentUuid = new Cuid2(7);
+ $this->deploymentUuid = new Cuid2;
$this->parameters['deployment_uuid'] = $this->deploymentUuid;
}
diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php
index df64c3fd3..30bc0a9d1 100644
--- a/app/Livewire/Project/Application/Previews.php
+++ b/app/Livewire/Project/Application/Previews.php
@@ -85,7 +85,7 @@ class Previews extends Component
$template = $this->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
- $random = new Cuid2(7);
+ $random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $preview->pull_request_id, $preview_fqdn);
@@ -170,7 +170,7 @@ class Previews extends Component
protected function setDeploymentUuid()
{
- $this->deployment_uuid = new Cuid2(7);
+ $this->deployment_uuid = new Cuid2;
$this->parameters['deployment_uuid'] = $this->deployment_uuid;
}
diff --git a/app/Livewire/Project/Application/PreviewsCompose.php b/app/Livewire/Project/Application/PreviewsCompose.php
index bf4478e53..b3e838bb3 100644
--- a/app/Livewire/Project/Application/PreviewsCompose.php
+++ b/app/Livewire/Project/Application/PreviewsCompose.php
@@ -44,7 +44,7 @@ class PreviewsCompose extends Component
$template = $this->preview->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
- $random = new Cuid2(7);
+ $random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->preview->pull_request_id, $preview_fqdn);
diff --git a/app/Livewire/Project/Application/Rollback.php b/app/Livewire/Project/Application/Rollback.php
index ed0ac1cef..1e58a1458 100644
--- a/app/Livewire/Project/Application/Rollback.php
+++ b/app/Livewire/Project/Application/Rollback.php
@@ -23,7 +23,7 @@ class Rollback extends Component
public function rollbackImage($commit)
{
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
queue_application_deployment(
application: $this->application,
diff --git a/app/Livewire/Project/CloneMe.php b/app/Livewire/Project/CloneMe.php
index 5373f1b3f..4d2bc6589 100644
--- a/app/Livewire/Project/CloneMe.php
+++ b/app/Livewire/Project/CloneMe.php
@@ -47,7 +47,7 @@ class CloneMe extends Component
$this->environment = $this->project->environments->where('name', $this->environment_name)->first();
$this->project_id = $this->project->id;
$this->servers = currentTeam()->servers;
- $this->newName = str($this->project->name.'-clone-'.(string) new Cuid2(7))->slug();
+ $this->newName = str($this->project->name.'-clone-'.(string) new Cuid2)->slug();
}
public function render()
@@ -106,7 +106,7 @@ class CloneMe extends Component
$databases = $this->environment->databases();
$services = $this->environment->services;
foreach ($applications as $application) {
- $uuid = (string) new Cuid2(7);
+ $uuid = (string) new Cuid2;
$newApplication = $application->replicate()->fill([
'uuid' => $uuid,
'fqdn' => generateFqdn($this->server, $uuid),
@@ -133,7 +133,7 @@ class CloneMe extends Component
}
}
foreach ($databases as $database) {
- $uuid = (string) new Cuid2(7);
+ $uuid = (string) new Cuid2;
$newDatabase = $database->replicate()->fill([
'uuid' => $uuid,
'status' => 'exited',
@@ -161,7 +161,7 @@ class CloneMe extends Component
}
}
foreach ($services as $service) {
- $uuid = (string) new Cuid2(7);
+ $uuid = (string) new Cuid2;
$newService = $service->replicate()->fill([
'uuid' => $uuid,
'environment_id' => $environment->id,
diff --git a/app/Livewire/Project/New/DockerImage.php b/app/Livewire/Project/New/DockerImage.php
index fdad052c7..d3f5b5261 100644
--- a/app/Livewire/Project/New/DockerImage.php
+++ b/app/Livewire/Project/New/DockerImage.php
@@ -48,7 +48,7 @@ class DockerImage extends Component
$environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first();
ray($image, $tag);
$application = Application::create([
- 'name' => 'docker-image-'.new Cuid2(7),
+ 'name' => 'docker-image-'.new Cuid2,
'repository_project_id' => 0,
'git_repository' => 'coollabsio/coolify',
'git_branch' => 'main',
diff --git a/app/Livewire/Project/New/SimpleDockerfile.php b/app/Livewire/Project/New/SimpleDockerfile.php
index 6f6bc9185..3c7f42329 100644
--- a/app/Livewire/Project/New/SimpleDockerfile.php
+++ b/app/Livewire/Project/New/SimpleDockerfile.php
@@ -53,7 +53,7 @@ CMD ["nginx", "-g", "daemon off;"]
$port = 80;
}
$application = Application::create([
- 'name' => 'dockerfile-'.new Cuid2(7),
+ 'name' => 'dockerfile-'.new Cuid2,
'repository_project_id' => 0,
'git_repository' => 'coollabsio/coolify',
'git_branch' => 'main',
diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php
index 7d3987b3d..419fef505 100644
--- a/app/Livewire/Project/Service/Navbar.php
+++ b/app/Livewire/Project/Service/Navbar.php
@@ -62,11 +62,17 @@ class Navbar extends Component
public function checkDeployments()
{
- $activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first();
- $status = data_get($activity, 'properties.status');
- if ($status === 'queued' || $status === 'in_progress') {
- $this->isDeploymentProgress = true;
- } else {
+ try {
+ // TODO: This is a temporary solution. We need to refactor this.
+ // We need to delete null bytes somehow.
+ $activity = Activity::where('properties->type_uuid', $this->service->uuid)->latest()->first();
+ $status = data_get($activity, 'properties.status');
+ if ($status === 'queued' || $status === 'in_progress') {
+ $this->isDeploymentProgress = true;
+ } else {
+ $this->isDeploymentProgress = false;
+ }
+ } catch (\Throwable $e) {
$this->isDeploymentProgress = false;
}
}
diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php
index 30c35410f..5f0178be4 100644
--- a/app/Livewire/Project/Shared/Danger.php
+++ b/app/Livewire/Project/Shared/Danger.php
@@ -22,7 +22,7 @@ class Danger extends Component
public function mount()
{
- $this->modalId = new Cuid2(7);
+ $this->modalId = new Cuid2;
$parameters = get_route_parameters();
$this->projectUuid = data_get($parameters, 'project_uuid');
$this->environmentName = data_get($parameters, 'environment_name');
diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php
index 22ada8ab8..a2c018beb 100644
--- a/app/Livewire/Project/Shared/Destination.php
+++ b/app/Livewire/Project/Shared/Destination.php
@@ -67,7 +67,7 @@ class Destination extends Component
return;
}
- $deployment_uuid = new Cuid2(7);
+ $deployment_uuid = new Cuid2;
$server = Server::find($server_id);
$destination = StandaloneDocker::find($network_id);
queue_application_deployment(
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
index b732b6b52..a859c90b0 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Add.php
@@ -48,14 +48,14 @@ class Add extends Component
public function submit()
{
$this->validate();
- if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) {
- $type = str($this->value)->after('{{')->before('.')->value;
- if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
- $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
+ // if (str($this->value)->startsWith('{{') && str($this->value)->endsWith('}}')) {
+ // $type = str($this->value)->after('{{')->before('.')->value;
+ // if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
+ // $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
- return;
- }
- }
+ // return;
+ // }
+ // }
$this->dispatch('saveKey', [
'key' => $this->key,
'value' => $this->value,
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
index d67dae19e..9e6760293 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php
@@ -39,7 +39,7 @@ class All extends Component
if (str($this->resourceClass)->contains($resourceWithPreviews) && ! $simpleDockerfile) {
$this->showPreview = true;
}
- $this->modalId = new Cuid2(7);
+ $this->modalId = new Cuid2;
$this->sortMe();
$this->getDevView();
}
@@ -125,29 +125,29 @@ class All extends Component
continue;
}
$found->value = $variable;
- if (str($found->value)->startsWith('{{') && str($found->value)->endsWith('}}')) {
- $type = str($found->value)->after('{{')->before('.')->value;
- if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
- $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
+ // if (str($found->value)->startsWith('{{') && str($found->value)->endsWith('}}')) {
+ // $type = str($found->value)->after('{{')->before('.')->value;
+ // if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
+ // $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
- return;
- }
- }
+ // return;
+ // }
+ // }
$found->save();
continue;
} else {
- $environment = new EnvironmentVariable();
+ $environment = new EnvironmentVariable;
$environment->key = $key;
$environment->value = $variable;
- if (str($environment->value)->startsWith('{{') && str($environment->value)->endsWith('}}')) {
- $type = str($environment->value)->after('{{')->before('.')->value;
- if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
- $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
+ // if (str($environment->value)->startsWith('{{') && str($environment->value)->endsWith('}}')) {
+ // $type = str($environment->value)->after('{{')->before('.')->value;
+ // if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
+ // $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
- return;
- }
- }
+ // return;
+ // }
+ // }
$environment->is_build_time = false;
$environment->is_multiline = false;
$environment->is_preview = $isPreview ? true : false;
@@ -209,7 +209,7 @@ class All extends Component
return;
}
- $environment = new EnvironmentVariable();
+ $environment = new EnvironmentVariable;
$environment->key = $data['key'];
$environment->value = $data['value'];
$environment->is_build_time = $data['is_build_time'];
diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
index c21d899e5..e63871602 100644
--- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
+++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php
@@ -24,6 +24,7 @@ class Show extends Component
public string $type;
protected $listeners = [
+ 'refresh' => 'refresh',
'compose_loaded' => '$refresh',
];
@@ -46,12 +47,18 @@ class Show extends Component
'env.is_shown_once' => 'Shown Once',
];
+ public function refresh()
+ {
+ $this->env->refresh();
+ $this->checkEnvs();
+ }
+
public function mount()
{
if ($this->env->getMorphClass() === 'App\Models\SharedEnvironmentVariable') {
$this->isSharedVariable = true;
}
- $this->modalId = new Cuid2(7);
+ $this->modalId = new Cuid2;
$this->parameters = get_route_parameters();
$this->checkEnvs();
}
@@ -101,14 +108,14 @@ class Show extends Component
} else {
$this->validate();
}
- if (str($this->env->value)->startsWith('{{') && str($this->env->value)->endsWith('}}')) {
- $type = str($this->env->value)->after('{{')->before('.')->value;
- if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
- $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
+ // if (str($this->env->value)->startsWith('{{') && str($this->env->value)->endsWith('}}')) {
+ // $type = str($this->env->value)->after('{{')->before('.')->value;
+ // if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
+ // $this->dispatch('error', 'Invalid shared variable type.', 'Valid types are: team, project, environment.');
- return;
- }
- }
+ // return;
+ // }
+ // }
$this->serialize();
$this->env->save();
$this->dispatch('success', 'Environment variable updated.');
diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php
index 586a125ae..ec09eb80f 100644
--- a/app/Livewire/Project/Shared/ResourceOperations.php
+++ b/app/Livewire/Project/Shared/ResourceOperations.php
@@ -39,7 +39,7 @@ class ResourceOperations extends Component
if (! $new_destination) {
return $this->addError('destination_id', 'Destination not found.');
}
- $uuid = (string) new Cuid2(7);
+ $uuid = (string) new Cuid2;
$server = $new_destination->server;
if ($this->resource->getMorphClass() === 'App\Models\Application') {
$new_resource = $this->resource->replicate()->fill([
@@ -87,7 +87,7 @@ class ResourceOperations extends Component
$this->resource->getMorphClass() === 'App\Models\StandaloneDragonfly' ||
$this->resource->getMorphClass() === 'App\Models\StandaloneClickhouse'
) {
- $uuid = (string) new Cuid2(7);
+ $uuid = (string) new Cuid2;
$new_resource = $this->resource->replicate()->fill([
'uuid' => $uuid,
'name' => $this->resource->name.'-clone-'.$uuid,
@@ -121,7 +121,7 @@ class ResourceOperations extends Component
return redirect()->to($route);
} elseif ($this->resource->type() === 'service') {
- $uuid = (string) new Cuid2(7);
+ $uuid = (string) new Cuid2;
$new_resource = $this->resource->replicate()->fill([
'uuid' => $uuid,
'name' => $this->resource->name.'-clone-'.$uuid,
diff --git a/app/Livewire/Project/Shared/ScheduledTask/All.php b/app/Livewire/Project/Shared/ScheduledTask/All.php
index 1aa5a2b87..4d8c87dbf 100644
--- a/app/Livewire/Project/Shared/ScheduledTask/All.php
+++ b/app/Livewire/Project/Shared/ScheduledTask/All.php
@@ -43,7 +43,7 @@ class All extends Component
public function submit($data)
{
try {
- $task = new ScheduledTask();
+ $task = new ScheduledTask;
$task->name = $data['name'];
$task->command = $data['command'];
$task->frequency = $data['frequency'];
diff --git a/app/Livewire/Project/Shared/ScheduledTask/Show.php b/app/Livewire/Project/Shared/ScheduledTask/Show.php
index dbd420d94..8be4ff643 100644
--- a/app/Livewire/Project/Shared/ScheduledTask/Show.php
+++ b/app/Livewire/Project/Shared/ScheduledTask/Show.php
@@ -47,7 +47,7 @@ class Show extends Component
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
}
- $this->modalId = new Cuid2(7);
+ $this->modalId = new Cuid2;
$this->task = ModelsScheduledTask::where('uuid', request()->route('task_uuid'))->first();
}
diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php
index c2a55afcb..9934ea345 100644
--- a/app/Livewire/Server/Form.php
+++ b/app/Livewire/Server/Form.php
@@ -164,6 +164,9 @@ class Form extends Component
public function validateServer($install = true)
{
+ $this->server->update([
+ 'validation_logs' => null,
+ ]);
$this->dispatch('init', $install);
}
diff --git a/app/Livewire/Server/New/ByIp.php b/app/Livewire/Server/New/ByIp.php
index 0aad33b1c..0f4c1afea 100644
--- a/app/Livewire/Server/New/ByIp.php
+++ b/app/Livewire/Server/New/ByIp.php
@@ -124,7 +124,6 @@ class ByIp extends Component
}
$server->settings->is_build_server = $this->is_build_server;
$server->settings->save();
- $server->addInitialNetwork();
return redirect()->route('server.show', $server->uuid);
} catch (\Throwable $e) {
diff --git a/app/Livewire/Server/Proxy/Deploy.php b/app/Livewire/Server/Proxy/Deploy.php
index 965c51e2d..2279951ee 100644
--- a/app/Livewire/Server/Proxy/Deploy.php
+++ b/app/Livewire/Server/Proxy/Deploy.php
@@ -50,7 +50,7 @@ class Deploy extends Component
public function proxyStarted()
{
CheckProxy::run($this->server, true);
- $this->dispatch('success', 'Proxy started.');
+ $this->dispatch('proxyStatusUpdated');
}
public function proxyStatusUpdated()
@@ -61,7 +61,7 @@ class Deploy extends Component
public function restart()
{
try {
- $this->stop();
+ $this->stop(forceStop: false);
$this->dispatch('checkProxy');
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -91,7 +91,7 @@ class Deploy extends Component
}
}
- public function stop()
+ public function stop(bool $forceStop = true)
{
try {
if ($this->server->isSwarm()) {
@@ -104,7 +104,7 @@ class Deploy extends Component
], $this->server);
}
$this->server->proxy->status = 'exited';
- $this->server->proxy->force_stop = true;
+ $this->server->proxy->force_stop = $forceStop;
$this->server->save();
$this->dispatch('proxyStatusUpdated');
} catch (\Throwable $e) {
diff --git a/app/Livewire/Server/Proxy/Status.php b/app/Livewire/Server/Proxy/Status.php
index 8dd4dd8e6..d23d7fc20 100644
--- a/app/Livewire/Server/Proxy/Status.php
+++ b/app/Livewire/Server/Proxy/Status.php
@@ -16,7 +16,10 @@ class Status extends Component
public int $numberOfPolls = 0;
- protected $listeners = ['proxyStatusUpdated' => '$refresh', 'startProxyPolling'];
+ protected $listeners = [
+ 'proxyStatusUpdated',
+ 'startProxyPolling',
+ ];
public function startProxyPolling()
{
diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php
index 422cae779..8c5bc23ed 100644
--- a/app/Livewire/Server/ValidateAndInstall.php
+++ b/app/Livewire/Server/ValidateAndInstall.php
@@ -87,7 +87,10 @@ class ValidateAndInstall extends Component
{
['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection();
if (! $this->uptime) {
- $this->error = 'Server is not reachable. Please validate your configuration and connection. Check this documentation for further help. Error: '.$error;
+ $this->error = 'Server is not reachable. Please validate your configuration and connection. Check this documentation for further help. Error: '.$error.'
';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
}
@@ -99,6 +102,9 @@ class ValidateAndInstall extends Component
$this->supported_os_type = $this->server->validateOS();
if (! $this->supported_os_type) {
$this->error = 'Server OS type is not supported. Please install Docker manually before continuing: documentation .';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
}
@@ -113,6 +119,9 @@ class ValidateAndInstall extends Component
if ($this->install) {
if ($this->number_of_tries == $this->max_tries) {
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: documentation .';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
} else {
@@ -126,6 +135,9 @@ class ValidateAndInstall extends Component
}
} else {
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: documentation .';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
}
@@ -148,6 +160,9 @@ class ValidateAndInstall extends Component
$this->dispatch('success', 'Server validated.');
} else {
$this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: documentation .';
+ $this->server->update([
+ 'validation_logs' => $this->error,
+ ]);
return;
}
diff --git a/app/Livewire/Storage/Create.php b/app/Livewire/Storage/Create.php
index 1ccc3997c..a05834ecc 100644
--- a/app/Livewire/Storage/Create.php
+++ b/app/Livewire/Storage/Create.php
@@ -59,7 +59,7 @@ class Create extends Component
{
try {
$this->validate();
- $this->storage = new S3Storage();
+ $this->storage = new S3Storage;
$this->storage->name = $this->name;
$this->storage->description = $this->description ?? null;
$this->storage->region = $this->region;
diff --git a/app/Livewire/Tags/Index.php b/app/Livewire/Tags/Index.php
index 2e3fbd8e0..a01d00a70 100644
--- a/app/Livewire/Tags/Index.php
+++ b/app/Livewire/Tags/Index.php
@@ -51,11 +51,11 @@ class Index extends Component
{
try {
$this->applications->each(function ($resource) {
- $deploy = new DeployController();
+ $deploy = new DeployController;
$deploy->deploy_resource($resource);
});
$this->services->each(function ($resource) {
- $deploy = new DeployController();
+ $deploy = new DeployController;
$deploy->deploy_resource($resource);
});
$this->dispatch('success', 'Mass deployment started.');
diff --git a/app/Livewire/Tags/Show.php b/app/Livewire/Tags/Show.php
index ccd190c16..668101edb 100644
--- a/app/Livewire/Tags/Show.php
+++ b/app/Livewire/Tags/Show.php
@@ -59,11 +59,11 @@ class Show extends Component
try {
$message = collect([]);
$this->applications->each(function ($resource) use ($message) {
- $deploy = new DeployController();
+ $deploy = new DeployController;
$message->push($deploy->deploy_resource($resource));
});
$this->services->each(function ($resource) use ($message) {
- $deploy = new DeployController();
+ $deploy = new DeployController;
$message->push($deploy->deploy_resource($resource));
});
$this->dispatch('success', 'Mass deployment started.');
diff --git a/app/Livewire/Team/InviteLink.php b/app/Livewire/Team/InviteLink.php
index 0fa9e980c..6c9e405fc 100644
--- a/app/Livewire/Team/InviteLink.php
+++ b/app/Livewire/Team/InviteLink.php
@@ -79,7 +79,7 @@ class InviteLink extends Component
'via' => $sendEmail ? 'email' : 'link',
]);
if ($sendEmail) {
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->view('emails.invitation-link', [
'team' => currentTeam()->name,
'invitation_link' => $link,
diff --git a/app/Models/Application.php b/app/Models/Application.php
index 81fdb5a1e..538eb89d9 100644
--- a/app/Models/Application.php
+++ b/app/Models/Application.php
@@ -232,12 +232,24 @@ class Application extends BaseModel
public function failedTaskLink($task_uuid)
{
if (data_get($this, 'environment.project.uuid')) {
- return route('project.application.scheduled-tasks', [
+ $route = route('project.application.scheduled-tasks', [
'project_uuid' => data_get($this, 'environment.project.uuid'),
'environment_name' => data_get($this, 'environment.name'),
'application_uuid' => data_get($this, 'uuid'),
'task_uuid' => $task_uuid,
]);
+ $settings = InstanceSettings::get();
+ if (data_get($settings, 'fqdn')) {
+ $url = Url::fromString($route);
+ $url = $url->withPort(null);
+ $fqdn = data_get($settings, 'fqdn');
+ $fqdn = str_replace(['http://', 'https://'], '', $fqdn);
+ $url = $url->withHost($fqdn);
+
+ return $url->__toString();
+ }
+
+ return $route;
}
return null;
@@ -275,12 +287,20 @@ class Application extends BaseModel
return Attribute::make(
get: function () {
if (! is_null($this->source?->html_url) && ! is_null($this->git_repository) && ! is_null($this->git_branch)) {
+ if (str($this->git_repository)->contains('bitbucket')) {
+ return "{$this->source->html_url}/{$this->git_repository}/src/{$this->git_branch}";
+ }
+
return "{$this->source->html_url}/{$this->git_repository}/tree/{$this->git_branch}";
}
// Convert the SSH URL to HTTPS URL
if (strpos($this->git_repository, 'git@') === 0) {
$git_repository = str_replace(['git@', ':', '.git'], ['', '/', ''], $this->git_repository);
+ if (str($this->git_repository)->contains('bitbucket')) {
+ return "https://{$git_repository}/src/{$this->git_branch}";
+ }
+
return "https://{$git_repository}/tree/{$this->git_branch}";
}
@@ -1066,7 +1086,7 @@ class Application extends BaseModel
if ($isInit && $this->docker_compose_raw) {
return;
}
- $uuid = new Cuid2();
+ $uuid = new Cuid2;
['commands' => $cloneCommand] = $this->generateGitImportCommands(deployment_uuid: $uuid, only_checkout: true, exec_in_docker: false, custom_base_dir: '.');
$workdir = rtrim($this->base_directory, '/');
$composeFile = $this->docker_compose_location;
@@ -1270,7 +1290,7 @@ class Application extends BaseModel
$template = $this->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
- $random = new Cuid2(7);
+ $random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);
diff --git a/app/Models/ApplicationPreview.php b/app/Models/ApplicationPreview.php
index 3bdd24014..2825f984f 100644
--- a/app/Models/ApplicationPreview.php
+++ b/app/Models/ApplicationPreview.php
@@ -49,7 +49,7 @@ class ApplicationPreview extends BaseModel
$template = $this->application->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
- $random = new Cuid2(7);
+ $random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php
index 7e028a6b5..17201ea6e 100644
--- a/app/Models/BaseModel.php
+++ b/app/Models/BaseModel.php
@@ -14,7 +14,7 @@ abstract class BaseModel extends Model
static::creating(function (Model $model) {
// Generate a UUID if one isn't set
if (! $model->uuid) {
- $model->uuid = (string) new Cuid2(7);
+ $model->uuid = (string) new Cuid2;
}
});
}
diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php
index 1d2a9dc66..5e1d8ae13 100644
--- a/app/Models/EnvironmentVariable.php
+++ b/app/Models/EnvironmentVariable.php
@@ -5,7 +5,6 @@ namespace App\Models;
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
-use Illuminate\Support\Str;
use OpenApi\Attributes as OA;
use Symfony\Component\Yaml\Yaml;
use Visus\Cuid2\Cuid2;
@@ -52,7 +51,7 @@ class EnvironmentVariable extends Model
{
static::creating(function (Model $model) {
if (! $model->uuid) {
- $model->uuid = (string) new Cuid2();
+ $model->uuid = (string) new Cuid2;
}
});
static::created(function (EnvironmentVariable $environment_variable) {
@@ -200,28 +199,33 @@ class EnvironmentVariable extends Model
return null;
}
$environment_variable = trim($environment_variable);
- $type = str($environment_variable)->after('{{')->before('.')->value;
- if (str($environment_variable)->startsWith('{{'.$type) && str($environment_variable)->endsWith('}}')) {
- $variable = Str::after($environment_variable, "{$type}.");
- $variable = Str::before($variable, '}}');
- $variable = str($variable)->trim()->value;
+ $sharedEnvsFound = str($environment_variable)->matchAll('/{{(.*?)}}/');
+ if ($sharedEnvsFound->isEmpty()) {
+ return $environment_variable;
+ }
+ foreach ($sharedEnvsFound as $sharedEnv) {
+ $type = str($sharedEnv)->match('/(.*?)\./');
if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) {
- return $variable;
+ continue;
}
- if ($type === 'environment') {
+ $variable = str($sharedEnv)->match('/\.(.*)/');
+ if ($type->value() === 'environment') {
$id = $resource->environment->id;
- } elseif ($type === 'project') {
+ } elseif ($type->value() === 'project') {
$id = $resource->environment->project->id;
- } else {
+ } elseif ($type->value() === 'team') {
$id = $resource->team()->id;
}
+ if (is_null($id)) {
+ continue;
+ }
$environment_variable_found = SharedEnvironmentVariable::where('type', $type)->where('key', $variable)->where('team_id', $resource->team()->id)->where("{$type}_id", $id)->first();
if ($environment_variable_found) {
- return $environment_variable_found;
+ $environment_variable = str($environment_variable)->replace("{{{$sharedEnv}}}", $environment_variable_found->value);
}
}
- return $environment_variable;
+ return str($environment_variable)->value();
}
private function get_environment_variables(?string $environment_variable = null): ?string
diff --git a/app/Models/Project.php b/app/Models/Project.php
index d4310e349..77f62d770 100644
--- a/app/Models/Project.php
+++ b/app/Models/Project.php
@@ -122,7 +122,7 @@ class Project extends BaseModel
public function resource_count()
{
- return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count() + $this->services()->count() + $this->clickhouses()->count();
+ return $this->applications()->count() + $this->postgresqls()->count() + $this->redis()->count() + $this->mongodbs()->count() + $this->mysqls()->count() + $this->mariadbs()->count() + $this->keydbs()->count() + $this->dragonflies()->count() + $this->clickhouses()->count() + $this->services()->count();
}
public function databases()
diff --git a/app/Models/S3Storage.php b/app/Models/S3Storage.php
index 278ee5995..4c7faaa6f 100644
--- a/app/Models/S3Storage.php
+++ b/app/Models/S3Storage.php
@@ -50,7 +50,7 @@ class S3Storage extends BaseModel
} catch (\Throwable $e) {
$this->is_usable = false;
if ($this->unusable_email_sent === false && is_transactional_emails_active()) {
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject('Coolify: S3 Storage Connection Error');
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('storage.show', ['storage_uuid' => $this->uuid])]);
$users = collect([]);
diff --git a/app/Models/Server.php b/app/Models/Server.php
index e164f2e27..9166f5a0f 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -19,85 +19,23 @@ use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml;
#[OA\Schema(
- description: 'Application model',
+ description: 'Server model',
type: 'object',
properties: [
'id' => ['type' => 'integer'],
- 'repository_project_id' => ['type' => 'integer', 'nullable' => true],
'uuid' => ['type' => 'string'],
'name' => ['type' => 'string'],
- 'fqdn' => ['type' => 'string'],
- 'config_hash' => ['type' => 'string'],
- 'git_repository' => ['type' => 'string'],
- 'git_branch' => ['type' => 'string'],
- 'git_commit_sha' => ['type' => 'string'],
- 'git_full_url' => ['type' => 'string', 'nullable' => true],
- 'docker_registry_image_name' => ['type' => 'string', 'nullable' => true],
- 'docker_registry_image_tag' => ['type' => 'string', 'nullable' => true],
- 'build_pack' => ['type' => 'string'],
- 'static_image' => ['type' => 'string'],
- 'install_command' => ['type' => 'string'],
- 'build_command' => ['type' => 'string'],
- 'start_command' => ['type' => 'string'],
- 'ports_exposes' => ['type' => 'string'],
- 'ports_mappings' => ['type' => 'string', 'nullable' => true],
- 'base_directory' => ['type' => 'string'],
- 'publish_directory' => ['type' => 'string'],
- 'health_check_path' => ['type' => 'string'],
- 'health_check_port' => ['type' => 'string', 'nullable' => true],
- 'health_check_host' => ['type' => 'string'],
- 'health_check_method' => ['type' => 'string'],
- 'health_check_return_code' => ['type' => 'integer'],
- 'health_check_scheme' => ['type' => 'string'],
- 'health_check_response_text' => ['type' => 'string', 'nullable' => true],
- 'health_check_interval' => ['type' => 'integer'],
- 'health_check_timeout' => ['type' => 'integer'],
- 'health_check_retries' => ['type' => 'integer'],
- 'health_check_start_period' => ['type' => 'integer'],
- 'limits_memory' => ['type' => 'string'],
- 'limits_memory_swap' => ['type' => 'string'],
- 'limits_memory_swappiness' => ['type' => 'integer'],
- 'limits_memory_reservation' => ['type' => 'string'],
- 'limits_cpus' => ['type' => 'string'],
- 'limits_cpuset' => ['type' => 'string', 'nullable' => true],
- 'limits_cpu_shares' => ['type' => 'integer'],
- 'status' => ['type' => 'string'],
- 'preview_url_template' => ['type' => 'string'],
- 'destination_type' => ['type' => 'string'],
- 'destination_id' => ['type' => 'integer'],
- 'source_type' => ['type' => 'string'],
- 'source_id' => ['type' => 'integer'],
- 'private_key_id' => ['type' => 'integer', 'nullable' => true],
- 'environment_id' => ['type' => 'integer'],
- 'created_at' => ['type' => 'string', 'format' => 'date-time'],
- 'updated_at' => ['type' => 'string', 'format' => 'date-time'],
- 'description' => ['type' => 'string', 'nullable' => true],
- 'dockerfile' => ['type' => 'string', 'nullable' => true],
- 'health_check_enabled' => ['type' => 'boolean'],
- 'dockerfile_location' => ['type' => 'string'],
- 'custom_labels' => ['type' => 'string'],
- 'dockerfile_target_build' => ['type' => 'string', 'nullable' => true],
- 'manual_webhook_secret_github' => ['type' => 'string', 'nullable' => true],
- 'manual_webhook_secret_gitlab' => ['type' => 'string', 'nullable' => true],
- 'docker_compose_location' => ['type' => 'string'],
- 'docker_compose' => ['type' => 'string', 'nullable' => true],
- 'docker_compose_raw' => ['type' => 'string', 'nullable' => true],
- 'docker_compose_domains' => ['type' => 'string', 'nullable' => true],
- 'deleted_at' => ['type' => 'string', 'format' => 'date-time', 'nullable' => true],
- 'docker_compose_custom_start_command' => ['type' => 'string', 'nullable' => true],
- 'docker_compose_custom_build_command' => ['type' => 'string', 'nullable' => true],
- 'swarm_replicas' => ['type' => 'integer'],
- 'swarm_placement_constraints' => ['type' => 'string', 'nullable' => true],
- 'manual_webhook_secret_bitbucket' => ['type' => 'string', 'nullable' => true],
- 'custom_docker_run_options' => ['type' => 'string', 'nullable' => true],
- 'post_deployment_command' => ['type' => 'string', 'nullable' => true],
- 'post_deployment_command_container' => ['type' => 'string', 'nullable' => true],
- 'pre_deployment_command' => ['type' => 'string', 'nullable' => true],
- 'pre_deployment_command_container' => ['type' => 'string', 'nullable' => true],
- 'watch_paths' => ['type' => 'string', 'nullable' => true],
- 'custom_healthcheck_found' => ['type' => 'boolean'],
- 'manual_webhook_secret_gitea' => ['type' => 'string', 'nullable' => true],
- 'redirect' => ['type' => 'string'],
+ 'description' => ['type' => 'string'],
+ 'ip' => ['type' => 'string'],
+ 'user' => ['type' => 'string'],
+ 'port' => ['type' => 'integer'],
+ 'proxy' => ['type' => 'object'],
+ 'high_disk_usage_notification_sent' => ['type' => 'boolean'],
+ 'unreachable_notification_sent' => ['type' => 'boolean'],
+ 'unreachable_count' => ['type' => 'integer'],
+ 'validation_logs' => ['type' => 'string'],
+ 'log_drain_notification_sent' => ['type' => 'boolean'],
+ 'swarm_cluster' => ['type' => 'string'],
]
)]
@@ -123,6 +61,37 @@ class Server extends BaseModel
ServerSetting::create([
'server_id' => $server->id,
]);
+ if ($server->id === 0) {
+ if ($server->isSwarm()) {
+ SwarmDocker::create([
+ 'id' => 0,
+ 'name' => 'coolify',
+ 'network' => 'coolify-overlay',
+ 'server_id' => $server->id,
+ ]);
+ } else {
+ StandaloneDocker::create([
+ 'id' => 0,
+ 'name' => 'coolify',
+ 'network' => 'coolify',
+ 'server_id' => $server->id,
+ ]);
+ }
+ } else {
+ if ($server->isSwarm()) {
+ SwarmDocker::create([
+ 'name' => 'coolify-overlay',
+ 'network' => 'coolify-overlay',
+ 'server_id' => $server->id,
+ ]);
+ } else {
+ StandaloneDocker::create([
+ 'name' => 'coolify',
+ 'network' => 'coolify',
+ 'server_id' => $server->id,
+ ]);
+ }
+ }
});
static::deleting(function ($server) {
$server->destinations()->each(function ($destination) {
@@ -176,41 +145,6 @@ class Server extends BaseModel
return $this->hasOne(ServerSetting::class);
}
- public function addInitialNetwork()
- {
- if ($this->id === 0) {
- if ($this->isSwarm()) {
- SwarmDocker::create([
- 'id' => 0,
- 'name' => 'coolify',
- 'network' => 'coolify-overlay',
- 'server_id' => $this->id,
- ]);
- } else {
- StandaloneDocker::create([
- 'id' => 0,
- 'name' => 'coolify',
- 'network' => 'coolify',
- 'server_id' => $this->id,
- ]);
- }
- } else {
- if ($this->isSwarm()) {
- SwarmDocker::create([
- 'name' => 'coolify-overlay',
- 'network' => 'coolify-overlay',
- 'server_id' => $this->id,
- ]);
- } else {
- StandaloneDocker::create([
- 'name' => 'coolify',
- 'network' => 'coolify',
- 'server_id' => $this->id,
- ]);
- }
- }
- }
-
public function setupDefault404Redirect()
{
$dynamic_conf_path = $this->proxyPath().'/dynamic';
diff --git a/app/Models/Service.php b/app/Models/Service.php
index 8336b90c8..3ab178046 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use OpenApi\Attributes as OA;
+use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml;
#[OA\Schema(
@@ -575,6 +576,30 @@ class Service extends BaseModel
$fields->put('Vaultwarden', $data);
break;
+ case str($image)->contains('gitlab/gitlab'):
+ $password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_GITLAB')->first();
+ $data = collect([]);
+ if ($password) {
+ $data = $data->merge([
+ 'Root Password' => [
+ 'key' => data_get($password, 'key'),
+ 'value' => data_get($password, 'value'),
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ ]);
+ }
+ $data = $data->merge([
+ 'Root User' => [
+ 'key' => 'N/A',
+ 'value' => 'root',
+ 'rules' => 'required',
+ 'isPassword' => true,
+ ],
+ ]);
+
+ $fields->put('GitLab', $data->toArray());
+ break;
}
}
$databases = $this->databases()->get();
@@ -764,12 +789,24 @@ class Service extends BaseModel
public function failedTaskLink($task_uuid)
{
if (data_get($this, 'environment.project.uuid')) {
- return route('project.service.scheduled-tasks', [
+ $route = route('project.service.scheduled-tasks', [
'project_uuid' => data_get($this, 'environment.project.uuid'),
'environment_name' => data_get($this, 'environment.name'),
'service_uuid' => data_get($this, 'uuid'),
'task_uuid' => $task_uuid,
]);
+ $settings = InstanceSettings::get();
+ if (data_get($settings, 'fqdn')) {
+ $url = Url::fromString($route);
+ $url = $url->withPort(null);
+ $fqdn = data_get($settings, 'fqdn');
+ $fqdn = str_replace(['http://', 'https://'], '', $fqdn);
+ $url = $url->withHost($fqdn);
+
+ return $url->__toString();
+ }
+
+ return $route;
}
return null;
diff --git a/app/Models/User.php b/app/Models/User.php
index 18d15c0e0..ecc4ef6b6 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -120,7 +120,7 @@ class User extends Authenticatable implements SendsEmail
public function sendVerificationEmail()
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$url = Url::temporarySignedRoute(
'verify.verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
@@ -180,6 +180,10 @@ class User extends Authenticatable implements SendsEmail
{
$found_root_team = auth()->user()->teams->filter(function ($team) {
if ($team->id == 0) {
+ if (! auth()->user()->isAdmin()) {
+ return false;
+ }
+
return true;
}
diff --git a/app/Notifications/Application/DeploymentFailed.php b/app/Notifications/Application/DeploymentFailed.php
index a95629087..1809da368 100644
--- a/app/Notifications/Application/DeploymentFailed.php
+++ b/app/Notifications/Application/DeploymentFailed.php
@@ -53,7 +53,7 @@ class DeploymentFailed extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
$fqdn = $this->fqdn;
if ($pull_request_id === 0) {
diff --git a/app/Notifications/Application/DeploymentSuccess.php b/app/Notifications/Application/DeploymentSuccess.php
index c06d070d8..5085065c2 100644
--- a/app/Notifications/Application/DeploymentSuccess.php
+++ b/app/Notifications/Application/DeploymentSuccess.php
@@ -59,7 +59,7 @@ class DeploymentSuccess extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$pull_request_id = data_get($this->preview, 'pull_request_id', 0);
$fqdn = $this->fqdn;
if ($pull_request_id === 0) {
diff --git a/app/Notifications/Application/StatusChanged.php b/app/Notifications/Application/StatusChanged.php
index 72442fcb3..53ed8a589 100644
--- a/app/Notifications/Application/StatusChanged.php
+++ b/app/Notifications/Application/StatusChanged.php
@@ -43,7 +43,7 @@ class StatusChanged extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$fqdn = $this->fqdn;
$mail->subject("Coolify: {$this->resource_name} has been stopped");
$mail->view('emails.application-status-changes', [
diff --git a/app/Notifications/Container/ContainerRestarted.php b/app/Notifications/Container/ContainerRestarted.php
index 86c1e6e69..23f6de264 100644
--- a/app/Notifications/Container/ContainerRestarted.php
+++ b/app/Notifications/Container/ContainerRestarted.php
@@ -23,7 +23,7 @@ class ContainerRestarted extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}");
$mail->view('emails.container-restarted', [
'containerName' => $this->name,
diff --git a/app/Notifications/Container/ContainerStopped.php b/app/Notifications/Container/ContainerStopped.php
index 75b4872cb..bcf5e67a5 100644
--- a/app/Notifications/Container/ContainerStopped.php
+++ b/app/Notifications/Container/ContainerStopped.php
@@ -23,7 +23,7 @@ class ContainerStopped extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: A resource has been stopped unexpectedly on {$this->server->name}");
$mail->view('emails.container-stopped', [
'containerName' => $this->name,
diff --git a/app/Notifications/Database/BackupFailed.php b/app/Notifications/Database/BackupFailed.php
index c6403ab71..77024c05b 100644
--- a/app/Notifications/Database/BackupFailed.php
+++ b/app/Notifications/Database/BackupFailed.php
@@ -33,7 +33,7 @@ class BackupFailed extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: [ACTION REQUIRED] Backup FAILED for {$this->database->name}");
$mail->view('emails.backup-failed', [
'name' => $this->name,
diff --git a/app/Notifications/Database/BackupSuccess.php b/app/Notifications/Database/BackupSuccess.php
index f3a3d5943..f8dc6eb56 100644
--- a/app/Notifications/Database/BackupSuccess.php
+++ b/app/Notifications/Database/BackupSuccess.php
@@ -33,7 +33,7 @@ class BackupSuccess extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: Backup successfully done for {$this->database->name}");
$mail->view('emails.backup-success', [
'name' => $this->name,
diff --git a/app/Notifications/Database/DailyBackup.php b/app/Notifications/Database/DailyBackup.php
index 90abee8a6..a51ac6283 100644
--- a/app/Notifications/Database/DailyBackup.php
+++ b/app/Notifications/Database/DailyBackup.php
@@ -25,7 +25,7 @@ class DailyBackup extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject('Coolify: Daily backup statuses');
$mail->view('emails.daily-backup', [
'databases' => $this->databases,
diff --git a/app/Notifications/ScheduledTask/TaskFailed.php b/app/Notifications/ScheduledTask/TaskFailed.php
index 3a41fb687..479cc1aa1 100644
--- a/app/Notifications/ScheduledTask/TaskFailed.php
+++ b/app/Notifications/ScheduledTask/TaskFailed.php
@@ -35,7 +35,7 @@ class TaskFailed extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: [ACTION REQUIRED] Scheduled task ({$this->task->name}) failed.");
$mail->view('emails.scheduled-task-failed', [
'task' => $this->task,
diff --git a/app/Notifications/Server/ForceDisabled.php b/app/Notifications/Server/ForceDisabled.php
index 9a76558e2..c0e2a3c31 100644
--- a/app/Notifications/Server/ForceDisabled.php
+++ b/app/Notifications/Server/ForceDisabled.php
@@ -41,7 +41,7 @@ class ForceDisabled extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: Server ({$this->server->name}) disabled because it is not paid!");
$mail->view('emails.server-force-disabled', [
'name' => $this->server->name,
diff --git a/app/Notifications/Server/ForceEnabled.php b/app/Notifications/Server/ForceEnabled.php
index a43e30376..83594d643 100644
--- a/app/Notifications/Server/ForceEnabled.php
+++ b/app/Notifications/Server/ForceEnabled.php
@@ -41,7 +41,7 @@ class ForceEnabled extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: Server ({$this->server->name}) enabled again!");
$mail->view('emails.server-force-enabled', [
'name' => $this->server->name,
diff --git a/app/Notifications/Server/HighDiskUsage.php b/app/Notifications/Server/HighDiskUsage.php
index a6e248170..3c68afe7b 100644
--- a/app/Notifications/Server/HighDiskUsage.php
+++ b/app/Notifications/Server/HighDiskUsage.php
@@ -41,7 +41,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: Server ({$this->server->name}) high disk usage detected!");
$mail->view('emails.high-disk-usage', [
'name' => $this->server->name,
diff --git a/app/Notifications/Server/Revived.php b/app/Notifications/Server/Revived.php
index 8eaadf359..e05e13e9b 100644
--- a/app/Notifications/Server/Revived.php
+++ b/app/Notifications/Server/Revived.php
@@ -50,7 +50,7 @@ class Revived extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: Server ({$this->server->name}) revived.");
$mail->view('emails.server-revived', [
'name' => $this->server->name,
diff --git a/app/Notifications/Server/Unreachable.php b/app/Notifications/Server/Unreachable.php
index ebbd6af77..f178c9be3 100644
--- a/app/Notifications/Server/Unreachable.php
+++ b/app/Notifications/Server/Unreachable.php
@@ -41,7 +41,7 @@ class Unreachable extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject("Coolify: Your server ({$this->server->name}) is unreachable.");
$mail->view('emails.server-lost-connection', [
'name' => $this->server->name,
diff --git a/app/Notifications/Test.php b/app/Notifications/Test.php
index f873a95d3..3b46a9a24 100644
--- a/app/Notifications/Test.php
+++ b/app/Notifications/Test.php
@@ -22,7 +22,7 @@ class Test extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject('Coolify: Test Email');
$mail->view('emails.test');
diff --git a/app/Notifications/TransactionalEmails/InvitationLink.php b/app/Notifications/TransactionalEmails/InvitationLink.php
index 49d2ad487..6da2a6fcc 100644
--- a/app/Notifications/TransactionalEmails/InvitationLink.php
+++ b/app/Notifications/TransactionalEmails/InvitationLink.php
@@ -29,7 +29,7 @@ class InvitationLink extends Notification implements ShouldQueue
$invitation = TeamInvitation::whereEmail($this->user->email)->first();
$invitation_team = Team::find($invitation->team->id);
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject('Coolify: Invitation for '.$invitation_team->name);
$mail->view('emails.invitation-link', [
'team' => $invitation_team->name,
diff --git a/app/Notifications/TransactionalEmails/ResetPassword.php b/app/Notifications/TransactionalEmails/ResetPassword.php
index 8988a4342..8b1c02d39 100644
--- a/app/Notifications/TransactionalEmails/ResetPassword.php
+++ b/app/Notifications/TransactionalEmails/ResetPassword.php
@@ -53,7 +53,7 @@ class ResetPassword extends Notification
protected function buildMailMessage($url)
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject('Coolify: Reset Password');
$mail->view('emails.reset-password', ['url' => $url, 'count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]);
diff --git a/app/Notifications/TransactionalEmails/Test.php b/app/Notifications/TransactionalEmails/Test.php
index a417e1ee5..64883a58e 100644
--- a/app/Notifications/TransactionalEmails/Test.php
+++ b/app/Notifications/TransactionalEmails/Test.php
@@ -23,7 +23,7 @@ class Test extends Notification implements ShouldQueue
public function toMail(): MailMessage
{
- $mail = new MailMessage();
+ $mail = new MailMessage;
$mail->subject('Coolify: Test Email');
$mail->view('emails.test');
diff --git a/app/View/Components/Forms/Datalist.php b/app/View/Components/Forms/Datalist.php
index df0c1cb11..25643753d 100644
--- a/app/View/Components/Forms/Datalist.php
+++ b/app/View/Components/Forms/Datalist.php
@@ -30,7 +30,7 @@ class Datalist extends Component
public function render(): View|Closure|string
{
if (is_null($this->id)) {
- $this->id = new Cuid2(7);
+ $this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;
diff --git a/app/View/Components/Forms/Input.php b/app/View/Components/Forms/Input.php
index 35448d5e5..6c9378cac 100644
--- a/app/View/Components/Forms/Input.php
+++ b/app/View/Components/Forms/Input.php
@@ -27,7 +27,7 @@ class Input extends Component
public function render(): View|Closure|string
{
if (is_null($this->id)) {
- $this->id = new Cuid2(7);
+ $this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;
diff --git a/app/View/Components/Forms/Select.php b/app/View/Components/Forms/Select.php
index 21c147c2b..dd5ba66b7 100644
--- a/app/View/Components/Forms/Select.php
+++ b/app/View/Components/Forms/Select.php
@@ -30,7 +30,7 @@ class Select extends Component
public function render(): View|Closure|string
{
if (is_null($this->id)) {
- $this->id = new Cuid2(7);
+ $this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;
diff --git a/app/View/Components/Forms/Textarea.php b/app/View/Components/Forms/Textarea.php
index 7d1860500..3f887877c 100644
--- a/app/View/Components/Forms/Textarea.php
+++ b/app/View/Components/Forms/Textarea.php
@@ -41,7 +41,7 @@ class Textarea extends Component
public function render(): View|Closure|string
{
if (is_null($this->id)) {
- $this->id = new Cuid2(7);
+ $this->id = new Cuid2;
}
if (is_null($this->name)) {
$this->name = $this->id;
diff --git a/bootstrap/helpers/databases.php b/bootstrap/helpers/databases.php
index 089298956..b8dcc1f3c 100644
--- a/bootstrap/helpers/databases.php
+++ b/bootstrap/helpers/databases.php
@@ -14,7 +14,7 @@ use Visus\Cuid2\Cuid2;
function generate_database_name(string $type): string
{
- $cuid = new Cuid2(7);
+ $cuid = new Cuid2;
return $type.'-database-'.$cuid;
}
@@ -25,7 +25,7 @@ function create_standalone_postgresql($environmentId, $destinationUuid, ?array $
if (! $destination) {
throw new Exception('Destination not found');
}
- $database = new StandalonePostgresql();
+ $database = new StandalonePostgresql;
$database->name = generate_database_name('postgresql');
$database->postgres_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->environment_id = $environmentId;
@@ -45,7 +45,7 @@ function create_standalone_redis($environment_id, $destination_uuid, ?array $oth
if (! $destination) {
throw new Exception('Destination not found');
}
- $database = new StandaloneRedis();
+ $database = new StandaloneRedis;
$database->name = generate_database_name('redis');
$database->redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->environment_id = $environment_id;
@@ -65,7 +65,7 @@ function create_standalone_mongodb($environment_id, $destination_uuid, ?array $o
if (! $destination) {
throw new Exception('Destination not found');
}
- $database = new StandaloneMongodb();
+ $database = new StandaloneMongodb;
$database->name = generate_database_name('mongodb');
$database->mongo_initdb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->environment_id = $environment_id;
@@ -84,7 +84,7 @@ function create_standalone_mysql($environment_id, $destination_uuid, ?array $oth
if (! $destination) {
throw new Exception('Destination not found');
}
- $database = new StandaloneMysql();
+ $database = new StandaloneMysql;
$database->name = generate_database_name('mysql');
$database->mysql_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->mysql_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -104,7 +104,7 @@ function create_standalone_mariadb($environment_id, $destination_uuid, ?array $o
if (! $destination) {
throw new Exception('Destination not found');
}
- $database = new StandaloneMariadb();
+ $database = new StandaloneMariadb;
$database->name = generate_database_name('mariadb');
$database->mariadb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->mariadb_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
@@ -125,7 +125,7 @@ function create_standalone_keydb($environment_id, $destination_uuid, ?array $oth
if (! $destination) {
throw new Exception('Destination not found');
}
- $database = new StandaloneKeydb();
+ $database = new StandaloneKeydb;
$database->name = generate_database_name('keydb');
$database->keydb_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->environment_id = $environment_id;
@@ -145,7 +145,7 @@ function create_standalone_dragonfly($environment_id, $destination_uuid, ?array
if (! $destination) {
throw new Exception('Destination not found');
}
- $database = new StandaloneDragonfly();
+ $database = new StandaloneDragonfly;
$database->name = generate_database_name('dragonfly');
$database->dragonfly_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->environment_id = $environment_id;
@@ -164,7 +164,7 @@ function create_standalone_clickhouse($environment_id, $destination_uuid, ?array
if (! $destination) {
throw new Exception('Destination not found');
}
- $database = new StandaloneClickhouse();
+ $database = new StandaloneClickhouse;
$database->name = generate_database_name('clickhouse');
$database->clickhouse_admin_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
$database->environment_id = $environment_id;
diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php
index 0aa5c6b74..f32100f88 100644
--- a/bootstrap/helpers/docker.php
+++ b/bootstrap/helpers/docker.php
@@ -338,7 +338,7 @@ function fqdnLabelsForTraefik(string $uuid, Collection $domains, bool $is_force_
foreach ($domains as $loop => $domain) {
try {
if ($generate_unique_uuid) {
- $uuid = new Cuid2(7);
+ $uuid = new Cuid2;
}
$url = Url::fromString($domain);
diff --git a/bootstrap/helpers/github.php b/bootstrap/helpers/github.php
index 98e405c74..97deb0b1c 100644
--- a/bootstrap/helpers/github.php
+++ b/bootstrap/helpers/github.php
@@ -14,9 +14,9 @@ use Lcobucci\JWT\Token\Builder;
function generate_github_installation_token(GithubApp $source)
{
$signingKey = InMemory::plainText($source->privateKey->private_key);
- $algorithm = new Sha256();
- $tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
- $now = new DateTimeImmutable();
+ $algorithm = new Sha256;
+ $tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
+ $now = new DateTimeImmutable;
$now = $now->setTime($now->format('H'), $now->format('i'));
$issuedToken = $tokenBuilder
->issuedBy($source->app_id)
@@ -38,9 +38,9 @@ function generate_github_installation_token(GithubApp $source)
function generate_github_jwt_token(GithubApp $source)
{
$signingKey = InMemory::plainText($source->privateKey->private_key);
- $algorithm = new Sha256();
- $tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
- $now = new DateTimeImmutable();
+ $algorithm = new Sha256;
+ $tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
+ $now = new DateTimeImmutable;
$now = $now->setTime($now->format('H'), $now->format('i'));
$issuedToken = $tokenBuilder
->issuedBy($source->app_id)
diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php
index c33b7eb34..d4e9ebcde 100644
--- a/bootstrap/helpers/services.php
+++ b/bootstrap/helpers/services.php
@@ -116,7 +116,7 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
if ($resourceFqdns->count() === 1) {
$resourceFqdns = $resourceFqdns->first();
$variableName = 'SERVICE_FQDN_'.str($resource->name)->upper()->replace('-', '');
- $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'LIKE', "{$variableName}_%")->first();
+ $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$fqdn = Url::fromString($resourceFqdns);
$port = $fqdn->getPort();
$path = $fqdn->getPath();
@@ -128,14 +128,13 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
if ($port) {
$variableName = $variableName."_$port";
$generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
- // ray($generatedEnv);
if ($generatedEnv) {
$generatedEnv->value = $fqdn.$path;
$generatedEnv->save();
}
}
$variableName = 'SERVICE_URL_'.str($resource->name)->upper()->replace('-', '');
- $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'LIKE', "{$variableName}_%")->first();
+ $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first();
$url = Url::fromString($fqdn);
$port = $url->getPort();
$path = $url->getPath();
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 7ae2b0309..b93d15b9a 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -196,11 +196,11 @@ function generate_random_name(?string $cuid = null): string
{
$generator = new \Nubs\RandomNameGenerator\All(
[
- new \Nubs\RandomNameGenerator\Alliteration(),
+ new \Nubs\RandomNameGenerator\Alliteration,
]
);
if (is_null($cuid)) {
- $cuid = new Cuid2(7);
+ $cuid = new Cuid2;
}
return Str::kebab("{$generator->getName()}-$cuid");
@@ -236,7 +236,7 @@ function formatPrivateKey(string $privateKey)
function generate_application_name(string $git_repository, string $git_branch, ?string $cuid = null): string
{
if (is_null($cuid)) {
- $cuid = new Cuid2(7);
+ $cuid = new Cuid2;
}
return Str::kebab("$git_repository:$git_branch-$cuid");
@@ -977,6 +977,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$target = str($volume)->after(':')->beforeLast(':');
if ($source->startsWith('./') || $source->startsWith('/') || $source->startsWith('~')) {
$type = str('bind');
+ // By default, we cannot determine if the bind is a directory or not, so we set it to directory
+ $isDirectory = true;
} else {
$type = str('volume');
}
@@ -985,14 +987,19 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$source = data_get_str($volume, 'source');
$target = data_get_str($volume, 'target');
$content = data_get($volume, 'content');
- $isDirectory = (bool) data_get($volume, 'isDirectory', false) || (bool) data_get($volume, 'is_directory', false);
+ $isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
$foundConfig = $savedService->fileStorages()->whereMountPath($target)->first();
if ($foundConfig) {
$contentNotNull = data_get($foundConfig, 'content');
if ($contentNotNull) {
$content = $contentNotNull;
}
- $isDirectory = (bool) data_get($volume, 'isDirectory', false) || (bool) data_get($volume, 'is_directory', false);
+ $isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
+ }
+ if (is_null($isDirectory) && is_null($content)) {
+ // if isDirectory is not set & content is also not set, we assume it is a directory
+ ray('setting isDirectory to true');
+ $isDirectory = true;
}
}
if ($type?->value() === 'bind') {
@@ -1058,30 +1065,26 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
data_set($service, 'volumes', $serviceVolumes->toArray());
}
- // Add env_file with at least .env to the service
- // $envFile = collect(data_get($service, 'env_file', []));
- // if ($envFile->count() > 0) {
- // if (!$envFile->contains('.env')) {
- // $envFile->push('.env');
- // }
- // } else {
- // $envFile = collect(['.env']);
- // }
- // data_set($service, 'env_file', $envFile->toArray());
-
// Get variables from the service
foreach ($serviceVariables as $variableName => $variable) {
if (is_numeric($variableName)) {
- $variable = str($variable);
- if ($variable->contains('=')) {
- // - SESSION_SECRET=123
- // - SESSION_SECRET=
- $key = $variable->before('=');
- $value = $variable->after('=');
+ if (is_array($variable)) {
+ // - SESSION_SECRET: 123
+ // - SESSION_SECRET:
+ $key = str(collect($variable)->keys()->first());
+ $value = str(collect($variable)->values()->first());
} else {
- // - SESSION_SECRET
- $key = $variable;
- $value = null;
+ $variable = str($variable);
+ if ($variable->contains('=')) {
+ // - SESSION_SECRET=123
+ // - SESSION_SECRET=
+ $key = $variable->before('=');
+ $value = $variable->after('=');
+ } else {
+ // - SESSION_SECRET
+ $key = $variable;
+ $value = null;
+ }
}
} else {
// SESSION_SECRET: 123
@@ -1837,16 +1840,23 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
// Get variables from the service
foreach ($serviceVariables as $variableName => $variable) {
if (is_numeric($variableName)) {
- $variable = str($variable);
- if ($variable->contains('=')) {
- // - SESSION_SECRET=123
- // - SESSION_SECRET=
- $key = $variable->before('=');
- $value = $variable->after('=');
+ if (is_array($variable)) {
+ // - SESSION_SECRET: 123
+ // - SESSION_SECRET:
+ $key = str(collect($variable)->keys()->first());
+ $value = str(collect($variable)->values()->first());
} else {
- // - SESSION_SECRET
- $key = $variable;
- $value = null;
+ $variable = str($variable);
+ if ($variable->contains('=')) {
+ // - SESSION_SECRET=123
+ // - SESSION_SECRET=
+ $key = $variable->before('=');
+ $value = $variable->after('=');
+ } else {
+ // - SESSION_SECRET
+ $key = $variable;
+ $value = null;
+ }
}
} else {
// SESSION_SECRET: 123
@@ -2012,7 +2022,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$template = $resource->preview_url_template;
$host = $url->getHost();
$schema = $url->getScheme();
- $random = new Cuid2(7);
+ $random = new Cuid2;
$preview_fqdn = str_replace('{{random}}', $random, $template);
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
$preview_fqdn = str_replace('{{pr_id}}', $pull_request_id, $preview_fqdn);
@@ -2188,9 +2198,9 @@ function generateEnvValue(string $command, ?Service $service = null)
$signingKey = $signingKey->value;
}
$key = InMemory::plainText($signingKey);
- $algorithm = new Sha256();
- $tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
- $now = new DateTimeImmutable();
+ $algorithm = new Sha256;
+ $tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
+ $now = new DateTimeImmutable;
$now = $now->setTime($now->format('H'), $now->format('i'));
$token = $tokenBuilder
->issuedBy('supabase')
@@ -2208,9 +2218,9 @@ function generateEnvValue(string $command, ?Service $service = null)
$signingKey = $signingKey->value;
}
$key = InMemory::plainText($signingKey);
- $algorithm = new Sha256();
- $tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
- $now = new DateTimeImmutable();
+ $algorithm = new Sha256;
+ $tokenBuilder = (new Builder(new JoseEncoder, ChainedFormatter::default()));
+ $now = new DateTimeImmutable;
$now = $now->setTime($now->format('H'), $now->format('i'));
$token = $tokenBuilder
->issuedBy('supabase')
diff --git a/config/sentry.php b/config/sentry.php
index 5a8a7c08e..b2f6ded80 100644
--- a/config/sentry.php
+++ b/config/sentry.php
@@ -7,7 +7,7 @@ return [
// The release version of your application
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
- 'release' => '4.0.0-beta.318',
+ 'release' => '4.0.0-beta.319',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),
diff --git a/config/version.php b/config/version.php
index 4c8bc6513..8b5a5afc6 100644
--- a/config/version.php
+++ b/config/version.php
@@ -1,3 +1,3 @@
each(function (EnvironmentVariable $environmentVariable) {
$environmentVariable->update([
- 'uuid' => (string) new Cuid2(),
+ 'uuid' => (string) new Cuid2,
]);
});
Schema::table('environment_variables', function (Blueprint $table) {
diff --git a/database/migrations/2024_07_23_112710_add_validation_logs_to_servers.php b/database/migrations/2024_07_23_112710_add_validation_logs_to_servers.php
new file mode 100644
index 000000000..d3293620b
--- /dev/null
+++ b/database/migrations/2024_07_23_112710_add_validation_logs_to_servers.php
@@ -0,0 +1,28 @@
+text('validation_logs')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('servers', function (Blueprint $table) {
+ $table->dropColumn('validation_logs');
+ });
+ }
+};
diff --git a/database/seeders/ProductionSeeder.php b/database/seeders/ProductionSeeder.php
index 5db2e826c..e030c0ae6 100644
--- a/database/seeders/ProductionSeeder.php
+++ b/database/seeders/ProductionSeeder.php
@@ -178,7 +178,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
get_public_ips();
- $oauth_settings_seeder = new OauthSettingSeeder();
+ $oauth_settings_seeder = new OauthSettingSeeder;
$oauth_settings_seeder->run();
}
}
diff --git a/openapi.yaml b/openapi.yaml
index 8b3c170d0..00d7bff43 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -5,6 +5,7 @@ info:
servers:
-
url: 'https://app.coolify.io/api/v1'
+ description: 'Coolify Cloud API. Change the host to your own instance if you are self-hosting.'
paths:
/applications:
get:
@@ -29,225 +30,6 @@ paths:
security:
-
bearerAuth: []
- patch:
- tags:
- - Applications
- summary: Update
- description: 'Update application by UUID.'
- operationId: ff28a22d25b1f658c40b54d2073abbca
- requestBody:
- description: 'Application updated.'
- required: true
- content:
- application/json:
- schema:
- properties:
- project_uuid:
- type: string
- description: 'The project UUID.'
- server_uuid:
- type: string
- description: 'The server UUID.'
- environment_name:
- type: string
- description: 'The environment name.'
- github_app_uuid:
- type: string
- description: 'The Github App UUID.'
- git_repository:
- type: string
- description: 'The git repository URL.'
- git_branch:
- type: string
- description: 'The git branch.'
- ports_exposes:
- type: string
- description: 'The ports to expose.'
- destination_uuid:
- type: string
- description: 'The destination UUID.'
- build_pack:
- type: string
- enum: [nixpacks, static, dockerfile, dockercompose]
- description: 'The build pack type.'
- name:
- type: string
- description: 'The application name.'
- description:
- type: string
- description: 'The application description.'
- domains:
- type: string
- description: 'The application domains.'
- git_commit_sha:
- type: string
- description: 'The git commit SHA.'
- docker_registry_image_name:
- type: string
- description: 'The docker registry image name.'
- docker_registry_image_tag:
- type: string
- description: 'The docker registry image tag.'
- is_static:
- type: boolean
- description: 'The flag to indicate if the application is static.'
- install_command:
- type: string
- description: 'The install command.'
- build_command:
- type: string
- description: 'The build command.'
- start_command:
- type: string
- description: 'The start command.'
- ports_mappings:
- type: string
- description: 'The ports mappings.'
- base_directory:
- type: string
- description: 'The base directory for all commands.'
- publish_directory:
- type: string
- description: 'The publish directory.'
- health_check_enabled:
- type: boolean
- description: 'Health check enabled.'
- health_check_path:
- type: string
- description: 'Health check path.'
- health_check_port:
- type: string
- nullable: true
- description: 'Health check port.'
- health_check_host:
- type: string
- nullable: true
- description: 'Health check host.'
- health_check_method:
- type: string
- description: 'Health check method.'
- health_check_return_code:
- type: integer
- description: 'Health check return code.'
- health_check_scheme:
- type: string
- description: 'Health check scheme.'
- health_check_response_text:
- type: string
- nullable: true
- description: 'Health check response text.'
- health_check_interval:
- type: integer
- description: 'Health check interval in seconds.'
- health_check_timeout:
- type: integer
- description: 'Health check timeout in seconds.'
- health_check_retries:
- type: integer
- description: 'Health check retries count.'
- health_check_start_period:
- type: integer
- description: 'Health check start period in seconds.'
- limits_memory:
- type: string
- description: 'Memory limit.'
- limits_memory_swap:
- type: string
- description: 'Memory swap limit.'
- limits_memory_swappiness:
- type: integer
- description: 'Memory swappiness.'
- limits_memory_reservation:
- type: string
- description: 'Memory reservation.'
- limits_cpus:
- type: string
- description: 'CPU limit.'
- limits_cpuset:
- type: string
- nullable: true
- description: 'CPU set.'
- limits_cpu_shares:
- type: integer
- description: 'CPU shares.'
- custom_labels:
- type: string
- description: 'Custom labels.'
- custom_docker_run_options:
- type: string
- description: 'Custom docker run options.'
- post_deployment_command:
- type: string
- description: 'Post deployment command.'
- post_deployment_command_container:
- type: string
- description: 'Post deployment command container.'
- pre_deployment_command:
- type: string
- description: 'Pre deployment command.'
- pre_deployment_command_container:
- type: string
- description: 'Pre deployment command container.'
- manual_webhook_secret_github:
- type: string
- description: 'Manual webhook secret for Github.'
- manual_webhook_secret_gitlab:
- type: string
- description: 'Manual webhook secret for Gitlab.'
- manual_webhook_secret_bitbucket:
- type: string
- description: 'Manual webhook secret for Bitbucket.'
- manual_webhook_secret_gitea:
- type: string
- description: 'Manual webhook secret for Gitea.'
- redirect:
- type: string
- nullable: true
- description: 'How to set redirect with Traefik / Caddy. www<->non-www.'
- enum: [www, non-www, both]
- instant_deploy:
- type: boolean
- description: 'The flag to indicate if the application should be deployed instantly.'
- dockerfile:
- type: string
- description: 'The Dockerfile content.'
- docker_compose_location:
- type: string
- description: 'The Docker Compose location.'
- docker_compose_raw:
- type: string
- description: 'The Docker Compose raw content.'
- docker_compose_custom_start_command:
- type: string
- description: 'The Docker Compose custom start command.'
- docker_compose_custom_build_command:
- type: string
- description: 'The Docker Compose custom build command.'
- docker_compose_domains:
- type: array
- description: 'The Docker Compose domains.'
- watch_paths:
- type: string
- description: 'The watch paths.'
- type: object
- responses:
- '200':
- description: 'Application updated.'
- content:
- application/json:
- schema:
- properties:
- uuid: { type: string }
- type: object
- '401':
- $ref: '#/components/responses/401'
- '400':
- $ref: '#/components/responses/400'
- '404':
- $ref: '#/components/responses/404'
- security:
- -
- bearerAuth: []
/applications/public:
post:
tags:
@@ -1369,6 +1151,225 @@ paths:
security:
-
bearerAuth: []
+ patch:
+ tags:
+ - Applications
+ summary: Update
+ description: 'Update application by UUID.'
+ operationId: 62a3b1775e8cba5d39a236ebb69830b7
+ requestBody:
+ description: 'Application updated.'
+ required: true
+ content:
+ application/json:
+ schema:
+ properties:
+ project_uuid:
+ type: string
+ description: 'The project UUID.'
+ server_uuid:
+ type: string
+ description: 'The server UUID.'
+ environment_name:
+ type: string
+ description: 'The environment name.'
+ github_app_uuid:
+ type: string
+ description: 'The Github App UUID.'
+ git_repository:
+ type: string
+ description: 'The git repository URL.'
+ git_branch:
+ type: string
+ description: 'The git branch.'
+ ports_exposes:
+ type: string
+ description: 'The ports to expose.'
+ destination_uuid:
+ type: string
+ description: 'The destination UUID.'
+ build_pack:
+ type: string
+ enum: [nixpacks, static, dockerfile, dockercompose]
+ description: 'The build pack type.'
+ name:
+ type: string
+ description: 'The application name.'
+ description:
+ type: string
+ description: 'The application description.'
+ domains:
+ type: string
+ description: 'The application domains.'
+ git_commit_sha:
+ type: string
+ description: 'The git commit SHA.'
+ docker_registry_image_name:
+ type: string
+ description: 'The docker registry image name.'
+ docker_registry_image_tag:
+ type: string
+ description: 'The docker registry image tag.'
+ is_static:
+ type: boolean
+ description: 'The flag to indicate if the application is static.'
+ install_command:
+ type: string
+ description: 'The install command.'
+ build_command:
+ type: string
+ description: 'The build command.'
+ start_command:
+ type: string
+ description: 'The start command.'
+ ports_mappings:
+ type: string
+ description: 'The ports mappings.'
+ base_directory:
+ type: string
+ description: 'The base directory for all commands.'
+ publish_directory:
+ type: string
+ description: 'The publish directory.'
+ health_check_enabled:
+ type: boolean
+ description: 'Health check enabled.'
+ health_check_path:
+ type: string
+ description: 'Health check path.'
+ health_check_port:
+ type: string
+ nullable: true
+ description: 'Health check port.'
+ health_check_host:
+ type: string
+ nullable: true
+ description: 'Health check host.'
+ health_check_method:
+ type: string
+ description: 'Health check method.'
+ health_check_return_code:
+ type: integer
+ description: 'Health check return code.'
+ health_check_scheme:
+ type: string
+ description: 'Health check scheme.'
+ health_check_response_text:
+ type: string
+ nullable: true
+ description: 'Health check response text.'
+ health_check_interval:
+ type: integer
+ description: 'Health check interval in seconds.'
+ health_check_timeout:
+ type: integer
+ description: 'Health check timeout in seconds.'
+ health_check_retries:
+ type: integer
+ description: 'Health check retries count.'
+ health_check_start_period:
+ type: integer
+ description: 'Health check start period in seconds.'
+ limits_memory:
+ type: string
+ description: 'Memory limit.'
+ limits_memory_swap:
+ type: string
+ description: 'Memory swap limit.'
+ limits_memory_swappiness:
+ type: integer
+ description: 'Memory swappiness.'
+ limits_memory_reservation:
+ type: string
+ description: 'Memory reservation.'
+ limits_cpus:
+ type: string
+ description: 'CPU limit.'
+ limits_cpuset:
+ type: string
+ nullable: true
+ description: 'CPU set.'
+ limits_cpu_shares:
+ type: integer
+ description: 'CPU shares.'
+ custom_labels:
+ type: string
+ description: 'Custom labels.'
+ custom_docker_run_options:
+ type: string
+ description: 'Custom docker run options.'
+ post_deployment_command:
+ type: string
+ description: 'Post deployment command.'
+ post_deployment_command_container:
+ type: string
+ description: 'Post deployment command container.'
+ pre_deployment_command:
+ type: string
+ description: 'Pre deployment command.'
+ pre_deployment_command_container:
+ type: string
+ description: 'Pre deployment command container.'
+ manual_webhook_secret_github:
+ type: string
+ description: 'Manual webhook secret for Github.'
+ manual_webhook_secret_gitlab:
+ type: string
+ description: 'Manual webhook secret for Gitlab.'
+ manual_webhook_secret_bitbucket:
+ type: string
+ description: 'Manual webhook secret for Bitbucket.'
+ manual_webhook_secret_gitea:
+ type: string
+ description: 'Manual webhook secret for Gitea.'
+ redirect:
+ type: string
+ nullable: true
+ description: 'How to set redirect with Traefik / Caddy. www<->non-www.'
+ enum: [www, non-www, both]
+ instant_deploy:
+ type: boolean
+ description: 'The flag to indicate if the application should be deployed instantly.'
+ dockerfile:
+ type: string
+ description: 'The Dockerfile content.'
+ docker_compose_location:
+ type: string
+ description: 'The Docker Compose location.'
+ docker_compose_raw:
+ type: string
+ description: 'The Docker Compose raw content.'
+ docker_compose_custom_start_command:
+ type: string
+ description: 'The Docker Compose custom start command.'
+ docker_compose_custom_build_command:
+ type: string
+ description: 'The Docker Compose custom build command.'
+ docker_compose_domains:
+ type: array
+ description: 'The Docker Compose domains.'
+ watch_paths:
+ type: string
+ description: 'The watch paths.'
+ type: object
+ responses:
+ '200':
+ description: 'Application updated.'
+ content:
+ application/json:
+ schema:
+ properties:
+ uuid: { type: string }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
'/applications/{uuid}/envs':
get:
tags:
@@ -2833,7 +2834,7 @@ paths:
description: 'Deployment Uuid'
required: true
schema:
- type: integer
+ type: string
responses:
'200':
description: 'Get deployment by UUID.'
@@ -3010,6 +3011,44 @@ paths:
security:
-
bearerAuth: []
+ post:
+ tags:
+ - Projects
+ summary: Create
+ description: 'Create Project.'
+ operationId: cf067eb7cf18216cda3239329a2eeadb
+ requestBody:
+ description: 'Project created.'
+ required: true
+ content:
+ application/json:
+ schema:
+ properties:
+ uuid:
+ type: string
+ description: 'The name of the project.'
+ description:
+ type: string
+ description: 'The description of the project.'
+ type: object
+ responses:
+ '201':
+ description: 'Project created.'
+ content:
+ application/json:
+ schema:
+ properties:
+ uuid: { type: string, example: og888os, description: 'The UUID of the project.' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
'/projects/{uuid}':
get:
tags:
@@ -3024,7 +3063,7 @@ paths:
description: 'Project UUID'
required: true
schema:
- type: integer
+ type: string
responses:
'200':
description: 'Project details'
@@ -3041,6 +3080,79 @@ paths:
security:
-
bearerAuth: []
+ delete:
+ tags:
+ - Projects
+ summary: Delete
+ description: 'Delete project by UUID.'
+ operationId: f668a936f505b4401948c74b6a663029
+ parameters:
+ -
+ name: uuid
+ in: path
+ description: 'UUID of the application.'
+ required: true
+ schema:
+ type: string
+ format: uuid
+ responses:
+ '200':
+ description: 'Project deleted.'
+ content:
+ application/json:
+ schema:
+ properties:
+ message: { type: string, example: 'Project deleted.' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
+ patch:
+ tags:
+ - Projects
+ summary: Update
+ description: 'Update Project.'
+ operationId: 2db343bd6fc14c658cb51a2b73b2f842
+ requestBody:
+ description: 'Project updated.'
+ required: true
+ content:
+ application/json:
+ schema:
+ properties:
+ name:
+ type: string
+ description: 'The name of the project.'
+ description:
+ type: string
+ description: 'The description of the project.'
+ type: object
+ responses:
+ '201':
+ description: 'Project updated.'
+ content:
+ application/json:
+ schema:
+ properties:
+ uuid: { type: string, example: og888os }
+ name: { type: string, example: 'Project Name' }
+ description: { type: string, example: 'Project Description' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
'/projects/{uuid}/{environment_name}':
get:
tags:
@@ -3055,7 +3167,7 @@ paths:
description: 'Project UUID'
required: true
schema:
- type: integer
+ type: string
-
name: environment_name
in: path
@@ -3214,7 +3326,7 @@ paths:
description: 'Private Key Uuid'
required: true
schema:
- type: integer
+ type: string
responses:
'200':
description: 'Get all private keys.'
@@ -3246,7 +3358,7 @@ paths:
description: 'Private Key Uuid'
required: true
schema:
- type: integer
+ type: string
responses:
'200':
description: 'Private Key deleted.'
@@ -3288,6 +3400,70 @@ paths:
security:
-
bearerAuth: []
+ post:
+ tags:
+ - Servers
+ summary: Create
+ description: 'Create Server.'
+ operationId: fa44b42490379e428ba5b8747716a8d9
+ requestBody:
+ description: 'Server created.'
+ required: true
+ content:
+ application/json:
+ schema:
+ properties:
+ name:
+ type: string
+ example: 'My Server'
+ description: 'The name of the server.'
+ description:
+ type: string
+ example: 'My Server Description'
+ description: 'The description of the server.'
+ ip:
+ type: string
+ example: 127.0.0.1
+ description: 'The IP of the server.'
+ port:
+ type: integer
+ example: 22
+ description: 'The port of the server.'
+ user:
+ type: string
+ example: root
+ description: 'The user of the server.'
+ private_key_uuid:
+ type: string
+ example: og888os
+ description: 'The UUID of the private key.'
+ is_build_server:
+ type: boolean
+ example: false
+ description: 'Is build server.'
+ instant_validate:
+ type: boolean
+ example: false
+ description: 'Instant validate.'
+ type: object
+ responses:
+ '201':
+ description: 'Server created.'
+ content:
+ application/json:
+ schema:
+ properties:
+ uuid: { type: string, example: og888os, description: 'The UUID of the server.' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
'/servers/{uuid}':
get:
tags:
@@ -3302,7 +3478,7 @@ paths:
description: "Server's Uuid"
required: true
schema:
- type: integer
+ type: string
responses:
'200':
description: 'Get server by UUID'
@@ -3319,6 +3495,95 @@ paths:
security:
-
bearerAuth: []
+ delete:
+ tags:
+ - Servers
+ summary: Delete
+ description: 'Delete server by UUID.'
+ operationId: 0231fe0134f0306b21f006ce51b0a3dc
+ parameters:
+ -
+ name: uuid
+ in: path
+ description: 'UUID of the server.'
+ required: true
+ schema:
+ type: string
+ format: uuid
+ responses:
+ '200':
+ description: 'Server deleted.'
+ content:
+ application/json:
+ schema:
+ properties:
+ message: { type: string, example: 'Server deleted.' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
+ patch:
+ tags:
+ - Servers
+ summary: Update
+ description: 'Update Server.'
+ operationId: 41bbdaf79eb1938592494fc5494442a0
+ requestBody:
+ description: 'Server updated.'
+ required: true
+ content:
+ application/json:
+ schema:
+ properties:
+ name:
+ type: string
+ description: 'The name of the server.'
+ description:
+ type: string
+ description: 'The description of the server.'
+ ip:
+ type: string
+ description: 'The IP of the server.'
+ port:
+ type: integer
+ description: 'The port of the server.'
+ user:
+ type: string
+ description: 'The user of the server.'
+ private_key_uuid:
+ type: string
+ description: 'The UUID of the private key.'
+ is_build_server:
+ type: boolean
+ description: 'Is build server.'
+ instant_validate:
+ type: boolean
+ description: 'Instant validate.'
+ type: object
+ responses:
+ '201':
+ description: 'Server updated.'
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Server'
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
'/servers/{uuid}/resources':
get:
tags:
@@ -3333,7 +3598,7 @@ paths:
description: "Server's Uuid"
required: true
schema:
- type: integer
+ type: string
responses:
'200':
description: 'Get resources by server'
@@ -3365,7 +3630,7 @@ paths:
description: "Server's Uuid"
required: true
schema:
- type: integer
+ type: string
responses:
'200':
description: 'Get domains by server'
@@ -3383,6 +3648,39 @@ paths:
security:
-
bearerAuth: []
+ '/servers/{uuid}/validate':
+ get:
+ tags:
+ - Servers
+ summary: Validate
+ description: 'Validate server by UUID.'
+ operationId: a543a12ef2cbc7a3dd22c3dbe6cbee89
+ parameters:
+ -
+ name: uuid
+ in: path
+ description: 'Server UUID'
+ required: true
+ schema:
+ type: string
+ responses:
+ '201':
+ description: 'Server validation started.'
+ content:
+ application/json:
+ schema:
+ properties:
+ message: { type: string, example: 'Validation started.' }
+ type: object
+ '401':
+ $ref: '#/components/responses/401'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ security:
+ -
+ bearerAuth: []
/services:
get:
tags:
@@ -4189,191 +4487,35 @@ components:
$ref: '#/components/schemas/Environment'
type: object
Server:
- description: 'Application model'
+ description: 'Server model'
properties:
id:
type: integer
- repository_project_id:
- type: integer
- nullable: true
uuid:
type: string
name:
type: string
- fqdn:
- type: string
- config_hash:
- type: string
- git_repository:
- type: string
- git_branch:
- type: string
- git_commit_sha:
- type: string
- git_full_url:
- type: string
- nullable: true
- docker_registry_image_name:
- type: string
- nullable: true
- docker_registry_image_tag:
- type: string
- nullable: true
- build_pack:
- type: string
- static_image:
- type: string
- install_command:
- type: string
- build_command:
- type: string
- start_command:
- type: string
- ports_exposes:
- type: string
- ports_mappings:
- type: string
- nullable: true
- base_directory:
- type: string
- publish_directory:
- type: string
- health_check_path:
- type: string
- health_check_port:
- type: string
- nullable: true
- health_check_host:
- type: string
- health_check_method:
- type: string
- health_check_return_code:
- type: integer
- health_check_scheme:
- type: string
- health_check_response_text:
- type: string
- nullable: true
- health_check_interval:
- type: integer
- health_check_timeout:
- type: integer
- health_check_retries:
- type: integer
- health_check_start_period:
- type: integer
- limits_memory:
- type: string
- limits_memory_swap:
- type: string
- limits_memory_swappiness:
- type: integer
- limits_memory_reservation:
- type: string
- limits_cpus:
- type: string
- limits_cpuset:
- type: string
- nullable: true
- limits_cpu_shares:
- type: integer
- status:
- type: string
- preview_url_template:
- type: string
- destination_type:
- type: string
- destination_id:
- type: integer
- source_type:
- type: string
- source_id:
- type: integer
- private_key_id:
- type: integer
- nullable: true
- environment_id:
- type: integer
- created_at:
- type: string
- format: date-time
- updated_at:
- type: string
- format: date-time
description:
type: string
- nullable: true
- dockerfile:
+ ip:
type: string
- nullable: true
- health_check_enabled:
- type: boolean
- dockerfile_location:
+ user:
type: string
- custom_labels:
- type: string
- dockerfile_target_build:
- type: string
- nullable: true
- manual_webhook_secret_github:
- type: string
- nullable: true
- manual_webhook_secret_gitlab:
- type: string
- nullable: true
- docker_compose_location:
- type: string
- docker_compose:
- type: string
- nullable: true
- docker_compose_raw:
- type: string
- nullable: true
- docker_compose_domains:
- type: string
- nullable: true
- deleted_at:
- type: string
- format: date-time
- nullable: true
- docker_compose_custom_start_command:
- type: string
- nullable: true
- docker_compose_custom_build_command:
- type: string
- nullable: true
- swarm_replicas:
+ port:
type: integer
- swarm_placement_constraints:
- type: string
- nullable: true
- manual_webhook_secret_bitbucket:
- type: string
- nullable: true
- custom_docker_run_options:
- type: string
- nullable: true
- post_deployment_command:
- type: string
- nullable: true
- post_deployment_command_container:
- type: string
- nullable: true
- pre_deployment_command:
- type: string
- nullable: true
- pre_deployment_command_container:
- type: string
- nullable: true
- watch_paths:
- type: string
- nullable: true
- custom_healthcheck_found:
+ proxy:
+ type: object
+ high_disk_usage_notification_sent:
type: boolean
- manual_webhook_secret_gitea:
+ unreachable_notification_sent:
+ type: boolean
+ unreachable_count:
+ type: integer
+ validation_logs:
type: string
- nullable: true
- redirect:
+ log_drain_notification_sent:
+ type: boolean
+ swarm_cluster:
type: string
type: object
ServerSetting:
@@ -4480,6 +4622,9 @@ components:
is_container_label_escape_enabled:
type: boolean
description: 'The flag to enable the container label escape.'
+ is_container_label_readonly_enabled:
+ type: boolean
+ description: 'The flag to enable the container label readonly.'
config_hash:
type: string
description: 'The hash of the service configuration.'
diff --git a/other/logos/branddev.png b/other/logos/branddev.png
new file mode 100644
index 000000000..ef259ff2e
Binary files /dev/null and b/other/logos/branddev.png differ
diff --git a/public/svgs/gitlab.svg b/public/svgs/gitlab.svg
new file mode 100644
index 000000000..1c7cb0719
--- /dev/null
+++ b/public/svgs/gitlab.svg
@@ -0,0 +1 @@
+
diff --git a/resources/views/livewire/project/service/edit-domain.blade.php b/resources/views/livewire/project/service/edit-domain.blade.php
index 214b729fb..5528834eb 100644
--- a/resources/views/livewire/project/service/edit-domain.blade.php
+++ b/resources/views/livewire/project/service/edit-domain.blade.php
@@ -1,5 +1,6 @@