diff --git a/.env.production b/.env.production index 099ec7c250..d3a1b17c5f 100644 --- a/.env.production +++ b/.env.production @@ -1,16 +1,12 @@ -# Coolify Configuration APP_ID= APP_NAME=Coolify APP_KEY= -# PostgreSQL Database Configuration DB_USERNAME=coolify DB_PASSWORD= -# Redis Configuration REDIS_PASSWORD= -# Pusher Configuration PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= diff --git a/app/Actions/Database/StartClickhouse.php b/app/Actions/Database/StartClickhouse.php index 42c6e14490..f218fcabb0 100644 --- a/app/Actions/Database/StartClickhouse.php +++ b/app/Actions/Database/StartClickhouse.php @@ -49,11 +49,7 @@ public function handle(StandaloneClickhouse $database) 'hard' => 262144, ], ], - 'labels' => [ - 'coolify.managed' => 'true', - 'coolify.type' => 'database', - 'coolify.databaseId' => $this->database->id, - ], + 'labels' => defaultDatabaseLabels($this->database)->toArray(), 'healthcheck' => [ 'test' => "clickhouse-client --password {$this->database->clickhouse_admin_password} --query 'SELECT 1'", 'interval' => '5s', diff --git a/app/Actions/Database/StartDatabaseProxy.php b/app/Actions/Database/StartDatabaseProxy.php index 3ddf6c036b..d9272356c1 100644 --- a/app/Actions/Database/StartDatabaseProxy.php +++ b/app/Actions/Database/StartDatabaseProxy.php @@ -67,6 +67,10 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St $type = \App\Models\StandaloneClickhouse::class; $containerName = "clickhouse-{$database->service->uuid}"; break; + case 'standalone-supabase/postgres': + $type = \App\Models\StandalonePostgresql::class; + $containerName = "supabase-db-{$database->service->uuid}"; + break; } } if ($type === \App\Models\StandaloneRedis::class) { diff --git a/app/Actions/Database/StartDragonfly.php b/app/Actions/Database/StartDragonfly.php index ea235be4e3..4f9f45b7c7 100644 --- a/app/Actions/Database/StartDragonfly.php +++ b/app/Actions/Database/StartDragonfly.php @@ -46,11 +46,7 @@ public function handle(StandaloneDragonfly $database) 'networks' => [ $this->database->destination->network, ], - 'labels' => [ - 'coolify.managed' => 'true', - 'coolify.type' => 'database', - 'coolify.databaseId' => $this->database->id, - ], + 'labels' => defaultDatabaseLabels($this->database)->toArray(), 'healthcheck' => [ 'test' => "redis-cli -a {$this->database->dragonfly_password} ping", 'interval' => '5s', diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php index 010bf58845..6c733d3180 100644 --- a/app/Actions/Database/StartKeydb.php +++ b/app/Actions/Database/StartKeydb.php @@ -48,11 +48,7 @@ public function handle(StandaloneKeydb $database) 'networks' => [ $this->database->destination->network, ], - 'labels' => [ - 'coolify.managed' => 'true', - 'coolify.type' => 'database', - 'coolify.databaseId' => $this->database->id, - ], + 'labels' => defaultDatabaseLabels($this->database)->toArray(), 'healthcheck' => [ 'test' => "keydb-cli --pass {$this->database->keydb_password} ping", 'interval' => '5s', diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php index 2437a013e5..299b07385b 100644 --- a/app/Actions/Database/StartMariadb.php +++ b/app/Actions/Database/StartMariadb.php @@ -43,11 +43,7 @@ public function handle(StandaloneMariadb $database) 'networks' => [ $this->database->destination->network, ], - 'labels' => [ - 'coolify.managed' => 'true', - 'coolify.type' => 'database', - 'coolify.databaseId' => $this->database->id, - ], + 'labels' => defaultDatabaseLabels($this->database)->toArray(), 'healthcheck' => [ 'test' => ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized'], 'interval' => '5s', diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index a33e72c27d..89d35ca7b4 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -51,11 +51,7 @@ public function handle(StandaloneMongodb $database) 'networks' => [ $this->database->destination->network, ], - 'labels' => [ - 'coolify.managed' => 'true', - 'coolify.type' => 'database', - 'coolify.databaseId' => $this->database->id, - ], + 'labels' => defaultDatabaseLabels($this->database)->toArray(), 'healthcheck' => [ 'test' => [ 'CMD', diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php index 0b19b3f0cb..73db1512a3 100644 --- a/app/Actions/Database/StartMysql.php +++ b/app/Actions/Database/StartMysql.php @@ -43,11 +43,7 @@ public function handle(StandaloneMysql $database) 'networks' => [ $this->database->destination->network, ], - 'labels' => [ - 'coolify.managed' => 'true', - 'coolify.type' => 'database', - 'coolify.databaseId' => $this->database->id, - ], + 'labels' => defaultDatabaseLabels($this->database)->toArray(), 'healthcheck' => [ 'test' => ['CMD', 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', "-p{$this->database->mysql_root_password}"], 'interval' => '5s', diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php index 7faa232c34..035849340d 100644 --- a/app/Actions/Database/StartPostgresql.php +++ b/app/Actions/Database/StartPostgresql.php @@ -23,6 +23,9 @@ public function handle(StandalonePostgresql $database) $this->database = $database; $container_name = $this->database->uuid; $this->configuration_dir = database_configuration_dir().'/'.$container_name; + if (isDev()) { + $this->configuration_dir = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/databases/'.$container_name; + } $this->commands = [ "echo 'Starting database.'", @@ -47,11 +50,7 @@ public function handle(StandalonePostgresql $database) 'networks' => [ $this->database->destination->network, ], - 'labels' => [ - 'coolify.managed' => 'true', - 'coolify.type' => 'database', - 'coolify.databaseId' => $this->database->id, - ], + 'labels' => defaultDatabaseLabels($this->database)->toArray(), 'healthcheck' => [ 'test' => [ 'CMD-SHELL', @@ -78,7 +77,7 @@ public function handle(StandalonePostgresql $database) ], ], ]; - if (! is_null($this->database->limits_cpuset)) { + if (filled($this->database->limits_cpuset)) { data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset); } if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) { @@ -108,7 +107,7 @@ public function handle(StandalonePostgresql $database) ]; } } - if (! is_null($this->database->postgres_conf) && ! empty($this->database->postgres_conf)) { + if (filled($this->database->postgres_conf)) { $docker_compose['services'][$container_name]['volumes'][] = [ 'type' => 'bind', 'source' => $this->configuration_dir.'/custom-postgres.conf', @@ -199,9 +198,12 @@ private function generate_environment_variables() private function generate_init_scripts() { - if (is_null($this->database->init_scripts) || count($this->database->init_scripts) === 0) { + $this->commands[] = "rm -rf $this->configuration_dir/docker-entrypoint-initdb.d/*"; + + if (blank($this->database->init_scripts) || count($this->database->init_scripts) === 0) { return; } + foreach ($this->database->init_scripts as $init_script) { $filename = data_get($init_script, 'filename'); $content = data_get($init_script, 'content'); @@ -213,10 +215,15 @@ private function generate_init_scripts() private function add_custom_conf() { - if (is_null($this->database->postgres_conf) || empty($this->database->postgres_conf)) { + $filename = 'custom-postgres.conf'; + $config_file_path = "$this->configuration_dir/$filename"; + + if (blank($this->database->postgres_conf)) { + $this->commands[] = "rm -f $config_file_path"; + return; } - $filename = 'custom-postgres.conf'; + $content = $this->database->postgres_conf; if (! str($content)->contains('listen_addresses')) { $content .= "\nlisten_addresses = '*'"; @@ -224,6 +231,6 @@ private function add_custom_conf() $this->database->save(); } $content_base64 = base64_encode($content); - $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null"; + $this->commands[] = "echo '{$content_base64}' | base64 -d | tee $config_file_path > /dev/null"; } } diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php index bacf49f823..1beebd134b 100644 --- a/app/Actions/Database/StartRedis.php +++ b/app/Actions/Database/StartRedis.php @@ -48,11 +48,7 @@ public function handle(StandaloneRedis $database) 'networks' => [ $this->database->destination->network, ], - 'labels' => [ - 'coolify.managed' => 'true', - 'coolify.type' => 'database', - 'coolify.databaseId' => $this->database->id, - ], + 'labels' => defaultDatabaseLabels($this->database)->toArray(), 'healthcheck' => [ 'test' => [ 'CMD-SHELL', diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php index 706356930b..c0e0882032 100644 --- a/app/Actions/Docker/GetContainersStatus.php +++ b/app/Actions/Docker/GetContainersStatus.php @@ -112,7 +112,7 @@ public function handle(Server $server, ?Collection $containers = null, ?Collecti $preview->update(['last_online_at' => now()]); } } else { - //Notify user that this container should not be there. + // Notify user that this container should not be there. } } else { $application = $this->applications->where('id', $applicationId)->first(); @@ -125,7 +125,7 @@ public function handle(Server $server, ?Collection $containers = null, ?Collecti $application->update(['last_online_at' => now()]); } } else { - //Notify user that this container should not be there. + // Notify user that this container should not be there. } } } else { diff --git a/app/Console/Commands/Emails.php b/app/Console/Commands/Emails.php index 33ddf3019f..a022d54dc6 100644 --- a/app/Console/Commands/Emails.php +++ b/app/Console/Commands/Emails.php @@ -183,7 +183,7 @@ public function handle() 'team_id' => 0, ]); } - //$this->mail = (new BackupSuccess($backup->frequency, $db->name))->toMail(); + // $this->mail = (new BackupSuccess($backup->frequency, $db->name))->toMail(); $this->sendEmail(); break; // case 'invitation-link': diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index c48944f792..03e621c771 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -137,14 +137,14 @@ private function checkResources(): void foreach ($servers as $server) { $serverTimezone = data_get($server->settings, 'server_timezone', $this->instanceTimezone); + if (validate_timezone($serverTimezone) === false) { + $serverTimezone = config('app.timezone'); + } // Sentinel check $lastSentinelUpdate = $server->sentinel_updated_at; if (Carbon::parse($lastSentinelUpdate)->isBefore(now()->subSeconds($server->waitBeforeDoingSshCheck()))) { // Check container status every minute if Sentinel does not activated - if (validate_timezone($serverTimezone) === false) { - $serverTimezone = config('app.timezone'); - } if (isCloud()) { $this->scheduleInstance->job(new ServerCheckJob($server))->timezone($serverTimezone)->everyFiveMinutes()->onOneServer(); } else { @@ -152,15 +152,11 @@ private function checkResources(): void } // $this->scheduleInstance->job(new \App\Jobs\ServerCheckNewJob($server))->everyFiveMinutes()->onOneServer(); - // Check storage usage every 10 minutes if Sentinel does not activated - $this->scheduleInstance->job(new ServerStorageCheckJob($server))->everyTenMinutes()->onOneServer(); - } - if ($server->settings->force_docker_cleanup) { - $this->scheduleInstance->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer(); - } else { - $this->scheduleInstance->job(new DockerCleanupJob($server))->everyTenMinutes()->timezone($serverTimezone)->onOneServer(); + $this->scheduleInstance->job(new ServerStorageCheckJob($server))->cron($server->settings->server_disk_usage_check_frequency)->timezone($serverTimezone)->onOneServer(); } + $this->scheduleInstance->job(new DockerCleanupJob($server))->cron($server->settings->docker_cleanup_frequency)->timezone($serverTimezone)->onOneServer(); + // Cleanup multiplexed connections every hour // $this->scheduleInstance->job(new ServerCleanupMux($server))->hourly()->onOneServer(); @@ -213,9 +209,13 @@ private function checkScheduledBackups(): void $serverTimezone = config('app.timezone'); } + if (isset(VALID_CRON_STRINGS[$scheduled_backup->frequency])) { + $scheduled_backup->frequency = VALID_CRON_STRINGS[$scheduled_backup->frequency]; + } + $serverTimezone = data_get($server->settings, 'server_timezone', $this->instanceTimezone); $this->scheduleInstance->job(new DatabaseBackupJob( backup: $scheduled_backup - ))->cron($scheduled_backup->frequency)->timezone($this->instanceTimezone)->onOneServer(); + ))->cron($scheduled_backup->frequency)->timezone($serverTimezone)->onOneServer(); } } @@ -273,7 +273,7 @@ private function checkScheduledTasks(): void } $this->scheduleInstance->job(new ScheduledTaskJob( task: $scheduled_task - ))->cron($scheduled_task->frequency)->timezone($this->instanceTimezone)->onOneServer(); + ))->cron($scheduled_task->frequency)->timezone($serverTimezone)->onOneServer(); } } diff --git a/app/Events/RestoreJobFinished.php b/app/Events/RestoreJobFinished.php new file mode 100644 index 0000000000..d3adb77984 --- /dev/null +++ b/app/Events/RestoreJobFinished.php @@ -0,0 +1,34 @@ +startsWith('/tmp/') + && str($scriptPath)->startsWith('/tmp/') + && ! str($tmpPath)->contains('..') + && ! str($scriptPath)->contains('..') + && strlen($tmpPath) > 5 // longer than just "/tmp/" + && strlen($scriptPath) > 5 + ) { + $commands[] = "docker exec {$container} sh -c 'rm {$scriptPath}'"; + $commands[] = "docker exec {$container} sh -c 'rm {$tmpPath}'"; + instant_remote_process($commands, Server::find($serverId), throwError: true); + } + } + } +} diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index f02c4255dd..4df187c198 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -27,6 +27,9 @@ private function removeSensitiveData($application) { $application->makeHidden([ 'id', + 'resourceable', + 'resourceable_id', + 'resourceable_type', ]); if (request()->attributes->get('can_read_sensitive', false) === false) { $application->makeHidden([ @@ -114,11 +117,12 @@ public function applications(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['project_uuid', 'server_uuid', 'environment_name', 'git_repository', 'git_branch', 'build_pack', 'ports_exposes'], + required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'git_repository', 'git_branch', 'build_pack', 'ports_exposes'], 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.'], + 'environment_name' => ['type' => 'string', 'description' => 'The environment name. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.'], 'git_repository' => ['type' => 'string', 'description' => 'The git repository URL.'], 'git_branch' => ['type' => 'string', 'description' => 'The git branch.'], 'build_pack' => ['type' => 'string', 'enum' => ['nixpacks', 'static', 'dockerfile', 'dockercompose'], 'description' => 'The build pack type.'], @@ -185,8 +189,17 @@ public function applications(Request $request) ), responses: [ new OA\Response( - response: 200, + response: 201, description: 'Application created successfully.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string'], + ] + ) + ) ), new OA\Response( response: 401, @@ -220,11 +233,12 @@ public function create_public_application(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['project_uuid', 'server_uuid', 'environment_name', 'github_app_uuid', 'git_repository', 'git_branch', 'build_pack', 'ports_exposes'], + required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'github_app_uuid', 'git_repository', 'git_branch', 'build_pack', 'ports_exposes'], 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.'], + 'environment_name' => ['type' => 'string', 'description' => 'The environment name. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.'], '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.'], @@ -291,8 +305,17 @@ public function create_public_application(Request $request) ), responses: [ new OA\Response( - response: 200, + response: 201, description: 'Application created successfully.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string'], + ] + ) + ) ), new OA\Response( response: 401, @@ -326,11 +349,12 @@ public function create_private_gh_app_application(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['project_uuid', 'server_uuid', 'environment_name', 'private_key_uuid', 'git_repository', 'git_branch', 'build_pack', 'ports_exposes'], + required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'private_key_uuid', 'git_repository', 'git_branch', 'build_pack', 'ports_exposes'], 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.'], + 'environment_name' => ['type' => 'string', 'description' => 'The environment name. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.'], 'private_key_uuid' => ['type' => 'string', 'description' => 'The private key UUID.'], 'git_repository' => ['type' => 'string', 'description' => 'The git repository URL.'], 'git_branch' => ['type' => 'string', 'description' => 'The git branch.'], @@ -397,8 +421,17 @@ public function create_private_gh_app_application(Request $request) ), responses: [ new OA\Response( - response: 200, + response: 201, description: 'Application created successfully.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string'], + ] + ) + ) ), new OA\Response( response: 401, @@ -432,11 +465,12 @@ public function create_private_deploy_key_application(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['project_uuid', 'server_uuid', 'environment_name', 'dockerfile'], + required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'dockerfile'], 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.'], + 'environment_name' => ['type' => 'string', 'description' => 'The environment name. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.'], 'dockerfile' => ['type' => 'string', 'description' => 'The Dockerfile content.'], 'build_pack' => ['type' => 'string', 'enum' => ['nixpacks', 'static', 'dockerfile', 'dockercompose'], 'description' => 'The build pack type.'], 'ports_exposes' => ['type' => 'string', 'description' => 'The ports to expose.'], @@ -487,8 +521,17 @@ public function create_private_deploy_key_application(Request $request) ), responses: [ new OA\Response( - response: 200, + response: 201, description: 'Application created successfully.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string'], + ] + ) + ) ), new OA\Response( response: 401, @@ -522,11 +565,12 @@ public function create_dockerfile_application(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['project_uuid', 'server_uuid', 'environment_name', 'docker_registry_image_name', 'ports_exposes'], + required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'docker_registry_image_name', 'ports_exposes'], 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.'], + 'environment_name' => ['type' => 'string', 'description' => 'The environment name. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.'], 'docker_registry_image_name' => ['type' => 'string', 'description' => 'The docker registry image name.'], 'docker_registry_image_tag' => ['type' => 'string', 'description' => 'The docker registry image tag.'], 'ports_exposes' => ['type' => 'string', 'description' => 'The ports to expose.'], @@ -574,8 +618,17 @@ public function create_dockerfile_application(Request $request) ), responses: [ new OA\Response( - response: 200, + response: 201, description: 'Application created successfully.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string'], + ] + ) + ) ), new OA\Response( response: 401, @@ -609,11 +662,12 @@ public function create_dockerimage_application(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['project_uuid', 'server_uuid', 'environment_name', 'docker_compose_raw'], + required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'docker_compose_raw'], 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.'], + 'environment_name' => ['type' => 'string', 'description' => 'The environment name. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.'], 'docker_compose_raw' => ['type' => 'string', 'description' => 'The Docker Compose raw content.'], 'destination_uuid' => ['type' => 'string', 'description' => 'The destination UUID if the server has more than one destinations.'], 'name' => ['type' => 'string', 'description' => 'The application name.'], @@ -627,8 +681,17 @@ public function create_dockerimage_application(Request $request) ), responses: [ new OA\Response( - response: 200, + response: 201, description: 'Application created successfully.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string'], + ] + ) + ) ), new OA\Response( response: 401, @@ -647,7 +710,7 @@ public function create_dockercompose_application(Request $request) private function create_application(Request $request, $type) { - $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server', 'static_image', 'custom_nginx_configuration']; + $allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'private_key_uuid', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile', 'docker_compose_location', 'docker_compose_raw', 'docker_compose_custom_start_command', 'docker_compose_custom_build_command', 'docker_compose_domains', 'watch_paths', 'use_build_server', 'static_image', 'custom_nginx_configuration']; $teamId = getTeamIdFromToken(); if (is_null($teamId)) { return invalidTokenResponse(); @@ -661,7 +724,8 @@ private function create_application(Request $request, $type) 'name' => 'string|max:255', 'description' => 'string|nullable', 'project_uuid' => 'string|required', - 'environment_name' => 'string|required', + 'environment_name' => 'string|nullable', + 'environment_uuid' => 'string|nullable', 'server_uuid' => 'string|required', 'destination_uuid' => 'string', ]); @@ -681,6 +745,11 @@ private function create_application(Request $request, $type) ], 422); } + $environmentUuid = $request->environment_uuid; + $environmentName = $request->environment_name; + if (blank($environmentUuid) && blank($environmentName)) { + return response()->json(['message' => 'You need to provide at least one of environment_name or environment_uuid.'], 422); + } $serverUuid = $request->server_uuid; $fqdn = $request->domains; $instantDeploy = $request->instant_deploy; @@ -713,7 +782,10 @@ private function create_application(Request $request, $type) if (! $project) { return response()->json(['message' => 'Project not found.'], 404); } - $environment = $project->environments()->where('name', $request->environment_name)->first(); + $environment = $project->environments()->where('name', $environmentName)->first(); + if (! $environment) { + $environment = $project->environments()->where('uuid', $environmentUuid)->first(); + } if (! $environment) { return response()->json(['message' => 'Environment not found.'], 404); } @@ -730,12 +802,6 @@ private function create_application(Request $request, $type) } $destination = $destinations->first(); if ($type === 'public') { - if (! $request->has('name')) { - $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); - } - if ($request->build_pack === 'dockercompose') { - $request->offsetSet('ports_exposes', '80'); - } $validationRules = [ 'git_repository' => 'string|required', 'git_branch' => 'string|required', @@ -745,7 +811,7 @@ private function create_application(Request $request, $type) 'docker_compose_raw' => 'string|nullable', 'docker_compose_domains' => 'array|nullable', ]; - $validationRules = array_merge($validationRules, sharedDataApplications()); + $validationRules = array_merge(sharedDataApplications(), $validationRules); $validator = customApiValidator($request->all(), $validationRules); if ($validator->fails()) { return response()->json([ @@ -753,6 +819,12 @@ private function create_application(Request $request, $type) 'errors' => $validator->errors(), ], 422); } + if (! $request->has('name')) { + $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); + } + if ($request->build_pack === 'dockercompose') { + $request->offsetSet('ports_exposes', '80'); + } $return = $this->validateDataApplications($request, $server); if ($return instanceof \Illuminate\Http\JsonResponse) { @@ -815,14 +887,8 @@ private function create_application(Request $request, $type) return response()->json(serializeApiResponse([ 'uuid' => data_get($application, 'uuid'), 'domains' => data_get($application, 'domains'), - ])); + ]))->setStatusCode(201); } elseif ($type === 'private-gh-app') { - if (! $request->has('name')) { - $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); - } - if ($request->build_pack === 'dockercompose') { - $request->offsetSet('ports_exposes', '80'); - } $validationRules = [ 'git_repository' => 'string|required', 'git_branch' => 'string|required', @@ -833,7 +899,7 @@ private function create_application(Request $request, $type) 'docker_compose_location' => 'string', 'docker_compose_raw' => 'string|nullable', ]; - $validationRules = array_merge($validationRules, sharedDataApplications()); + $validationRules = array_merge(sharedDataApplications(), $validationRules); $validator = customApiValidator($request->all(), $validationRules); if ($validator->fails()) { @@ -842,6 +908,14 @@ private function create_application(Request $request, $type) 'errors' => $validator->errors(), ], 422); } + + if (! $request->has('name')) { + $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); + } + if ($request->build_pack === 'dockercompose') { + $request->offsetSet('ports_exposes', '80'); + } + $return = $this->validateDataApplications($request, $server); if ($return instanceof \Illuminate\Http\JsonResponse) { return $return; @@ -884,12 +958,12 @@ private function create_application(Request $request, $type) $application->environment_id = $environment->id; $application->source_type = $githubApp->getMorphClass(); $application->source_id = $githubApp->id; + $application->save(); + $application->refresh(); if (isset($useBuildServer)) { $application->settings->is_build_server_enabled = $useBuildServer; $application->settings->save(); } - $application->save(); - $application->refresh(); if (! $application->settings->is_container_label_readonly_enabled) { $application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); $application->save(); @@ -914,14 +988,8 @@ private function create_application(Request $request, $type) return response()->json(serializeApiResponse([ 'uuid' => data_get($application, 'uuid'), 'domains' => data_get($application, 'domains'), - ])); + ]))->setStatusCode(201); } elseif ($type === 'private-deploy-key') { - if (! $request->has('name')) { - $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); - } - if ($request->build_pack === 'dockercompose') { - $request->offsetSet('ports_exposes', '80'); - } $validationRules = [ 'git_repository' => 'string|required', @@ -934,7 +1002,7 @@ private function create_application(Request $request, $type) 'docker_compose_raw' => 'string|nullable', ]; - $validationRules = array_merge($validationRules, sharedDataApplications()); + $validationRules = array_merge(sharedDataApplications(), $validationRules); $validator = customApiValidator($request->all(), $validationRules); if ($validator->fails()) { @@ -943,6 +1011,13 @@ private function create_application(Request $request, $type) 'errors' => $validator->errors(), ], 422); } + if (! $request->has('name')) { + $request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch)); + } + if ($request->build_pack === 'dockercompose') { + $request->offsetSet('ports_exposes', '80'); + } + $return = $this->validateDataApplications($request, $server); if ($return instanceof \Illuminate\Http\JsonResponse) { return $return; @@ -980,12 +1055,12 @@ private function create_application(Request $request, $type) $application->destination_id = $destination->id; $application->destination_type = $destination->getMorphClass(); $application->environment_id = $environment->id; + $application->save(); + $application->refresh(); if (isset($useBuildServer)) { $application->settings->is_build_server_enabled = $useBuildServer; $application->settings->save(); } - $application->save(); - $application->refresh(); if (! $application->settings->is_container_label_readonly_enabled) { $application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); $application->save(); @@ -1010,16 +1085,12 @@ private function create_application(Request $request, $type) return response()->json(serializeApiResponse([ 'uuid' => data_get($application, 'uuid'), 'domains' => data_get($application, 'domains'), - ])); + ]))->setStatusCode(201); } elseif ($type === 'dockerfile') { - if (! $request->has('name')) { - $request->offsetSet('name', 'dockerfile-'.new Cuid2); - } - $validationRules = [ 'dockerfile' => 'string|required', ]; - $validationRules = array_merge($validationRules, sharedDataApplications()); + $validationRules = array_merge(sharedDataApplications(), $validationRules); $validator = customApiValidator($request->all(), $validationRules); if ($validator->fails()) { @@ -1028,6 +1099,10 @@ private function create_application(Request $request, $type) 'errors' => $validator->errors(), ], 422); } + if (! $request->has('name')) { + $request->offsetSet('name', 'dockerfile-'.new Cuid2); + } + $return = $this->validateDataApplications($request, $server); if ($return instanceof \Illuminate\Http\JsonResponse) { return $return; @@ -1066,15 +1141,15 @@ private function create_application(Request $request, $type) $application->destination_id = $destination->id; $application->destination_type = $destination->getMorphClass(); $application->environment_id = $environment->id; - if (isset($useBuildServer)) { - $application->settings->is_build_server_enabled = $useBuildServer; - $application->settings->save(); - } $application->git_repository = 'coollabsio/coolify'; $application->git_branch = 'main'; $application->save(); $application->refresh(); + if (isset($useBuildServer)) { + $application->settings->is_build_server_enabled = $useBuildServer; + $application->settings->save(); + } if (! $application->settings->is_container_label_readonly_enabled) { $application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); $application->save(); @@ -1095,17 +1170,14 @@ private function create_application(Request $request, $type) return response()->json(serializeApiResponse([ 'uuid' => data_get($application, 'uuid'), 'domains' => data_get($application, 'domains'), - ])); + ]))->setStatusCode(201); } elseif ($type === 'dockerimage') { - if (! $request->has('name')) { - $request->offsetSet('name', 'docker-image-'.new Cuid2); - } $validationRules = [ 'docker_registry_image_name' => 'string|required', 'docker_registry_image_tag' => 'string', 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required', ]; - $validationRules = array_merge($validationRules, sharedDataApplications()); + $validationRules = array_merge(sharedDataApplications(), $validationRules); $validator = customApiValidator($request->all(), $validationRules); if ($validator->fails()) { @@ -1114,6 +1186,9 @@ private function create_application(Request $request, $type) 'errors' => $validator->errors(), ], 422); } + if (! $request->has('name')) { + $request->offsetSet('name', 'docker-image-'.new Cuid2); + } $return = $this->validateDataApplications($request, $server); if ($return instanceof \Illuminate\Http\JsonResponse) { return $return; @@ -1130,15 +1205,15 @@ private function create_application(Request $request, $type) $application->destination_id = $destination->id; $application->destination_type = $destination->getMorphClass(); $application->environment_id = $environment->id; - if (isset($useBuildServer)) { - $application->settings->is_build_server_enabled = $useBuildServer; - $application->settings->save(); - } $application->git_repository = 'coollabsio/coolify'; $application->git_branch = 'main'; $application->save(); $application->refresh(); + if (isset($useBuildServer)) { + $application->settings->is_build_server_enabled = $useBuildServer; + $application->settings->save(); + } if (! $application->settings->is_container_label_readonly_enabled) { $application->custom_labels = str(implode('|coolify|', generateLabelsApplication($application)))->replace('|coolify|', "\n"); $application->save(); @@ -1159,9 +1234,9 @@ private function create_application(Request $request, $type) return response()->json(serializeApiResponse([ 'uuid' => data_get($application, 'uuid'), 'domains' => data_get($application, 'domains'), - ])); + ]))->setStatusCode(201); } elseif ($type === 'dockercompose') { - $allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'instant_deploy', 'docker_compose_raw']; + $allowedFields = ['project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'instant_deploy', 'docker_compose_raw']; $extraFields = array_diff(array_keys($request->all()), $allowedFields); if ($validator->fails() || ! empty($extraFields)) { @@ -1183,7 +1258,7 @@ private function create_application(Request $request, $type) $validationRules = [ 'docker_compose_raw' => 'string|required', ]; - $validationRules = array_merge($validationRules, sharedDataApplications()); + $validationRules = array_merge(sharedDataApplications(), $validationRules); $validator = customApiValidator($request->all(), $validationRules); if ($validator->fails()) { @@ -1241,7 +1316,7 @@ private function create_application(Request $request, $type) return response()->json(serializeApiResponse([ 'uuid' => data_get($service, 'uuid'), 'domains' => data_get($service, 'domains'), - ])); + ]))->setStatusCode(201); } return response()->json(['message' => 'Invalid type.'], 400); @@ -1551,7 +1626,7 @@ public function update_by_uuid(Request $request) 'docker_compose_custom_build_command' => 'string|nullable', 'custom_nginx_configuration' => 'string|nullable', ]; - $validationRules = array_merge($validationRules, sharedDataApplications()); + $validationRules = array_merge(sharedDataApplications(), $validationRules); $validator = customApiValidator($request->all(), $validationRules); // Validate ports_exposes @@ -1668,7 +1743,10 @@ public function update_by_uuid(Request $request) removeUnnecessaryFieldsFromRequest($request); $data = $request->all(); - data_set($data, 'fqdn', $domains); + if ($request->has('domains') && $server->isProxyShouldRun()) { + data_set($data, 'fqdn', $domains); + } + if ($dockerComposeDomainsJson->count() > 0) { data_set($data, 'docker_compose_domains', json_encode($dockerComposeDomainsJson)); } @@ -1893,8 +1971,9 @@ public function update_env_by_uuid(Request $request) $is_preview = $request->is_preview ?? false; $is_build_time = $request->is_build_time ?? false; $is_literal = $request->is_literal ?? false; + $key = str($request->key)->trim()->replace(' ', '_')->value; if ($is_preview) { - $env = $application->environment_variables_preview->where('key', $request->key)->first(); + $env = $application->environment_variables_preview->where('key', $key)->first(); if ($env) { $env->value = $request->value; if ($env->is_build_time != $is_build_time) { @@ -1921,7 +2000,7 @@ public function update_env_by_uuid(Request $request) ], 404); } } else { - $env = $application->environment_variables->where('key', $request->key)->first(); + $env = $application->environment_variables->where('key', $key)->first(); if ($env) { $env->value = $request->value; if ($env->is_build_time != $is_build_time) { @@ -2064,6 +2143,7 @@ public function create_bulk_envs(Request $request) $bulk_data = collect($bulk_data)->map(function ($item) { return collect($item)->only(['key', 'value', 'is_preview', 'is_build_time', 'is_literal']); }); + $returnedEnvs = collect(); foreach ($bulk_data as $item) { $validator = customApiValidator($item, [ 'key' => 'string|required', @@ -2085,8 +2165,9 @@ public function create_bulk_envs(Request $request) $is_literal = $item->get('is_literal') ?? false; $is_multi_line = $item->get('is_multiline') ?? false; $is_shown_once = $item->get('is_shown_once') ?? false; + $key = str($item->get('key'))->trim()->replace(' ', '_')->value; if ($is_preview) { - $env = $application->environment_variables_preview->where('key', $item->get('key'))->first(); + $env = $application->environment_variables_preview->where('key', $key)->first(); if ($env) { $env->value = $item->get('value'); if ($env->is_build_time != $is_build_time) { @@ -2111,10 +2192,12 @@ public function create_bulk_envs(Request $request) 'is_literal' => $is_literal, 'is_multiline' => $is_multi_line, 'is_shown_once' => $is_shown_once, + 'resourceable_type' => get_class($application), + 'resourceable_id' => $application->id, ]); } } else { - $env = $application->environment_variables->where('key', $item->get('key'))->first(); + $env = $application->environment_variables->where('key', $key)->first(); if ($env) { $env->value = $item->get('value'); if ($env->is_build_time != $is_build_time) { @@ -2139,12 +2222,15 @@ public function create_bulk_envs(Request $request) 'is_literal' => $is_literal, 'is_multiline' => $is_multi_line, 'is_shown_once' => $is_shown_once, + 'resourceable_type' => get_class($application), + 'resourceable_id' => $application->id, ]); } } + $returnedEnvs->push($this->removeSensitiveData($env)); } - return response()->json($this->removeSensitiveData($env))->setStatusCode(201); + return response()->json($returnedEnvs)->setStatusCode(201); } #[OA\Post( @@ -2257,8 +2343,10 @@ public function create_env(Request $request) ], 422); } $is_preview = $request->is_preview ?? false; + $key = str($request->key)->trim()->replace(' ', '_')->value; + if ($is_preview) { - $env = $application->environment_variables_preview->where('key', $request->key)->first(); + $env = $application->environment_variables_preview->where('key', $key)->first(); if ($env) { return response()->json([ 'message' => 'Environment variable already exists. Use PATCH request to update it.', @@ -2272,6 +2360,8 @@ public function create_env(Request $request) 'is_literal' => $request->is_literal ?? false, 'is_multiline' => $request->is_multiline ?? false, 'is_shown_once' => $request->is_shown_once ?? false, + 'resourceable_type' => get_class($application), + 'resourceable_id' => $application->id, ]); return response()->json([ @@ -2279,7 +2369,7 @@ public function create_env(Request $request) ])->setStatusCode(201); } } else { - $env = $application->environment_variables->where('key', $request->key)->first(); + $env = $application->environment_variables->where('key', $key)->first(); if ($env) { return response()->json([ 'message' => 'Environment variable already exists. Use PATCH request to update it.', @@ -2293,6 +2383,8 @@ public function create_env(Request $request) 'is_literal' => $request->is_literal ?? false, 'is_multiline' => $request->is_multiline ?? false, 'is_shown_once' => $request->is_shown_once ?? false, + 'resourceable_type' => get_class($application), + 'resourceable_id' => $application->id, ]); return response()->json([ @@ -2380,7 +2472,10 @@ public function delete_env_by_uuid(Request $request) 'message' => 'Application not found.', ], 404); } - $found_env = EnvironmentVariable::where('uuid', $request->env_uuid)->where('application_id', $application->id)->first(); + $found_env = EnvironmentVariable::where('uuid', $request->env_uuid) + ->where('resourceable_type', Application::class) + ->where('resourceable_id', $application->id) + ->first(); if (! $found_env) { return response()->json([ 'message' => 'Environment variable not found.', diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index 917171e5cd..504665f6a8 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -523,11 +523,12 @@ public function update_by_uuid(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'], properties: [ 'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'], 'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'], - 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], + 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'], 'postgres_user' => ['type' => 'string', 'description' => 'PostgreSQL user'], 'postgres_password' => ['type' => 'string', 'description' => 'PostgreSQL password'], 'postgres_db' => ['type' => 'string', 'description' => 'PostgreSQL database'], @@ -589,11 +590,12 @@ public function create_database_postgresql(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'], properties: [ 'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'], 'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'], - 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], + 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'], 'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'], 'clickhouse_admin_user' => ['type' => 'string', 'description' => 'Clickhouse admin user'], 'clickhouse_admin_password' => ['type' => 'string', 'description' => 'Clickhouse admin password'], @@ -651,11 +653,12 @@ public function create_database_clickhouse(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'], properties: [ 'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'], 'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'], - 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], + 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'], 'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'], 'dragonfly_password' => ['type' => 'string', 'description' => 'DragonFly password'], 'name' => ['type' => 'string', 'description' => 'Name of the database'], @@ -712,11 +715,12 @@ public function create_database_dragonfly(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'], properties: [ 'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'], 'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'], - 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], + 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'], 'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'], 'redis_password' => ['type' => 'string', 'description' => 'Redis password'], 'redis_conf' => ['type' => 'string', 'description' => 'Redis conf'], @@ -774,11 +778,12 @@ public function create_database_redis(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'], properties: [ 'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'], 'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'], - 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], + 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'], 'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'], 'keydb_password' => ['type' => 'string', 'description' => 'KeyDB password'], 'keydb_conf' => ['type' => 'string', 'description' => 'KeyDB conf'], @@ -836,11 +841,12 @@ public function create_database_keydb(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'], properties: [ 'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'], 'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'], - 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], + 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'], 'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'], 'mariadb_conf' => ['type' => 'string', 'description' => 'MariaDB conf'], 'mariadb_root_password' => ['type' => 'string', 'description' => 'MariaDB root password'], @@ -901,11 +907,12 @@ public function create_database_mariadb(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'], properties: [ 'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'], 'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'], - 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], + 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'], 'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'], 'mysql_root_password' => ['type' => 'string', 'description' => 'MySQL root password'], 'mysql_password' => ['type' => 'string', 'description' => 'MySQL password'], @@ -966,11 +973,12 @@ public function create_database_mysql(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'], properties: [ 'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'], 'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'], - 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment'], + 'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'], 'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'], 'mongo_conf' => ['type' => 'string', 'description' => 'MongoDB conf'], 'mongo_initdb_root_username' => ['type' => 'string', 'description' => 'MongoDB initdb root username'], @@ -1013,7 +1021,7 @@ public function create_database_mongodb(Request $request) public function create_database(Request $request, NewDatabaseTypes $type) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $teamId = getTeamIdFromToken(); if (is_null($teamId)) { @@ -1039,6 +1047,11 @@ public function create_database(Request $request, NewDatabaseTypes $type) 'errors' => $errors, ], 422); } + $environmentUuid = $request->environment_uuid; + $environmentName = $request->environment_name; + if (blank($environmentUuid) && blank($environmentName)) { + return response()->json(['message' => 'You need to provide at least one of environment_name or environment_uuid.'], 422); + } $serverUuid = $request->server_uuid; $instantDeploy = $request->instant_deploy ?? false; if ($request->is_public && ! $request->public_port) { @@ -1048,9 +1061,12 @@ public function create_database(Request $request, NewDatabaseTypes $type) if (! $project) { return response()->json(['message' => 'Project not found.'], 404); } - $environment = $project->environments()->where('name', $request->environment_name)->first(); + $environment = $project->environments()->where('name', $environmentName)->first(); + if (! $environment) { + $environment = $project->environments()->where('uuid', $environmentUuid)->first(); + } if (! $environment) { - return response()->json(['message' => 'Environment not found.'], 404); + return response()->json(['message' => 'You need to provide a valid environment_name or environment_uuid.'], 422); } $server = Server::whereTeamId($teamId)->whereUuid($serverUuid)->first(); if (! $server) { @@ -1074,7 +1090,8 @@ public function create_database(Request $request, NewDatabaseTypes $type) 'description' => 'string|nullable', 'image' => 'string', 'project_uuid' => 'string|required', - 'environment_name' => 'string|required', + 'environment_name' => 'string|nullable', + 'environment_uuid' => 'string|nullable', 'server_uuid' => 'string|required', 'destination_uuid' => 'string', 'is_public' => 'boolean', @@ -1105,7 +1122,7 @@ public function create_database(Request $request, NewDatabaseTypes $type) } } if ($type === NewDatabaseTypes::POSTGRESQL) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf']; $validator = customApiValidator($request->all(), [ 'postgres_user' => 'string', 'postgres_password' => 'string', @@ -1164,7 +1181,7 @@ public function create_database(Request $request, NewDatabaseTypes $type) return response()->json(serializeApiResponse($payload))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::MARIADB) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database']; $validator = customApiValidator($request->all(), [ 'clickhouse_admin_user' => 'string', 'clickhouse_admin_password' => 'string', @@ -1220,7 +1237,7 @@ public function create_database(Request $request, NewDatabaseTypes $type) return response()->json(serializeApiResponse($payload))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::MYSQL) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf']; $validator = customApiValidator($request->all(), [ 'mysql_root_password' => 'string', 'mysql_password' => 'string', @@ -1279,7 +1296,7 @@ public function create_database(Request $request, NewDatabaseTypes $type) return response()->json(serializeApiResponse($payload))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::REDIS) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'redis_password', 'redis_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'redis_password', 'redis_conf']; $validator = customApiValidator($request->all(), [ 'redis_password' => 'string', 'redis_conf' => 'string', @@ -1335,7 +1352,7 @@ public function create_database(Request $request, NewDatabaseTypes $type) return response()->json(serializeApiResponse($payload))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::DRAGONFLY) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'dragonfly_password']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'dragonfly_password']; $validator = customApiValidator($request->all(), [ 'dragonfly_password' => 'string', ]); @@ -1365,7 +1382,7 @@ public function create_database(Request $request, NewDatabaseTypes $type) 'uuid' => $database->uuid, ]))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::KEYDB) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'keydb_password', 'keydb_conf']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'keydb_password', 'keydb_conf']; $validator = customApiValidator($request->all(), [ 'keydb_password' => 'string', 'keydb_conf' => 'string', @@ -1421,7 +1438,7 @@ public function create_database(Request $request, NewDatabaseTypes $type) return response()->json(serializeApiResponse($payload))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::CLICKHOUSE) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'clickhouse_admin_user', 'clickhouse_admin_password']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'clickhouse_admin_user', 'clickhouse_admin_password']; $validator = customApiValidator($request->all(), [ 'clickhouse_admin_user' => 'string', 'clickhouse_admin_password' => 'string', @@ -1457,7 +1474,7 @@ public function create_database(Request $request, NewDatabaseTypes $type) return response()->json(serializeApiResponse($payload))->setStatusCode(201); } elseif ($type === NewDatabaseTypes::MONGODB) { - $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database']; + $allowedFields = ['name', 'description', 'image', 'public_port', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database']; $validator = customApiValidator($request->all(), [ 'mongo_conf' => 'string', 'mongo_initdb_root_username' => 'string', diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index 1d89c82edf..b94ce9c67f 100644 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -90,11 +90,13 @@ public function project_by_uuid(Request $request) if (is_null($teamId)) { return invalidTokenResponse(); } - $project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']); + $project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first(); if (! $project) { return response()->json(['message' => 'Project not found.'], 404); } + $project->load(['environments']); + return response()->json( serializeApiResponse($project), ); @@ -102,16 +104,16 @@ public function project_by_uuid(Request $request) #[OA\Get( summary: 'Environment', - description: 'Get environment by name.', - path: '/projects/{uuid}/{environment_name}', - operationId: 'get-environment-by-name', + description: 'Get environment by name or UUID.', + path: '/projects/{uuid}/{environment_name_or_uuid}', + operationId: 'get-environment-by-name-or-uuid', security: [ ['bearerAuth' => []], ], tags: ['Projects'], parameters: [ 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')), + new OA\Parameter(name: 'environment_name_or_uuid', in: 'path', required: true, description: 'Environment name or UUID', schema: new OA\Schema(type: 'string')), ], responses: [ new OA\Response( @@ -141,14 +143,17 @@ public function environment_details(Request $request) if (! $request->uuid) { return response()->json(['message' => 'UUID is required.'], 422); } - if (! $request->environment_name) { - return response()->json(['message' => 'Environment name is required.'], 422); + if (! $request->environment_name_or_uuid) { + return response()->json(['message' => 'Environment name or UUID is required.'], 422); } $project = Project::whereTeamId($teamId)->whereUuid($request->uuid)->first(); if (! $project) { return response()->json(['message' => 'Project not found.'], 404); } - $environment = $project->environments()->whereName($request->environment_name)->first(); + $environment = $project->environments()->whereName($request->environment_name_or_uuid)->first(); + if (! $environment) { + $environment = $project->environments()->whereUuid($request->environment_name_or_uuid)->first(); + } if (! $environment) { return response()->json(['message' => 'Environment not found.'], 404); } diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index bcaba71078..03d9d209cc 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -20,6 +20,9 @@ private function removeSensitiveData($service) { $service->makeHidden([ 'id', + 'resourceable', + 'resourceable_id', + 'resourceable_type', ]); if (request()->attributes->get('can_read_sensitive', false) === false) { $service->makeHidden([ @@ -99,7 +102,7 @@ public function services(Request $request) mediaType: 'application/json', schema: new OA\Schema( type: 'object', - required: ['server_uuid', 'project_uuid', 'environment_name', 'type'], + required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid', 'type'], properties: [ 'type' => [ 'description' => 'The one-click service type', @@ -196,7 +199,8 @@ public function services(Request $request) 'name' => ['type' => 'string', 'maxLength' => 255, 'description' => 'Name of the service.'], 'description' => ['type' => 'string', 'nullable' => true, 'description' => 'Description of the service.'], 'project_uuid' => ['type' => 'string', 'description' => 'Project UUID.'], - 'environment_name' => ['type' => 'string', 'description' => 'Environment name.'], + 'environment_name' => ['type' => 'string', 'description' => 'Environment name. You need to provide at least one of environment_name or environment_uuid.'], + 'environment_uuid' => ['type' => 'string', 'description' => 'Environment UUID. You need to provide at least one of environment_name or environment_uuid.'], 'server_uuid' => ['type' => 'string', 'description' => 'Server UUID.'], 'destination_uuid' => ['type' => 'string', 'description' => 'Destination UUID. Required if server has multiple destinations.'], 'instant_deploy' => ['type' => 'boolean', 'default' => false, 'description' => 'Start the service immediately after creation.'], @@ -233,7 +237,7 @@ public function services(Request $request) )] public function create_service(Request $request) { - $allowedFields = ['type', 'name', 'description', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy']; + $allowedFields = ['type', 'name', 'description', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy']; $teamId = getTeamIdFromToken(); if (is_null($teamId)) { @@ -247,7 +251,8 @@ public function create_service(Request $request) $validator = customApiValidator($request->all(), [ 'type' => 'string|required', 'project_uuid' => 'string|required', - 'environment_name' => 'string|required', + 'environment_name' => 'string|nullable', + 'environment_uuid' => 'string|nullable', 'server_uuid' => 'string|required', 'destination_uuid' => 'string', 'name' => 'string|max:255', @@ -269,6 +274,11 @@ public function create_service(Request $request) 'errors' => $errors, ], 422); } + $environmentUuid = $request->environment_uuid; + $environmentName = $request->environment_name; + if (blank($environmentUuid) && blank($environmentName)) { + return response()->json(['message' => 'You need to provide at least one of environment_name or environment_uuid.'], 422); + } $serverUuid = $request->server_uuid; $instantDeploy = $request->instant_deploy ?? false; if ($request->is_public && ! $request->public_port) { @@ -278,7 +288,10 @@ public function create_service(Request $request) if (! $project) { return response()->json(['message' => 'Project not found.'], 404); } - $environment = $project->environments()->where('name', $request->environment_name)->first(); + $environment = $project->environments()->where('name', $environmentName)->first(); + if (! $environment) { + $environment = $project->environments()->where('uuid', $environmentUuid)->first(); + } if (! $environment) { return response()->json(['message' => 'Environment not found.'], 404); } @@ -333,7 +346,8 @@ public function create_service(Request $request) EnvironmentVariable::create([ 'key' => $key, 'value' => $generatedValue, - 'service_id' => $service->id, + 'resourceable_id' => $service->id, + 'resourceable_type' => $service->getMorphClass(), 'is_build_time' => false, 'is_preview' => false, ]); @@ -345,7 +359,11 @@ public function create_service(Request $request) } $domains = $service->applications()->get()->pluck('fqdn')->sort(); $domains = $domains->map(function ($domain) { - return str($domain)->beforeLast(':')->value(); + if (count(explode(':', $domain)) > 2) { + return str($domain)->beforeLast(':')->value(); + } + + return $domain; }); return response()->json([ @@ -673,7 +691,8 @@ public function update_env_by_uuid(Request $request) ], 422); } - $env = $service->environment_variables()->where('key', $request->key)->first(); + $key = str($request->key)->trim()->replace(' ', '_')->value; + $env = $service->environment_variables()->where('key', $key)->first(); if (! $env) { return response()->json(['message' => 'Environment variable not found.'], 404); } @@ -799,9 +818,9 @@ public function create_bulk_envs(Request $request) 'errors' => $validator->errors(), ], 422); } - + $key = str($item['key'])->trim()->replace(' ', '_')->value; $env = $service->environment_variables()->updateOrCreate( - ['key' => $item['key']], + ['key' => $key], $item ); @@ -909,7 +928,8 @@ public function create_env(Request $request) ], 422); } - $existingEnv = $service->environment_variables()->where('key', $request->key)->first(); + $key = str($request->key)->trim()->replace(' ', '_')->value; + $existingEnv = $service->environment_variables()->where('key', $key)->first(); if ($existingEnv) { return response()->json([ 'message' => 'Environment variable already exists. Use PATCH request to update it.', @@ -995,7 +1015,8 @@ public function delete_env_by_uuid(Request $request) } $env = EnvironmentVariable::where('uuid', $request->env_uuid) - ->where('service_id', $service->id) + ->where('resourceable_type', Service::class) + ->where('resourceable_id', $service->id) ->first(); if (! $env) { diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 538c2eca08..ffdf771d88 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -39,6 +39,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue { use Dispatchable, ExecuteRemoteCommand, InteractsWithQueue, Queueable, SerializesModels; + public $tries = 1; + public $timeout = 3600; public static int $batch_counter = 0; @@ -124,6 +126,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue private ?string $nixpacks_plan = null; + private Collection $nixpacks_plan_json; + private ?string $nixpacks_type = null; private string $dockerfile_location = '/Dockerfile'; @@ -175,6 +179,8 @@ public function __construct(public int $application_deployment_queue_id) $this->onQueue('high'); $this->application_deployment_queue = ApplicationDeploymentQueue::find($this->application_deployment_queue_id); + $this->nixpacks_plan_json = collect([]); + $this->application = Application::find($this->application_deployment_queue->application_id); $this->build_pack = data_get($this->application, 'build_pack'); $this->build_args = collect([]); @@ -1404,7 +1410,7 @@ private function deploy_to_additional_destinations() 'project_uuid' => data_get($this->application, 'environment.project.uuid'), 'application_uuid' => data_get($this->application, 'uuid'), 'deployment_uuid' => $deployment_uuid, - 'environment_name' => data_get($this->application, 'environment.name'), + 'environment_uuid' => data_get($this->application, 'environment.uuid'), ])); } } @@ -1544,7 +1550,7 @@ private function generate_nixpacks_confs() // Do any modifications here $this->generate_env_variables(); - $merged_envs = $this->env_args->merge(collect(data_get($parsed, 'variables', []))); + $merged_envs = collect(data_get($parsed, 'variables', []))->merge($this->env_args); $aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []); if (count($aptPkgs) === 0) { $aptPkgs = ['curl', 'wget']; @@ -1569,6 +1575,7 @@ private function generate_nixpacks_confs() $this->elixir_finetunes(); } $this->nixpacks_plan = json_encode($parsed, JSON_PRETTY_PRINT); + $this->nixpacks_plan_json = collect($parsed); $this->application_deployment_queue->addLogEntry("Final Nixpacks plan: {$this->nixpacks_plan}", hidden: true); if ($this->nixpacks_type === 'rust') { // temporary: disable healthcheck for rust because the start phase does not have curl/wget @@ -1689,7 +1696,7 @@ private function generate_compose_file() return escapeDollarSign($value); }); } - $labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray(); + $labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->application->project()->name, $this->application->name, $this->application->environment->name, $this->pull_request_id))->toArray(); // Check for custom HEALTHCHECK if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) { @@ -2277,18 +2284,10 @@ private function start_by_compose_file() private function generate_build_env_variables() { - $this->build_args = collect(["--build-arg SOURCE_COMMIT=\"{$this->commit}\""]); - if ($this->pull_request_id === 0) { - foreach ($this->application->build_environment_variables as $env) { - $value = escapeshellarg($env->real_value); - $this->build_args->push("--build-arg {$env->key}={$value}"); - } - } else { - foreach ($this->application->build_environment_variables_preview as $env) { - $value = escapeshellarg($env->real_value); - $this->build_args->push("--build-arg {$env->key}={$value}"); - } - } + $variables = collect($this->nixpacks_plan_json->get('variables')); + $this->build_args = $variables->map(function ($value, $key) { + return "--build-arg {$key}={$value}"; + }); } private function add_build_env_variables_to_dockerfile() diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 06aec5e495..6730dceb72 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -32,8 +32,6 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue public Server $server; - public ScheduledDatabaseBackup $backup; - public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database; public ?string $container_name = null; @@ -58,10 +56,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue public ?S3Storage $s3 = null; - public function __construct($backup) + public function __construct(public ScheduledDatabaseBackup $backup) { $this->onQueue('high'); - $this->backup = $backup; } public function handle(): void diff --git a/app/Jobs/SendMessageToPushoverJob.php b/app/Jobs/SendMessageToPushoverJob.php index 834a32b07b..e2a94cdaa0 100644 --- a/app/Jobs/SendMessageToPushoverJob.php +++ b/app/Jobs/SendMessageToPushoverJob.php @@ -44,7 +44,7 @@ public function handle(): void { $response = Http::post('https://api.pushover.net/1/messages.json', $this->message->toPayload($this->token, $this->user)); if ($response->failed()) { - throw new \RuntimeException('Pushover notification failed with ' . $response->status() . ' status code.' . $response->body()); + throw new \RuntimeException('Pushover notification failed with '.$response->status().' status code.'.$response->body()); } } } diff --git a/app/Livewire/Admin/Index.php b/app/Livewire/Admin/Index.php index 359db63298..b5f6d29294 100644 --- a/app/Livewire/Admin/Index.php +++ b/app/Livewire/Admin/Index.php @@ -21,16 +21,28 @@ class Index extends Component public function mount() { - if (! isCloud()) { + if (! isCloud() && ! isDev()) { return redirect()->route('dashboard'); } - - if (Auth::id() !== 0) { + if (Auth::id() !== 0 && ! session('impersonating')) { return redirect()->route('dashboard'); } $this->getSubscribers(); } + public function back() + { + if (session('impersonating')) { + session()->forget('impersonating'); + $user = User::find(0); + $team_to_switch_to = $user->teams->first(); + Auth::login($user); + refreshSession($team_to_switch_to); + + return redirect(request()->header('Referer')); + } + } + public function submitSearch() { if ($this->search !== '') { @@ -52,9 +64,10 @@ public function switchUser(int $user_id) if (Auth::id() !== 0) { return redirect()->route('dashboard'); } + session(['impersonating' => true]); $user = User::find($user_id); $team_to_switch_to = $user->teams->first(); - Cache::forget("team:{$user->id}"); + // Cache::forget("team:{$user->id}"); Auth::login($user); refreshSession($team_to_switch_to); diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index eadabba7c3..15eabfec57 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -9,6 +9,7 @@ use App\Models\Team; use Illuminate\Support\Collection; use Livewire\Component; +use Visus\Cuid2\Cuid2; class Index extends Component { @@ -334,6 +335,7 @@ public function createNewProject() $this->createdProject = Project::create([ 'name' => 'My first project', 'team_id' => currentTeam()->id, + 'uuid' => (string) new Cuid2, ]); $this->currentState = 'create-resource'; } @@ -346,7 +348,7 @@ public function showNewResource() 'project.resource.create', [ 'project_uuid' => $this->createdProject->uuid, - 'environment_name' => 'production', + 'environment_uuid' => $this->createdProject->environments->first()->uuid, 'server' => $this->createdServer->id, ] ); diff --git a/app/Livewire/Dashboard.php b/app/Livewire/Dashboard.php index 69ba19e401..c3cb797bff 100644 --- a/app/Livewire/Dashboard.php +++ b/app/Livewire/Dashboard.php @@ -8,6 +8,7 @@ use App\Models\Server; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Redirect; use Livewire\Component; class Dashboard extends Component @@ -49,6 +50,20 @@ public function loadDeployments() ])->sortBy('id')->groupBy('server_name')->toArray(); } + public function navigateToProject($projectUuid) + { + $project = Project::where('uuid', $projectUuid)->first(); + + if ($project && $project->environments->count() === 1) { + return Redirect::route('project.resource.index', [ + 'project_uuid' => $projectUuid, + 'environment_uuid' => $project->environments->first()->uuid, + ]); + } + + return Redirect::route('project.show', ['project_uuid' => $projectUuid]); + } + public function render() { return view('livewire.dashboard'); diff --git a/app/Livewire/Project/AddEmpty.php b/app/Livewire/Project/AddEmpty.php index fd976548ab..07873c059d 100644 --- a/app/Livewire/Project/AddEmpty.php +++ b/app/Livewire/Project/AddEmpty.php @@ -5,6 +5,7 @@ use App\Models\Project; use Livewire\Attributes\Validate; use Livewire\Component; +use Visus\Cuid2\Cuid2; class AddEmpty extends Component { @@ -22,6 +23,7 @@ public function submit() 'name' => $this->name, 'description' => $this->description, 'team_id' => currentTeam()->id, + 'uuid' => (string) new Cuid2, ]); return redirect()->route('project.show', $project->uuid); diff --git a/app/Livewire/Project/Application/Configuration.php b/app/Livewire/Project/Application/Configuration.php index 5261a08009..56e0caf750 100644 --- a/app/Livewire/Project/Application/Configuration.php +++ b/app/Livewire/Project/Application/Configuration.php @@ -3,43 +3,42 @@ namespace App\Livewire\Project\Application; use App\Models\Application; -use App\Models\Server; use Livewire\Component; class Configuration extends Component { + public $currentRoute; + public Application $application; + public $project; + + public $environment; + public $servers; protected $listeners = ['buildPackUpdated' => '$refresh']; public function mount() { + $this->currentRoute = request()->route()->getName(); $project = currentTeam() ->projects() ->select('id', 'uuid', 'team_id') ->where('uuid', request()->route('project_uuid')) ->firstOrFail(); $environment = $project->environments() - ->select('id', 'name', 'project_id') - ->where('name', request()->route('environment_name')) + ->select('id', 'uuid', 'name', 'project_id') + ->where('uuid', request()->route('environment_uuid')) ->firstOrFail(); $application = $environment->applications() ->with(['destination']) ->where('uuid', request()->route('application_uuid')) ->firstOrFail(); + $this->project = $project; + $this->environment = $environment; $this->application = $application; - if ($application->destination && $application->destination->server) { - $mainServer = $application->destination->server; - $this->servers = Server::ownedByCurrentTeam() - ->select('id', 'name') - ->where('id', '!=', $mainServer->id) - ->get(); - } else { - $this->servers = collect(); - } } public function render() diff --git a/app/Livewire/Project/Application/Deployment/Index.php b/app/Livewire/Project/Application/Deployment/Index.php index 4f761c2cf6..b847c40ef1 100644 --- a/app/Livewire/Project/Application/Deployment/Index.php +++ b/app/Livewire/Project/Application/Deployment/Index.php @@ -34,7 +34,7 @@ public function mount() if (! $project) { return redirect()->route('dashboard'); } - $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); + $environment = $project->load(['environments'])->environments->where('uuid', request()->route('environment_uuid'))->first()->load(['applications']); if (! $environment) { return redirect()->route('dashboard'); } diff --git a/app/Livewire/Project/Application/Deployment/Show.php b/app/Livewire/Project/Application/Deployment/Show.php index fadc0ed5ec..7b2ac09d36 100644 --- a/app/Livewire/Project/Application/Deployment/Show.php +++ b/app/Livewire/Project/Application/Deployment/Show.php @@ -28,7 +28,7 @@ public function mount() if (! $project) { return redirect()->route('dashboard'); } - $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); + $environment = $project->load(['environments'])->environments->where('uuid', request()->route('environment_uuid'))->first()->load(['applications']); if (! $environment) { return redirect()->route('dashboard'); } @@ -36,19 +36,11 @@ public function mount() if (! $application) { return redirect()->route('dashboard'); } - // $activity = Activity::where('properties->type_uuid', '=', $deploymentUuid)->first(); - // if (!$activity) { - // return redirect()->route('project.application.deployment.index', [ - // 'project_uuid' => $project->uuid, - // 'environment_name' => $environment->name, - // 'application_uuid' => $application->uuid, - // ]); - // } $application_deployment_queue = ApplicationDeploymentQueue::where('deployment_uuid', $deploymentUuid)->first(); if (! $application_deployment_queue) { return redirect()->route('project.application.deployment.index', [ 'project_uuid' => $project->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid, ]); } diff --git a/app/Livewire/Project/Application/DeploymentNavbar.php b/app/Livewire/Project/Application/DeploymentNavbar.php index 6a6fa24823..87b40d4dc1 100644 --- a/app/Livewire/Project/Application/DeploymentNavbar.php +++ b/app/Livewire/Project/Application/DeploymentNavbar.php @@ -23,7 +23,7 @@ class DeploymentNavbar extends Component public function mount() { - $this->application = Application::find($this->application_deployment_queue->application_id); + $this->application = Application::ownedByCurrentTeam()->find($this->application_deployment_queue->application_id); $this->server = $this->application->destination->server; $this->is_debug_enabled = $this->application->settings->is_debug_enabled; } diff --git a/app/Livewire/Project/Application/General.php b/app/Livewire/Project/Application/General.php index ff29b74e9a..d1cc3476cd 100644 --- a/app/Livewire/Project/Application/General.php +++ b/app/Livewire/Project/Application/General.php @@ -327,7 +327,7 @@ public function checkFqdns($showToaster = true) } } - public function set_redirect() + public function setRedirect() { try { $has_www = collect($this->application->fqdns)->filter(fn ($fqdn) => str($fqdn)->contains('www.'))->count(); @@ -360,10 +360,10 @@ public function submit($showToaster = true) if ($warning) { $this->dispatch('warning', __('warning.sslipdomain')); } - $this->resetDefaultLabels(); + // $this->resetDefaultLabels(); if ($this->application->isDirty('redirect')) { - $this->set_redirect(); + $this->setRedirect(); } $this->checkFqdns(); diff --git a/app/Livewire/Project/Application/Heading.php b/app/Livewire/Project/Application/Heading.php index 19a6145b78..0afc9123a2 100644 --- a/app/Livewire/Project/Application/Heading.php +++ b/app/Livewire/Project/Application/Heading.php @@ -38,7 +38,7 @@ public function mount() { $this->parameters = [ 'project_uuid' => $this->application->project()->uuid, - 'environment_name' => $this->application->environment->name, + 'environment_uuid' => $this->application->environment->uuid, 'application_uuid' => $this->application->uuid, ]; $lastDeployment = $this->application->get_last_successful_deployment(); @@ -94,7 +94,7 @@ public function deploy(bool $force_rebuild = false) 'project_uuid' => $this->parameters['project_uuid'], 'application_uuid' => $this->parameters['application_uuid'], 'deployment_uuid' => $this->deploymentUuid, - 'environment_name' => $this->parameters['environment_name'], + 'environment_uuid' => $this->parameters['environment_uuid'], ]); } @@ -136,7 +136,7 @@ public function restart() 'project_uuid' => $this->parameters['project_uuid'], 'application_uuid' => $this->parameters['application_uuid'], 'deployment_uuid' => $this->deploymentUuid, - 'environment_name' => $this->parameters['environment_name'], + 'environment_uuid' => $this->parameters['environment_uuid'], ]); } diff --git a/app/Livewire/Project/Application/Previews.php b/app/Livewire/Project/Application/Previews.php index d42bf03d78..bdf62706c3 100644 --- a/app/Livewire/Project/Application/Previews.php +++ b/app/Livewire/Project/Application/Previews.php @@ -171,7 +171,7 @@ public function deploy(int $pull_request_id, ?string $pull_request_html_url = nu 'project_uuid' => $this->parameters['project_uuid'], 'application_uuid' => $this->parameters['application_uuid'], 'deployment_uuid' => $this->deployment_uuid, - 'environment_name' => $this->parameters['environment_name'], + 'environment_uuid' => $this->parameters['environment_uuid'], ]); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Project/Application/Rollback.php b/app/Livewire/Project/Application/Rollback.php index 1e58a14586..ff5db1e08c 100644 --- a/app/Livewire/Project/Application/Rollback.php +++ b/app/Livewire/Project/Application/Rollback.php @@ -37,7 +37,7 @@ public function rollbackImage($commit) 'project_uuid' => $this->parameters['project_uuid'], 'application_uuid' => $this->parameters['application_uuid'], 'deployment_uuid' => $deployment_uuid, - 'environment_name' => $this->parameters['environment_name'], + 'environment_uuid' => $this->parameters['environment_uuid'], ]); } diff --git a/app/Livewire/Project/CloneMe.php b/app/Livewire/Project/CloneMe.php index 4d2bc65891..593355c443 100644 --- a/app/Livewire/Project/CloneMe.php +++ b/app/Livewire/Project/CloneMe.php @@ -12,7 +12,7 @@ class CloneMe extends Component { public string $project_uuid; - public string $environment_name; + public string $environment_uuid; public int $project_id; @@ -44,7 +44,7 @@ public function mount($project_uuid) { $this->project_uuid = $project_uuid; $this->project = Project::where('uuid', $project_uuid)->firstOrFail(); - $this->environment = $this->project->environments->where('name', $this->environment_name)->first(); + $this->environment = $this->project->environments->where('uuid', $this->environment_uuid)->first(); $this->project_id = $this->project->id; $this->servers = currentTeam()->servers; $this->newName = str($this->project->name.'-clone-'.(string) new Cuid2)->slug(); @@ -89,6 +89,7 @@ public function clone(string $type) if ($this->environment->name !== 'production') { $project->environments()->create([ 'name' => $this->environment->name, + 'uuid' => (string) new Cuid2, ]); } $environment = $project->environments->where('name', $this->environment->name)->first(); @@ -100,6 +101,7 @@ public function clone(string $type) $project = $this->project; $environment = $this->project->environments()->create([ 'name' => $this->newName, + 'uuid' => (string) new Cuid2, ]); } $applications = $this->environment->applications; @@ -119,7 +121,7 @@ public function clone(string $type) $environmentVaribles = $application->environment_variables()->get(); foreach ($environmentVaribles as $environmentVarible) { $newEnvironmentVariable = $environmentVarible->replicate()->fill([ - 'application_id' => $newApplication->id, + 'resourceable_id' => $newApplication->id, ]); $newEnvironmentVariable->save(); } @@ -145,17 +147,8 @@ public function clone(string $type) $environmentVaribles = $database->environment_variables()->get(); foreach ($environmentVaribles as $environmentVarible) { $payload = []; - if ($database->type() === 'standalone-postgresql') { - $payload['standalone_postgresql_id'] = $newDatabase->id; - } elseif ($database->type() === 'standalone-redis') { - $payload['standalone_redis_id'] = $newDatabase->id; - } elseif ($database->type() === 'standalone-mongodb') { - $payload['standalone_mongodb_id'] = $newDatabase->id; - } elseif ($database->type() === 'standalone-mysql') { - $payload['standalone_mysql_id'] = $newDatabase->id; - } elseif ($database->type() === 'standalone-mariadb') { - $payload['standalone_mariadb_id'] = $newDatabase->id; - } + $payload['resourceable_id'] = $newDatabase->id; + $payload['resourceable_type'] = $newDatabase->getMorphClass(); $newEnvironmentVariable = $environmentVarible->replicate()->fill($payload); $newEnvironmentVariable->save(); } @@ -183,7 +176,7 @@ public function clone(string $type) return redirect()->route('project.resource.index', [ 'project_uuid' => $project->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, ]); } catch (\Exception $e) { return handleError($e, $this); diff --git a/app/Livewire/Project/Database/Backup/Execution.php b/app/Livewire/Project/Database/Backup/Execution.php index 5640916595..4ac3b2e2c5 100644 --- a/app/Livewire/Project/Database/Backup/Execution.php +++ b/app/Livewire/Project/Database/Backup/Execution.php @@ -22,7 +22,7 @@ public function mount() if (! $project) { return redirect()->route('dashboard'); } - $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); + $environment = $project->load(['environments'])->environments->where('uuid', request()->route('environment_uuid'))->first()->load(['applications']); if (! $environment) { return redirect()->route('dashboard'); } diff --git a/app/Livewire/Project/Database/Backup/Index.php b/app/Livewire/Project/Database/Backup/Index.php index 9ff2f48d5f..2df32ec7bf 100644 --- a/app/Livewire/Project/Database/Backup/Index.php +++ b/app/Livewire/Project/Database/Backup/Index.php @@ -14,7 +14,7 @@ public function mount() if (! $project) { return redirect()->route('dashboard'); } - $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); + $environment = $project->load(['environments'])->environments->where('uuid', request()->route('environment_uuid'))->first()->load(['applications']); if (! $environment) { return redirect()->route('dashboard'); } @@ -31,7 +31,7 @@ public function mount() ) { return redirect()->route('project.database.configuration', [ 'project_uuid' => $project->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid, ]); } diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php index b3a54f0abd..0dea0496ca 100644 --- a/app/Livewire/Project/Database/BackupEdit.php +++ b/app/Livewire/Project/Database/BackupEdit.php @@ -40,6 +40,9 @@ class BackupEdit extends Component #[Validate(['required', 'string'])] public string $frequency = ''; + #[Validate(['string'])] + public string $timezone = ''; + #[Validate(['required', 'integer', 'min:1'])] public int $numberOfBackupsLocally = 1; @@ -68,7 +71,6 @@ public function mount() public function syncData(bool $toModel = false) { if ($toModel) { - $this->customValidate(); $this->backup->enabled = $this->backupEnabled; $this->backup->frequency = $this->frequency; $this->backup->number_of_backups_locally = $this->numberOfBackupsLocally; @@ -76,10 +78,12 @@ public function syncData(bool $toModel = false) $this->backup->s3_storage_id = $this->s3StorageId; $this->backup->databases_to_backup = $this->databasesToBackup; $this->backup->dump_all = $this->dumpAll; + $this->customValidate(); $this->backup->save(); } else { $this->backupEnabled = $this->backup->enabled; $this->frequency = $this->backup->frequency; + $this->timezone = data_get($this->backup->server(), 'settings.server_timezone', 'Instance timezone'); $this->numberOfBackupsLocally = $this->backup->number_of_backups_locally; $this->saveS3 = $this->backup->save_s3; $this->s3StorageId = $this->backup->s3_storage_id; @@ -183,12 +187,12 @@ private function deleteAssociatedBackupsLocally() private function deleteAssociatedBackupsS3() { - //Add function to delete backups from S3 + // Add function to delete backups from S3 } private function deleteAssociatedBackupsSftp() { - //Add function to delete backups from SFTP + // Add function to delete backups from SFTP } private function deleteEmptyBackupFolder($folderPath, $server) diff --git a/app/Livewire/Project/Database/BackupNow.php b/app/Livewire/Project/Database/BackupNow.php index 9c9c175e26..3cd3605623 100644 --- a/app/Livewire/Project/Database/BackupNow.php +++ b/app/Livewire/Project/Database/BackupNow.php @@ -9,11 +9,9 @@ class BackupNow extends Component { public $backup; - public function backup_now() + public function backupNow() { - dispatch(new DatabaseBackupJob( - backup: $this->backup - )); + DatabaseBackupJob::dispatch($this->backup); $this->dispatch('success', 'Backup queued. It will be available in a few minutes.'); } } diff --git a/app/Livewire/Project/Database/Configuration.php b/app/Livewire/Project/Database/Configuration.php index e14b27cf61..938abba549 100644 --- a/app/Livewire/Project/Database/Configuration.php +++ b/app/Livewire/Project/Database/Configuration.php @@ -6,23 +6,34 @@ class Configuration extends Component { + public $currentRoute; + public $database; + public $project; + + public $environment; + public function mount() { - $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (! $project) { - return redirect()->route('dashboard'); - } - $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first()->load(['applications']); - if (! $environment) { - return redirect()->route('dashboard'); - } - $database = $environment->databases()->where('uuid', request()->route('database_uuid'))->first(); - if (! $database) { - return redirect()->route('dashboard'); - } + $this->currentRoute = request()->route()->getName(); + + $project = currentTeam() + ->projects() + ->select('id', 'uuid', 'team_id') + ->where('uuid', request()->route('project_uuid')) + ->firstOrFail(); + $environment = $project->environments() + ->select('id', 'name', 'project_id', 'uuid') + ->where('uuid', request()->route('environment_uuid')) + ->firstOrFail(); + $database = $environment->databases() + ->where('uuid', request()->route('database_uuid')) + ->firstOrFail(); + $this->database = $database; + $this->project = $project; + $this->environment = $environment; if (str($this->database->status)->startsWith('running') && is_null($this->database->config_hash)) { $this->database->isConfigurationChanged(true); $this->dispatch('configurationChanged'); diff --git a/app/Livewire/Project/Database/Import.php b/app/Livewire/Project/Database/Import.php index 062f454b14..dc330b3af8 100644 --- a/app/Livewire/Project/Database/Import.php +++ b/app/Livewire/Project/Database/Import.php @@ -37,6 +37,12 @@ class Import extends Component public array $importCommands = []; + public bool $dumpAll = false; + + public string $restoreCommandText = ''; + + public string $customLocation = ''; + public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB'; public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE'; @@ -56,10 +62,62 @@ public function getListeners() public function mount() { + if (isDev()) { + $this->customLocation = '/data/coolify/pg-dump-all-1736245863.gz'; + } $this->parameters = get_route_parameters(); $this->getContainers(); } + public function updatedDumpAll($value) + { + switch ($this->resource->getMorphClass()) { + case \App\Models\StandaloneMariadb::class: + if ($value === true) { + $this->mariadbRestoreCommand = <<<'EOD' +for pid in $(mariadb -u root -p$MARIADB_ROOT_PASSWORD -N -e "SELECT id FROM information_schema.processlist WHERE user != 'root';"); do + mariadb -u root -p$MARIADB_ROOT_PASSWORD -e "KILL $pid" 2>/dev/null || true +done && \ +mariadb -u root -p$MARIADB_ROOT_PASSWORD -N -e "SELECT CONCAT('DROP DATABASE IF EXISTS \`',schema_name,'\`;') FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema','mysql','performance_schema','sys');" | mariadb -u root -p$MARIADB_ROOT_PASSWORD && \ +mariadb -u root -p$MARIADB_ROOT_PASSWORD -e "CREATE DATABASE IF NOT EXISTS \`default\`;" && \ +(gunzip -cf $tmpPath 2>/dev/null || cat $tmpPath) | sed -e '/^CREATE DATABASE/d' -e '/^USE \`mysql\`/d' | mariadb -u root -p$MARIADB_ROOT_PASSWORD default +EOD; + $this->restoreCommandText = $this->mariadbRestoreCommand.' && (gunzip -cf 2>/dev/null || cat ) | mariadb -u root -p$MARIADB_ROOT_PASSWORD default'; + } else { + $this->mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE'; + } + break; + case \App\Models\StandaloneMysql::class: + if ($value === true) { + $this->mysqlRestoreCommand = <<<'EOD' +for pid in $(mysql -u root -p$MYSQL_ROOT_PASSWORD -N -e "SELECT id FROM information_schema.processlist WHERE user != 'root';"); do + mysql -u root -p$MYSQL_ROOT_PASSWORD -e "KILL $pid" 2>/dev/null || true +done && \ +mysql -u root -p$MYSQL_ROOT_PASSWORD -N -e "SELECT CONCAT('DROP DATABASE IF EXISTS \`',schema_name,'\`;') FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema','mysql','performance_schema','sys');" | mysql -u root -p$MYSQL_ROOT_PASSWORD && \ +mysql -u root -p$MYSQL_ROOT_PASSWORD -e "CREATE DATABASE IF NOT EXISTS \`default\`;" && \ +(gunzip -cf $tmpPath 2>/dev/null || cat $tmpPath) | sed -e '/^CREATE DATABASE/d' -e '/^USE \`mysql\`/d' | mysql -u root -p$MYSQL_ROOT_PASSWORD default +EOD; + $this->restoreCommandText = $this->mysqlRestoreCommand.' && (gunzip -cf 2>/dev/null || cat ) | mysql -u root -p$MYSQL_ROOT_PASSWORD default'; + } else { + $this->mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE'; + } + break; + case \App\Models\StandalonePostgresql::class: + if ($value === true) { + $this->postgresqlRestoreCommand = <<<'EOD' +psql -U $POSTGRES_USER -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname IS NOT NULL AND pid <> pg_backend_pid()" && \ +psql -U $POSTGRES_USER -t -c "SELECT datname FROM pg_database WHERE NOT datistemplate" | xargs -I {} dropdb -U $POSTGRES_USER --if-exists {} && \ +createdb -U $POSTGRES_USER postgres +EOD; + $this->restoreCommandText = $this->postgresqlRestoreCommand.' && (gunzip -cf 2>/dev/null || cat ) | psql -U $POSTGRES_USER postgres'; + } else { + $this->postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB'; + } + break; + } + + } + public function getContainers() { $this->containers = collect(); @@ -87,6 +145,24 @@ public function getContainers() } } + public function checkFile() + { + if (filled($this->customLocation)) { + try { + $result = instant_remote_process(["ls -l {$this->customLocation}"], $this->server, throwError: false); + if (blank($result)) { + $this->dispatch('error', 'The file does not exist or has been deleted.'); + + return; + } + $this->filename = $this->customLocation; + $this->dispatch('success', 'The file exists.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + } + public function runImport() { if ($this->filename === '') { @@ -95,46 +171,83 @@ public function runImport() return; } try { - $uploadedFilename = "upload/{$this->resource->uuid}/restore"; - $path = Storage::path($uploadedFilename); - if (! Storage::exists($uploadedFilename)) { - $this->dispatch('error', 'The file does not exist or has been deleted.'); - - return; + $this->importCommands = []; + if (filled($this->customLocation)) { + $backupFileName = '/tmp/restore_'.$this->resource->uuid; + $this->importCommands[] = "docker cp {$this->customLocation} {$this->container}:{$backupFileName}"; + $tmpPath = $backupFileName; + } else { + $backupFileName = "upload/{$this->resource->uuid}/restore"; + $path = Storage::path($backupFileName); + if (! Storage::exists($backupFileName)) { + $this->dispatch('error', 'The file does not exist or has been deleted.'); + + return; + } + $tmpPath = '/tmp/'.basename($backupFileName).'_'.$this->resource->uuid; + instant_scp($path, $tmpPath, $this->server); + Storage::delete($backupFileName); + $this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}"; } - $tmpPath = '/tmp/'.basename($uploadedFilename); - instant_scp($path, $tmpPath, $this->server); - Storage::delete($uploadedFilename); - $this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}"; + + // Copy the restore command to a script file + $scriptPath = "/tmp/restore_{$this->resource->uuid}.sh"; switch ($this->resource->getMorphClass()) { case \App\Models\StandaloneMariadb::class: - $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mariadbRestoreCommand} < {$tmpPath}'"; - $this->importCommands[] = "rm {$tmpPath}"; + $restoreCommand = $this->mariadbRestoreCommand; + if ($this->dumpAll) { + $restoreCommand .= " && (gunzip -cf {$tmpPath} 2>/dev/null || cat {$tmpPath}) | mariadb -u root -p\$MARIADB_ROOT_PASSWORD"; + } else { + $restoreCommand .= " < {$tmpPath}"; + } break; case \App\Models\StandaloneMysql::class: - $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mysqlRestoreCommand} < {$tmpPath}'"; - $this->importCommands[] = "rm {$tmpPath}"; + $restoreCommand = $this->mysqlRestoreCommand; + if ($this->dumpAll) { + $restoreCommand .= " && (gunzip -cf {$tmpPath} 2>/dev/null || cat {$tmpPath}) | mysql -u root -p\$MYSQL_ROOT_PASSWORD"; + } else { + $restoreCommand .= " < {$tmpPath}"; + } break; case \App\Models\StandalonePostgresql::class: - $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->postgresqlRestoreCommand} {$tmpPath}'"; - $this->importCommands[] = "rm {$tmpPath}"; + $restoreCommand = $this->postgresqlRestoreCommand; + if ($this->dumpAll) { + $restoreCommand .= " && (gunzip -cf {$tmpPath} 2>/dev/null || cat {$tmpPath}) | psql -U \$POSTGRES_USER postgres"; + } else { + $restoreCommand .= " {$tmpPath}"; + } break; case \App\Models\StandaloneMongodb::class: - $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mongodbRestoreCommand}{$tmpPath}'"; - $this->importCommands[] = "rm {$tmpPath}"; + $restoreCommand = $this->mongodbRestoreCommand; + if ($this->dumpAll === false) { + $restoreCommand .= " {$tmpPath}"; + } break; } - $this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$tmpPath}'"; + $restoreCommandBase64 = base64_encode($restoreCommand); + $this->importCommands[] = "echo \"{$restoreCommandBase64}\" | base64 -d > {$scriptPath}"; + $this->importCommands[] = "chmod +x {$scriptPath}"; + $this->importCommands[] = "docker cp {$scriptPath} {$this->container}:{$scriptPath}"; + + $this->importCommands[] = "docker exec {$this->container} sh -c '{$scriptPath}'"; $this->importCommands[] = "docker exec {$this->container} sh -c 'echo \"Import finished with exit code $?\"'"; if (! empty($this->importCommands)) { - $activity = remote_process($this->importCommands, $this->server, ignore_errors: true); + $activity = remote_process($this->importCommands, $this->server, ignore_errors: true, callEventOnFinish: 'RestoreJobFinished', callEventData: [ + 'scriptPath' => $scriptPath, + 'tmpPath' => $tmpPath, + 'container' => $this->container, + 'serverId' => $this->server->id, + ]); $this->dispatch('activityMonitor', $activity->id); } } catch (\Throwable $e) { return handleError($e, $this); + } finally { + $this->filename = null; + $this->importCommands = []; } } } diff --git a/app/Livewire/Project/Database/Postgresql/General.php b/app/Livewire/Project/Database/Postgresql/General.php index c12fa49f34..88dd5c1a86 100644 --- a/app/Livewire/Project/Database/Postgresql/General.php +++ b/app/Livewire/Project/Database/Postgresql/General.php @@ -9,8 +9,6 @@ use Exception; use Livewire\Component; -use function Aws\filter; - class General extends Component { public StandalonePostgresql $database; @@ -126,10 +124,52 @@ public function instantSave() public function save_init_script($script) { - $this->database->init_scripts = filter($this->database->init_scripts, fn ($s) => $s['filename'] !== $script['filename']); - $this->database->init_scripts = array_merge($this->database->init_scripts, [$script]); + $initScripts = collect($this->database->init_scripts ?? []); + + $existingScript = $initScripts->firstWhere('filename', $script['filename']); + $oldScript = $initScripts->firstWhere('index', $script['index']); + + if ($existingScript && $existingScript['index'] !== $script['index']) { + $this->dispatch('error', 'A script with this filename already exists.'); + + return; + } + + $container_name = $this->database->uuid; + $configuration_dir = database_configuration_dir().'/'.$container_name; + + if ($oldScript && $oldScript['filename'] !== $script['filename']) { + $old_file_path = "$configuration_dir/docker-entrypoint-initdb.d/{$oldScript['filename']}"; + $delete_command = "rm -f $old_file_path"; + try { + instant_remote_process([$delete_command], $this->server); + } catch (\Exception $e) { + $this->dispatch('error', 'Failed to remove old init script from server: '.$e->getMessage()); + + return; + } + } + + $index = $initScripts->search(function ($item) use ($script) { + return $item['index'] === $script['index']; + }); + + if ($index !== false) { + $initScripts[$index] = $script; + } else { + $initScripts->push($script); + } + + $this->database->init_scripts = $initScripts->values() + ->map(function ($item, $index) { + $item['index'] = $index; + + return $item; + }) + ->all(); + $this->database->save(); - $this->dispatch('success', 'Init script saved.'); + $this->dispatch('success', 'Init script saved and updated.'); } public function delete_init_script($script) @@ -137,12 +177,32 @@ public function delete_init_script($script) $collection = collect($this->database->init_scripts); $found = $collection->firstWhere('filename', $script['filename']); if ($found) { - $this->database->init_scripts = $collection->filter(fn ($s) => $s['filename'] !== $script['filename'])->toArray(); + $container_name = $this->database->uuid; + $configuration_dir = database_configuration_dir().'/'.$container_name; + $file_path = "$configuration_dir/docker-entrypoint-initdb.d/{$script['filename']}"; + + $command = "rm -f $file_path"; + try { + instant_remote_process([$command], $this->server); + } catch (\Exception $e) { + $this->dispatch('error', 'Failed to remove init script from server: '.$e->getMessage()); + + return; + } + + $updatedScripts = $collection->filter(fn ($s) => $s['filename'] !== $script['filename']) + ->values() + ->map(function ($item, $index) { + $item['index'] = $index; + + return $item; + }) + ->all(); + + $this->database->init_scripts = $updatedScripts; $this->database->save(); $this->refresh(); - $this->dispatch('success', 'Init script deleted.'); - - return; + $this->dispatch('success', 'Init script deleted from the database and the server.'); } } diff --git a/app/Livewire/Project/EnvironmentEdit.php b/app/Livewire/Project/EnvironmentEdit.php index f48220b3dd..e98b088ecf 100644 --- a/app/Livewire/Project/EnvironmentEdit.php +++ b/app/Livewire/Project/EnvironmentEdit.php @@ -23,11 +23,11 @@ class EnvironmentEdit extends Component #[Validate(['nullable', 'string', 'max:255'])] public ?string $description = null; - public function mount(string $project_uuid, string $environment_name) + public function mount(string $project_uuid, string $environment_uuid) { try { $this->project = Project::ownedByCurrentTeam()->where('uuid', $project_uuid)->firstOrFail(); - $this->environment = $this->project->environments()->where('name', $environment_name)->firstOrFail(); + $this->environment = $this->project->environments()->where('uuid', $environment_uuid)->firstOrFail(); $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); @@ -52,7 +52,10 @@ public function submit() { try { $this->syncData(true); - $this->redirectRoute('project.environment.edit', ['environment_name' => $this->environment->name, 'project_uuid' => $this->project->uuid]); + $this->redirectRoute('project.environment.edit', [ + 'environment_uuid' => $this->environment->uuid, + 'project_uuid' => $this->project->uuid, + ]); } catch (\Throwable $e) { return handleError($e, $this); } diff --git a/app/Livewire/Project/Index.php b/app/Livewire/Project/Index.php index f8eb838be6..06bf88219b 100644 --- a/app/Livewire/Project/Index.php +++ b/app/Livewire/Project/Index.php @@ -5,6 +5,7 @@ use App\Models\PrivateKey; use App\Models\Project; use App\Models\Server; +use Illuminate\Support\Facades\Redirect; use Livewire\Component; class Index extends Component @@ -30,4 +31,18 @@ public function render() { return view('livewire.project.index'); } + + public function navigateToProject($projectUuid) + { + $project = Project::where('uuid', $projectUuid)->first(); + + if ($project && $project->environments->count() === 1) { + return Redirect::route('project.resource.index', [ + 'project_uuid' => $projectUuid, + 'environment_uuid' => $project->environments->first()->uuid, + ]); + } + + return Redirect::route('project.show', ['project_uuid' => $projectUuid]); + } } diff --git a/app/Livewire/Project/New/DockerCompose.php b/app/Livewire/Project/New/DockerCompose.php index 199a20cf6e..27975eaa2b 100644 --- a/app/Livewire/Project/New/DockerCompose.php +++ b/app/Livewire/Project/New/DockerCompose.php @@ -59,7 +59,7 @@ public function submit() } $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); - $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); + $environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first(); $destination_uuid = $this->query['destination']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); @@ -87,7 +87,8 @@ public function submit() 'value' => $variable, 'is_build_time' => false, 'is_preview' => false, - 'service_id' => $service->id, + 'resourceable_id' => $service->id, + 'resourceable_type' => $service->getMorphClass(), ]); } $service->name = "service-$service->uuid"; @@ -96,7 +97,7 @@ public function submit() return redirect()->route('project.service.configuration', [ 'service_uuid' => $service->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid, ]); } catch (\Throwable $e) { diff --git a/app/Livewire/Project/New/DockerImage.php b/app/Livewire/Project/New/DockerImage.php index 417fb2ea02..9429244377 100644 --- a/app/Livewire/Project/New/DockerImage.php +++ b/app/Livewire/Project/New/DockerImage.php @@ -45,7 +45,7 @@ public function submit() $destination_class = $destination->getMorphClass(); $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); - $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); + $environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first(); $application = Application::create([ 'name' => 'docker-image-'.new Cuid2, 'repository_project_id' => 0, @@ -69,7 +69,7 @@ public function submit() return redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid, ]); } diff --git a/app/Livewire/Project/New/EmptyProject.php b/app/Livewire/Project/New/EmptyProject.php index 28249b442a..54cfc4b4d8 100644 --- a/app/Livewire/Project/New/EmptyProject.php +++ b/app/Livewire/Project/New/EmptyProject.php @@ -4,6 +4,7 @@ use App\Models\Project; use Livewire\Component; +use Visus\Cuid2\Cuid2; class EmptyProject extends Component { @@ -12,8 +13,9 @@ public function createEmptyProject() $project = Project::create([ 'name' => generate_random_name(), 'team_id' => currentTeam()->id, + 'uuid' => (string) new Cuid2, ]); - return redirect()->route('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production']); + return redirect()->route('project.show', ['project_uuid' => $project->uuid, 'environment_uuid' => $project->environments->first()->uuid]); } } diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index 2f4f5a25c5..370d005557 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -177,7 +177,7 @@ public function submit() $destination_class = $destination->getMorphClass(); $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); - $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); + $environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first(); $application = Application::create([ 'name' => generate_application_name($this->selected_repository_owner.'/'.$this->selected_repository_repo, $this->selected_branch_name), @@ -211,7 +211,7 @@ public function submit() return redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid, ]); } catch (\Throwable $e) { diff --git a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php index b46c4a7943..01b0c9ae85 100644 --- a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php +++ b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php @@ -136,7 +136,7 @@ public function submit() $this->get_git_source(); $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); - $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); + $environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first(); if ($this->git_source === 'other') { $application_init = [ 'name' => generate_random_name(), @@ -184,7 +184,7 @@ public function submit() return redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid, ]); } catch (\Throwable $e) { diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index bd35dccef6..2f2331fc00 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -225,7 +225,7 @@ public function submit() $this->validate(); $destination_uuid = $this->query['destination']; $project_uuid = $this->parameters['project_uuid']; - $environment_name = $this->parameters['environment_name']; + $environment_uuid = $this->parameters['environment_uuid']; $destination = StandaloneDocker::where('uuid', $destination_uuid)->first(); if (! $destination) { @@ -237,7 +237,7 @@ public function submit() $destination_class = $destination->getMorphClass(); $project = Project::where('uuid', $project_uuid)->first(); - $environment = $project->load(['environments'])->environments->where('name', $environment_name)->first(); + $environment = $project->load(['environments'])->environments->where('uuid', $environment_uuid)->first(); if ($this->build_pack === 'dockercompose' && isDev() && $this->new_compose_services) { $server = $destination->server; @@ -260,7 +260,7 @@ public function submit() return redirect()->route('project.service.configuration', [ 'service_uuid' => $service->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid, ]); @@ -319,7 +319,7 @@ public function submit() return redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid, ]); } catch (\Throwable $e) { diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php index 0b6d075a4c..b645a89150 100644 --- a/app/Livewire/Project/New/Select.php +++ b/app/Livewire/Project/New/Select.php @@ -23,6 +23,8 @@ class Select extends Component public Collection|null|Server $servers; + public bool $onlyBuildServerAvailable = false; + public ?Collection $standaloneDockers; public ?Collection $swarmDockers; @@ -61,7 +63,7 @@ public function mount() } $projectUuid = data_get($this->parameters, 'project_uuid'); $this->environments = Project::whereUuid($projectUuid)->first()->environments; - $this->selectedEnvironment = data_get($this->parameters, 'environment_name'); + $this->selectedEnvironment = data_get($this->parameters, 'environment_uuid'); } public function render() @@ -73,20 +75,10 @@ public function updatedSelectedEnvironment() { return redirect()->route('project.resource.create', [ 'project_uuid' => $this->parameters['project_uuid'], - 'environment_name' => $this->selectedEnvironment, + 'environment_uuid' => $this->selectedEnvironment, ]); } - // public function addExistingPostgresql() - // { - // try { - // instantCommand("psql {$this->existingPostgresqlUrl} -c 'SELECT 1'"); - // $this->dispatch('success', 'Successfully connected to the database.'); - // } catch (\Throwable $e) { - // return handleError($e, $this); - // } - // } - public function loadServices() { $services = get_service_templates(true); @@ -308,7 +300,7 @@ public function setPostgresqlType(string $type) return redirect()->route('project.resource.create', [ 'project_uuid' => $this->parameters['project_uuid'], - 'environment_name' => $this->parameters['environment_name'], + 'environment_uuid' => $this->parameters['environment_uuid'], 'type' => $this->type, 'destination' => $this->destination_uuid, 'server_id' => $this->server_id, @@ -323,7 +315,7 @@ public function whatToDoNext() } else { return redirect()->route('project.resource.create', [ 'project_uuid' => $this->parameters['project_uuid'], - 'environment_name' => $this->parameters['environment_name'], + 'environment_uuid' => $this->parameters['environment_uuid'], 'type' => $this->type, 'destination' => $this->destination_uuid, 'server_id' => $this->server_id, @@ -335,5 +327,11 @@ public function loadServers() { $this->servers = Server::isUsable()->get()->sortBy('name'); $this->allServers = $this->servers; + + if ($this->allServers && $this->allServers->isNotEmpty()) { + $this->onlyBuildServerAvailable = $this->allServers->every(function ($server) { + return $server->isBuildServer(); + }); + } } } diff --git a/app/Livewire/Project/New/SimpleDockerfile.php b/app/Livewire/Project/New/SimpleDockerfile.php index 3c7f42329e..c3ed6039a0 100644 --- a/app/Livewire/Project/New/SimpleDockerfile.php +++ b/app/Livewire/Project/New/SimpleDockerfile.php @@ -46,7 +46,7 @@ public function submit() $destination_class = $destination->getMorphClass(); $project = Project::where('uuid', $this->parameters['project_uuid'])->first(); - $environment = $project->load(['environments'])->environments->where('name', $this->parameters['environment_name'])->first(); + $environment = $project->load(['environments'])->environments->where('uuid', $this->parameters['environment_uuid'])->first(); $port = get_port_from_dockerfile($this->dockerfile); if (! $port) { @@ -78,7 +78,7 @@ public function submit() return redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid, ]); } diff --git a/app/Livewire/Project/Resource/Create.php b/app/Livewire/Project/Resource/Create.php index 9266a57fc8..0faf0b8da2 100644 --- a/app/Livewire/Project/Resource/Create.php +++ b/app/Livewire/Project/Resource/Create.php @@ -25,7 +25,7 @@ public function mount() return redirect()->route('dashboard'); } $this->project = $project; - $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first(); + $environment = $project->load(['environments'])->environments->where('uuid', request()->route('environment_uuid'))->first(); if (! $environment) { return redirect()->route('dashboard'); } @@ -57,7 +57,7 @@ public function mount() return redirect()->route('project.database.configuration', [ 'project_uuid' => $project->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid, ]); } @@ -95,7 +95,8 @@ public function mount() EnvironmentVariable::create([ 'key' => $key, 'value' => $value, - 'service_id' => $service->id, + 'resourceable_id' => $service->id, + 'resourceable_type' => $service->getMorphClass(), 'is_build_time' => false, 'is_preview' => false, ]); @@ -106,7 +107,7 @@ public function mount() return redirect()->route('project.service.configuration', [ 'service_uuid' => $service->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid, ]); } diff --git a/app/Livewire/Project/Resource/EnvironmentSelect.php b/app/Livewire/Project/Resource/EnvironmentSelect.php index efb1b6ca23..a38d750daa 100644 --- a/app/Livewire/Project/Resource/EnvironmentSelect.php +++ b/app/Livewire/Project/Resource/EnvironmentSelect.php @@ -15,7 +15,7 @@ class EnvironmentSelect extends Component public function mount() { - $this->selectedEnvironment = request()->route('environment_name'); + $this->selectedEnvironment = request()->route('environment_uuid'); $this->project_uuid = request()->route('project_uuid'); } @@ -28,7 +28,7 @@ public function updatedSelectedEnvironment($value) } else { return redirect()->route('project.resource.index', [ 'project_uuid' => $this->project_uuid, - 'environment_name' => $value, + 'environment_uuid' => $value, ]); } } diff --git a/app/Livewire/Project/Resource/Index.php b/app/Livewire/Project/Resource/Index.php index 0c2ea802a6..2b199dcfd6 100644 --- a/app/Livewire/Project/Resource/Index.php +++ b/app/Livewire/Project/Resource/Index.php @@ -4,6 +4,7 @@ use App\Models\Environment; use App\Models\Project; +use Illuminate\Support\Collection; use Livewire\Component; class Index extends Component @@ -12,39 +13,42 @@ class Index extends Component public Environment $environment; - public $applications = []; + public Collection $applications; - public $postgresqls = []; + public Collection $postgresqls; - public $redis = []; + public Collection $redis; - public $mongodbs = []; + public Collection $mongodbs; - public $mysqls = []; + public Collection $mysqls; - public $mariadbs = []; + public Collection $mariadbs; - public $keydbs = []; + public Collection $keydbs; - public $dragonflies = []; + public Collection $dragonflies; - public $clickhouses = []; + public Collection $clickhouses; - public $services = []; + public Collection $services; public array $parameters; public function mount() { + $this->applications = $this->postgresqls = $this->redis = $this->mongodbs = $this->mysqls = $this->mariadbs = $this->keydbs = $this->dragonflies = $this->clickhouses = $this->services = collect(); $this->parameters = get_route_parameters(); - $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); - if (! $project) { - return redirect()->route('dashboard'); - } - $environment = $project->load(['environments'])->environments->where('name', request()->route('environment_name'))->first(); - if (! $environment) { - return redirect()->route('dashboard'); - } + $project = currentTeam() + ->projects() + ->select('id', 'uuid', 'team_id', 'name') + ->where('uuid', request()->route('project_uuid')) + ->firstOrFail(); + $environment = $project->environments() + ->select('id', 'uuid', 'name', 'project_id') + ->where('uuid', request()->route('environment_uuid')) + ->firstOrFail(); + $this->project = $project; $this->environment = $environment->loadCount([ 'applications', @@ -69,9 +73,9 @@ public function mount() ])->get()->sortBy('name'); $this->applications = $this->applications->map(function ($application) { $application->hrefLink = route('project.application.configuration', [ - 'project_uuid' => $this->project->uuid, - 'application_uuid' => $application->uuid, - 'environment_name' => $this->environment->name, + 'project_uuid' => data_get($application, 'environment.project.uuid'), + 'environment_uuid' => data_get($application, 'environment.uuid'), + 'application_uuid' => data_get($application, 'uuid'), ]); return $application; @@ -89,14 +93,6 @@ public function mount() 'clickhouses' => 'clickhouses', ]; - // Load all server-related data first to prevent duplicate queries - $serverData = $this->environment->applications() - ->with(['destination.server.settings']) - ->get() - ->pluck('destination.server') - ->filter() - ->unique('id'); - foreach ($databaseTypes as $property => $relation) { $this->{$property} = $this->environment->{$relation}()->with([ 'tags', @@ -106,7 +102,7 @@ public function mount() $db->hrefLink = route('project.database.configuration', [ 'project_uuid' => $this->project->uuid, 'database_uuid' => $db->uuid, - 'environment_name' => $this->environment->name, + 'environment_uuid' => data_get($this->environment, 'uuid'), ]); return $db; @@ -120,9 +116,9 @@ public function mount() ])->get()->sortBy('name'); $this->services = $this->services->map(function ($service) { $service->hrefLink = route('project.service.configuration', [ - 'project_uuid' => $this->project->uuid, - 'service_uuid' => $service->uuid, - 'environment_name' => $this->environment->name, + 'project_uuid' => data_get($service, 'environment.project.uuid'), + 'environment_uuid' => data_get($service, 'environment.uuid'), + 'service_uuid' => data_get($service, 'uuid'), ]); return $service; diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php index 319ead3619..4a5e8627fa 100644 --- a/app/Livewire/Project/Service/Configuration.php +++ b/app/Livewire/Project/Service/Configuration.php @@ -9,16 +9,22 @@ class Configuration extends Component { + public $currentRoute; + + public $project; + + public $environment; + public ?Service $service = null; public $applications; public $databases; - public array $parameters; - public array $query; + public array $parameters; + public function getListeners() { $userId = Auth::id(); @@ -38,11 +44,21 @@ public function render() public function mount() { $this->parameters = get_route_parameters(); + $this->currentRoute = request()->route()->getName(); $this->query = request()->query(); - $this->service = Service::whereUuid($this->parameters['service_uuid'])->first(); - if (! $this->service) { - return redirect()->route('dashboard'); - } + $project = currentTeam() + ->projects() + ->select('id', 'uuid', 'team_id') + ->where('uuid', request()->route('project_uuid')) + ->firstOrFail(); + $environment = $project->environments() + ->select('id', 'uuid', 'name', 'project_id') + ->where('uuid', request()->route('environment_uuid')) + ->firstOrFail(); + $this->service = $environment->services()->whereUuid(request()->route('service_uuid'))->firstOrFail(); + + $this->project = $project; + $this->environment = $environment; $this->applications = $this->service->applications->sort(); $this->databases = $this->service->databases->sort(); } diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php index a0b4ac2c45..7da48f9fb4 100644 --- a/app/Livewire/Project/Shared/Danger.php +++ b/app/Livewire/Project/Shared/Danger.php @@ -20,7 +20,7 @@ class Danger extends Component public $projectUuid; - public $environmentName; + public $environmentUuid; public bool $delete_configurations = true; @@ -39,7 +39,7 @@ public function mount() $parameters = get_route_parameters(); $this->modalId = new Cuid2; $this->projectUuid = data_get($parameters, 'project_uuid'); - $this->environmentName = data_get($parameters, 'environment_name'); + $this->environmentUuid = data_get($parameters, 'environment_uuid'); if ($this->resource === null) { if (isset($parameters['service_uuid'])) { @@ -107,7 +107,7 @@ public function delete($password) return redirect()->route('project.resource.index', [ 'project_uuid' => $this->projectUuid, - 'environment_name' => $this->environmentName, + 'environment_uuid' => $this->environmentUuid, ]); } catch (\Throwable $e) { return handleError($e, $this); diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php index c305e817cf..1759fe08ac 100644 --- a/app/Livewire/Project/Shared/Destination.php +++ b/app/Livewire/Project/Shared/Destination.php @@ -8,6 +8,7 @@ use App\Models\InstanceSettings; use App\Models\Server; use App\Models\StandaloneDocker; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Livewire\Component; @@ -17,7 +18,7 @@ class Destination extends Component { public $resource; - public $networks = []; + public Collection $networks; public function getListeners() { @@ -30,6 +31,7 @@ public function getListeners() public function mount() { + $this->networks = collect([]); $this->loadData(); } @@ -55,38 +57,46 @@ public function loadData() } } - public function stop(int $server_id) + public function stop($serverId) { - $server = Server::find($server_id); - StopApplicationOneServer::run($this->resource, $server); - $this->refreshServers(); + try { + $server = Server::ownedByCurrentTeam()->findOrFail($serverId); + StopApplicationOneServer::run($this->resource, $server); + $this->refreshServers(); + } catch (\Exception $e) { + return handleError($e, $this); + } } public function redeploy(int $network_id, int $server_id) { - if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) { - $this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.
More information here: documentation'); + try { + if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) { + $this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.
More information here: documentation'); - return; + return; + } + $deployment_uuid = new Cuid2; + $server = Server::ownedByCurrentTeam()->findOrFail($server_id); + $destination = $server->standaloneDockers->where('id', $network_id)->firstOrFail(); + queue_application_deployment( + deployment_uuid: $deployment_uuid, + application: $this->resource, + server: $server, + destination: $destination, + only_this_server: true, + no_questions_asked: true, + ); + + return redirect()->route('project.application.deployment.show', [ + 'project_uuid' => data_get($this->resource, 'environment.project.uuid'), + 'application_uuid' => data_get($this->resource, 'uuid'), + 'deployment_uuid' => $deployment_uuid, + 'environment_uuid' => data_get($this->resource, 'environment.uuid'), + ]); + } catch (\Exception $e) { + return handleError($e, $this); } - $deployment_uuid = new Cuid2; - $server = Server::find($server_id); - $destination = StandaloneDocker::find($network_id); - queue_application_deployment( - deployment_uuid: $deployment_uuid, - application: $this->resource, - server: $server, - destination: $destination, - only_this_server: true, - no_questions_asked: true, - ); - - return redirect()->route('project.application.deployment.show', [ - 'project_uuid' => data_get($this->resource, 'environment.project.uuid'), - 'application_uuid' => data_get($this->resource, 'uuid'), - 'deployment_uuid' => $deployment_uuid, - 'environment_name' => data_get($this->resource, 'environment.name'), - ]); } public function promote(int $network_id, int $server_id) @@ -119,23 +129,27 @@ public function addServer(int $network_id, int $server_id) public function removeServer(int $network_id, int $server_id, $password) { - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); + try { + if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { + if (! Hash::check($password, Auth::user()->password)) { + $this->addError('password', 'The provided password is incorrect.'); - return; + return; + } } - } - if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) { - $this->dispatch('error', 'You cannot remove this destination server.', 'You are trying to remove the main server.'); + if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) { + $this->dispatch('error', 'You cannot remove this destination server.', 'You are trying to remove the main server.'); - return; + return; + } + $server = Server::ownedByCurrentTeam()->findOrFail($server_id); + StopApplicationOneServer::run($this->resource, $server); + $this->resource->additional_networks()->detach($network_id, ['server_id' => $server_id]); + $this->loadData(); + ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id')); + } catch (\Exception $e) { + return handleError($e, $this); } - $server = Server::find($server_id); - StopApplicationOneServer::run($this->resource, $server); - $this->resource->additional_networks()->detach($network_id, ['server_id' => $server_id]); - $this->loadData(); - ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id')); } } diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/All.php b/app/Livewire/Project/Shared/EnvironmentVariable/All.php index 787d33a690..80156bf65a 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/All.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/All.php @@ -4,7 +4,6 @@ use App\Models\EnvironmentVariable; use Livewire\Component; -use Visus\Cuid2\Cuid2; class All extends Component { @@ -14,38 +13,35 @@ class All extends Component public bool $showPreview = false; - public ?string $modalId = null; - public ?string $variables = null; public ?string $variablesPreview = null; public string $view = 'normal'; + public bool $is_env_sorting_enabled = false; + protected $listeners = [ 'saveKey' => 'submit', 'refreshEnvs', 'environmentVariableDeleted' => 'refreshEnvs', ]; - protected $rules = [ - 'resource.settings.is_env_sorting_enabled' => 'required|boolean', - ]; - public function mount() { + $this->is_env_sorting_enabled = data_get($this->resource, 'settings.is_env_sorting_enabled', false); $this->resourceClass = get_class($this->resource); $resourceWithPreviews = [\App\Models\Application::class]; - $simpleDockerfile = ! is_null(data_get($this->resource, 'dockerfile')); + $simpleDockerfile = filled(data_get($this->resource, 'dockerfile')); if (str($this->resourceClass)->contains($resourceWithPreviews) && ! $simpleDockerfile) { $this->showPreview = true; } - $this->modalId = new Cuid2; $this->sortEnvironmentVariables(); } public function instantSave() { + $this->resource->settings->is_env_sorting_enabled = $this->is_env_sorting_enabled; $this->resource->settings->save(); $this->sortEnvironmentVariables(); $this->dispatch('success', 'Environment variable settings updated.'); @@ -53,7 +49,7 @@ public function instantSave() public function sortEnvironmentVariables() { - if (! data_get($this->resource, 'settings.is_env_sorting_enabled')) { + if ($this->is_env_sorting_enabled === false) { if ($this->resource->environment_variables) { $this->resource->environment_variables = $this->resource->environment_variables->sortBy('order')->values(); } @@ -178,35 +174,12 @@ private function createEnvironmentVariable($data) $environment->is_multiline = $data['is_multiline'] ?? false; $environment->is_literal = $data['is_literal'] ?? false; $environment->is_preview = $data['is_preview'] ?? false; - - $resourceType = $this->resource->type(); - $resourceIdField = $this->getResourceIdField($resourceType); - - if ($resourceIdField) { - $environment->$resourceIdField = $this->resource->id; - } + $environment->resourceable_id = $this->resource->id; + $environment->resourceable_type = $this->resource->getMorphClass(); return $environment; } - private function getResourceIdField($resourceType) - { - $resourceTypes = [ - 'application' => 'application_id', - 'standalone-postgresql' => 'standalone_postgresql_id', - 'standalone-redis' => 'standalone_redis_id', - 'standalone-mongodb' => 'standalone_mongodb_id', - 'standalone-mysql' => 'standalone_mysql_id', - 'standalone-mariadb' => 'standalone_mariadb_id', - 'standalone-keydb' => 'standalone_keydb_id', - 'standalone-dragonfly' => 'standalone_dragonfly_id', - 'standalone-clickhouse' => 'standalone_clickhouse_id', - 'service' => 'service_id', - ]; - - return $resourceTypes[$resourceType] ?? null; - } - private function deleteRemovedVariables($isPreview, $variables) { $method = $isPreview ? 'environment_variables_preview' : 'environment_variables'; @@ -231,34 +204,14 @@ private function updateOrCreateVariables($isPreview, $variables) $environment->is_build_time = false; $environment->is_multiline = false; $environment->is_preview = $isPreview; + $environment->resourceable_id = $this->resource->id; + $environment->resourceable_type = $this->resource->getMorphClass(); - $this->setEnvironmentResourceId($environment); $environment->save(); } } } - private function setEnvironmentResourceId($environment) - { - $resourceTypes = [ - 'application' => 'application_id', - 'standalone-postgresql' => 'standalone_postgresql_id', - 'standalone-redis' => 'standalone_redis_id', - 'standalone-mongodb' => 'standalone_mongodb_id', - 'standalone-mysql' => 'standalone_mysql_id', - 'standalone-mariadb' => 'standalone_mariadb_id', - 'standalone-keydb' => 'standalone_keydb_id', - 'standalone-dragonfly' => 'standalone_dragonfly_id', - 'standalone-clickhouse' => 'standalone_clickhouse_id', - 'service' => 'service_id', - ]; - - $resourceType = $this->resource->type(); - if (isset($resourceTypes[$resourceType])) { - $environment->{$resourceTypes[$resourceType]} = $this->resource->id; - } - } - public function refreshEnvs() { $this->resource->refresh(); diff --git a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php index 6294d97c6e..4b66bfdcb6 100644 --- a/app/Livewire/Project/Shared/EnvironmentVariable/Show.php +++ b/app/Livewire/Project/Shared/EnvironmentVariable/Show.php @@ -5,7 +5,6 @@ use App\Models\EnvironmentVariable as ModelsEnvironmentVariable; use App\Models\SharedEnvironmentVariable; use Livewire\Component; -use Visus\Cuid2\Cuid2; class Show extends Component { @@ -13,8 +12,6 @@ class Show extends Component public ModelsEnvironmentVariable|SharedEnvironmentVariable $env; - public ?string $modalId = null; - public bool $isDisabled = false; public bool $isLocked = false; @@ -23,6 +20,26 @@ class Show extends Component public string $type; + public string $key; + + public ?string $value = null; + + public ?string $real_value = null; + + public bool $is_shared = false; + + public bool $is_build_time = false; + + public bool $is_multiline = false; + + public bool $is_literal = false; + + public bool $is_shown_once = false; + + public bool $is_required = false; + + public bool $is_really_required = false; + protected $listeners = [ 'refreshEnvs' => 'refresh', 'refresh', @@ -30,40 +47,59 @@ class Show extends Component ]; protected $rules = [ - 'env.key' => 'required|string', - 'env.value' => 'nullable', - 'env.is_build_time' => 'required|boolean', - 'env.is_multiline' => 'required|boolean', - 'env.is_literal' => 'required|boolean', - 'env.is_shown_once' => 'required|boolean', - 'env.real_value' => 'nullable', - 'env.is_required' => 'required|boolean', + 'key' => 'required|string', + 'value' => 'nullable', + 'is_build_time' => 'required|boolean', + 'is_multiline' => 'required|boolean', + 'is_literal' => 'required|boolean', + 'is_shown_once' => 'required|boolean', + 'real_value' => 'nullable', + 'is_required' => 'required|boolean', ]; - protected $validationAttributes = [ - 'env.key' => 'Key', - 'env.value' => 'Value', - 'env.is_build_time' => 'Build Time', - 'env.is_multiline' => 'Multiline', - 'env.is_literal' => 'Literal', - 'env.is_shown_once' => 'Shown Once', - 'env.is_required' => 'Required', - ]; + public function mount() + { + $this->syncData(); + if ($this->env->getMorphClass() === \App\Models\SharedEnvironmentVariable::class) { + $this->isSharedVariable = true; + } + $this->parameters = get_route_parameters(); + $this->checkEnvs(); + + } public function refresh() { - $this->env->refresh(); + $this->syncData(); $this->checkEnvs(); } - public function mount() + public function syncData(bool $toModel = false) { - if ($this->env->getMorphClass() === \App\Models\SharedEnvironmentVariable::class) { - $this->isSharedVariable = true; + if ($toModel) { + $this->validate(); + $this->env->key = $this->key; + $this->env->value = $this->value; + $this->env->is_build_time = $this->is_build_time; + $this->env->is_multiline = $this->is_multiline; + $this->env->is_literal = $this->is_literal; + $this->env->is_shown_once = $this->is_shown_once; + $this->env->is_required = $this->is_required; + $this->env->is_shared = $this->is_shared; + $this->env->save(); + } else { + + $this->key = $this->env->key; + $this->value = $this->env->value; + $this->is_build_time = $this->env->is_build_time ?? false; + $this->is_multiline = $this->env->is_multiline; + $this->is_literal = $this->env->is_literal; + $this->is_shown_once = $this->env->is_shown_once; + $this->is_required = $this->env->is_required ?? false; + $this->is_really_required = $this->env->is_really_required ?? false; + $this->is_shared = $this->env->is_shared ?? false; + $this->real_value = $this->env->real_value; } - $this->modalId = new Cuid2; - $this->parameters = get_route_parameters(); - $this->checkEnvs(); } public function checkEnvs() @@ -107,17 +143,17 @@ public function submit() try { if ($this->isSharedVariable) { $this->validate([ - 'env.key' => 'required|string', - 'env.value' => 'nullable', - 'env.is_shown_once' => 'required|boolean', + 'key' => 'required|string', + 'value' => 'nullable', + 'is_shown_once' => 'required|boolean', ]); } else { $this->validate(); } - if (! $this->isSharedVariable && $this->env->is_required && str($this->env->real_value)->isEmpty()) { + if (! $this->isSharedVariable && $this->is_required && str($this->value)->isEmpty()) { $oldValue = $this->env->getOriginal('value'); - $this->env->value = $oldValue; + $this->value = $oldValue; $this->dispatch('error', 'Required environment variable cannot be empty.'); return; @@ -126,10 +162,10 @@ public function submit() $this->serialize(); if ($this->isSharedVariable) { - unset($this->env->is_required); + unset($this->is_required); } - $this->env->save(); + $this->syncData(true); $this->dispatch('success', 'Environment variable updated.'); $this->dispatch('envsUpdated'); } catch (\Exception $e) { diff --git a/app/Livewire/Project/Shared/ResourceOperations.php b/app/Livewire/Project/Shared/ResourceOperations.php index e67df6aa90..c6a32f0c7a 100644 --- a/app/Livewire/Project/Shared/ResourceOperations.php +++ b/app/Livewire/Project/Shared/ResourceOperations.php @@ -15,7 +15,7 @@ class ResourceOperations extends Component public $projectUuid; - public $environmentName; + public $environmentUuid; public $projects; @@ -25,7 +25,7 @@ public function mount() { $parameters = get_route_parameters(); $this->projectUuid = data_get($parameters, 'project_uuid'); - $this->environmentName = data_get($parameters, 'environment_name'); + $this->environmentUuid = data_get($parameters, 'environment_uuid'); $this->projects = Project::ownedByCurrentTeam()->get(); $this->servers = currentTeam()->servers; } @@ -42,9 +42,11 @@ public function cloneTo($destination_id) $uuid = (string) new Cuid2; $server = $new_destination->server; if ($this->resource->getMorphClass() === \App\Models\Application::class) { + $name = 'clone-of-'.str($this->resource->name)->limit(20).'-'.$uuid; + $new_resource = $this->resource->replicate()->fill([ 'uuid' => $uuid, - 'name' => $this->resource->name.'-clone-'.$uuid, + 'name' => $name, 'fqdn' => generateFqdn($server, $uuid), 'status' => 'exited', 'destination_id' => $new_destination->id, @@ -58,21 +60,26 @@ public function cloneTo($destination_id) $environmentVaribles = $this->resource->environment_variables()->get(); foreach ($environmentVaribles as $environmentVarible) { $newEnvironmentVariable = $environmentVarible->replicate()->fill([ - 'application_id' => $new_resource->id, + 'resourceable_id' => $new_resource->id, + 'resourceable_type' => $new_resource->getMorphClass(), ]); $newEnvironmentVariable->save(); } $persistentVolumes = $this->resource->persistentStorages()->get(); foreach ($persistentVolumes as $volume) { + $volumeName = str($volume->name)->replace($this->resource->uuid, $new_resource->uuid)->value(); + if ($volumeName === $volume->name) { + $volumeName = $new_resource->uuid.'-'.str($volume->name)->afterLast('-'); + } $newPersistentVolume = $volume->replicate()->fill([ - 'name' => $new_resource->uuid.'-'.str($volume->name)->afterLast('-'), + 'name' => $volumeName, 'resource_id' => $new_resource->id, ]); $newPersistentVolume->save(); } $route = route('project.application.configuration', [ 'project_uuid' => $this->projectUuid, - 'environment_name' => $this->environmentName, + 'environment_uuid' => $this->environmentUuid, 'application_uuid' => $new_resource->uuid, ]).'#resource-operations'; @@ -115,7 +122,7 @@ public function cloneTo($destination_id) } $route = route('project.database.configuration', [ 'project_uuid' => $this->projectUuid, - 'environment_name' => $this->environmentName, + 'environment_uuid' => $this->environmentUuid, 'database_uuid' => $new_resource->uuid, ]).'#resource-operations'; @@ -141,7 +148,7 @@ public function cloneTo($destination_id) $new_resource->parse(); $route = route('project.service.configuration', [ 'project_uuid' => $this->projectUuid, - 'environment_name' => $this->environmentName, + 'environment_uuid' => $this->environmentUuid, 'service_uuid' => $new_resource->uuid, ]).'#resource-operations'; @@ -159,7 +166,7 @@ public function moveTo($environment_id) if ($this->resource->type() === 'application') { $route = route('project.application.configuration', [ 'project_uuid' => $new_environment->project->uuid, - 'environment_name' => $new_environment->name, + 'environment_uuid' => $new_environment->uuid, 'application_uuid' => $this->resource->uuid, ]).'#resource-operations'; @@ -167,7 +174,7 @@ public function moveTo($environment_id) } elseif (str($this->resource->type())->startsWith('standalone-')) { $route = route('project.database.configuration', [ 'project_uuid' => $new_environment->project->uuid, - 'environment_name' => $new_environment->name, + 'environment_uuid' => $new_environment->uuid, 'database_uuid' => $this->resource->uuid, ]).'#resource-operations'; @@ -175,7 +182,7 @@ public function moveTo($environment_id) } elseif ($this->resource->type() === 'service') { $route = route('project.service.configuration', [ 'project_uuid' => $new_environment->project->uuid, - 'environment_name' => $new_environment->name, + 'environment_uuid' => $new_environment->uuid, 'service_uuid' => $this->resource->uuid, ]).'#resource-operations'; diff --git a/app/Livewire/Project/Shared/ScheduledTask/Show.php b/app/Livewire/Project/Shared/ScheduledTask/Show.php index 0900a1d700..0764ab3b96 100644 --- a/app/Livewire/Project/Shared/ScheduledTask/Show.php +++ b/app/Livewire/Project/Shared/ScheduledTask/Show.php @@ -46,7 +46,7 @@ class Show extends Component #[Locked] public string $task_uuid; - public function mount(string $task_uuid, string $project_uuid, string $environment_name, ?string $application_uuid = null, ?string $service_uuid = null) + public function mount(string $task_uuid, string $project_uuid, string $environment_uuid, ?string $application_uuid = null, ?string $service_uuid = null) { try { $this->task_uuid = $task_uuid; @@ -60,7 +60,7 @@ public function mount(string $task_uuid, string $project_uuid, string $environme $this->resource = Service::ownedByCurrentTeam()->where('uuid', $service_uuid)->firstOrFail(); } $this->parameters = [ - 'environment_name' => $environment_name, + 'environment_uuid' => $environment_uuid, 'project_uuid' => $project_uuid, 'application_uuid' => $application_uuid, 'service_uuid' => $service_uuid, diff --git a/app/Livewire/Project/Show.php b/app/Livewire/Project/Show.php index 2335519c74..886a20218f 100644 --- a/app/Livewire/Project/Show.php +++ b/app/Livewire/Project/Show.php @@ -6,6 +6,7 @@ use App\Models\Project; use Livewire\Attributes\Validate; use Livewire\Component; +use Visus\Cuid2\Cuid2; class Show extends Component { @@ -33,17 +34,26 @@ public function submit() $environment = Environment::create([ 'name' => $this->name, 'project_id' => $this->project->id, + 'uuid' => (string) new Cuid2, ]); return redirect()->route('project.resource.index', [ 'project_uuid' => $this->project->uuid, - 'environment_name' => $environment->name, + 'environment_uuid' => $environment->uuid, ]); } catch (\Throwable $e) { handleError($e, $this); } } + public function navigateToEnvironment($projectUuid, $environmentUuid) + { + return redirect()->route('project.resource.index', [ + 'project_uuid' => $projectUuid, + 'environment_uuid' => $environmentUuid, + ]); + } + public function render() { return view('livewire.project.show'); diff --git a/app/Livewire/Server/Advanced.php b/app/Livewire/Server/Advanced.php index 0650de9a00..577730f244 100644 --- a/app/Livewire/Server/Advanced.php +++ b/app/Livewire/Server/Advanced.php @@ -13,14 +13,11 @@ class Advanced extends Component public array $parameters = []; - #[Validate(['integer', 'min:1'])] - public int $concurrentBuilds = 1; - - #[Validate(['integer', 'min:1'])] - public int $dynamicTimeout = 1; + #[Validate(['string'])] + public string $serverDiskUsageCheckFrequency = '0 23 * * *'; - #[Validate('boolean')] - public bool $forceDockerCleanup = false; + #[Validate(['integer', 'min:1', 'max:99'])] + public int $serverDiskUsageNotificationThreshold = 50; #[Validate(['string', 'required'])] public string $dockerCleanupFrequency = '*/10 * * * *'; @@ -28,8 +25,8 @@ class Advanced extends Component #[Validate(['integer', 'min:1', 'max:99'])] public int $dockerCleanupThreshold = 10; - #[Validate(['integer', 'min:1', 'max:99'])] - public int $serverDiskUsageNotificationThreshold = 50; + #[Validate('boolean')] + public bool $forceDockerCleanup = false; #[Validate('boolean')] public bool $deleteUnusedVolumes = false; @@ -37,6 +34,12 @@ class Advanced extends Component #[Validate('boolean')] public bool $deleteUnusedNetworks = false; + #[Validate(['integer', 'min:1'])] + public int $concurrentBuilds = 1; + + #[Validate(['integer', 'min:1'])] + public int $dynamicTimeout = 1; + public function mount(string $server_uuid) { try { @@ -60,6 +63,7 @@ public function syncData(bool $toModel = false) $this->server->settings->server_disk_usage_notification_threshold = $this->serverDiskUsageNotificationThreshold; $this->server->settings->delete_unused_volumes = $this->deleteUnusedVolumes; $this->server->settings->delete_unused_networks = $this->deleteUnusedNetworks; + $this->server->settings->server_disk_usage_check_frequency = $this->serverDiskUsageCheckFrequency; $this->server->settings->save(); } else { $this->concurrentBuilds = $this->server->settings->concurrent_builds; @@ -70,6 +74,7 @@ public function syncData(bool $toModel = false) $this->serverDiskUsageNotificationThreshold = $this->server->settings->server_disk_usage_notification_threshold; $this->deleteUnusedVolumes = $this->server->settings->delete_unused_volumes; $this->deleteUnusedNetworks = $this->server->settings->delete_unused_networks; + $this->serverDiskUsageCheckFrequency = $this->server->settings->server_disk_usage_check_frequency; } } @@ -100,6 +105,10 @@ public function submit() $this->dockerCleanupFrequency = $this->server->settings->getOriginal('docker_cleanup_frequency'); throw new \Exception('Invalid Cron / Human expression for Docker Cleanup Frequency.'); } + if (! validate_cron_expression($this->serverDiskUsageCheckFrequency)) { + $this->serverDiskUsageCheckFrequency = $this->server->settings->getOriginal('server_disk_usage_check_frequency'); + throw new \Exception('Invalid Cron / Human expression for Disk Usage Check Frequency.'); + } $this->syncData(true); $this->dispatch('success', 'Server updated.'); } catch (\Throwable $e) { diff --git a/app/Livewire/Settings/Index.php b/app/Livewire/Settings/Index.php index 7adb0f8a79..3d90024b7e 100644 --- a/app/Livewire/Settings/Index.php +++ b/app/Livewire/Settings/Index.php @@ -226,16 +226,18 @@ public function checkManually() } } - public function toggleTwoStepConfirmation($password) + public function toggleTwoStepConfirmation($password): bool { if (! Hash::check($password, Auth::user()->password)) { $this->addError('password', 'The provided password is incorrect.'); - return; + return false; } $this->settings->disable_two_step_confirmation = $this->disable_two_step_confirmation = true; $this->settings->save(); $this->dispatch('success', 'Two step confirmation has been disabled.'); + + return true; } } diff --git a/app/Livewire/SharedVariables/Environment/Show.php b/app/Livewire/SharedVariables/Environment/Show.php index 6a33eb60d6..e88ac5f135 100644 --- a/app/Livewire/SharedVariables/Environment/Show.php +++ b/app/Livewire/SharedVariables/Environment/Show.php @@ -42,8 +42,8 @@ public function saveKey($data) public function mount() { $this->parameters = get_route_parameters(); - $this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->first(); - $this->environment = $this->project->environments()->where('name', request()->route('environment_name'))->first(); + $this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->firstOrFail(); + $this->environment = $this->project->environments()->where('uuid', request()->route('environment_uuid'))->firstOrFail(); } public function render() diff --git a/app/Livewire/Source/Github/Change.php b/app/Livewire/Source/Github/Change.php index 4679274848..8f4f02f70f 100644 --- a/app/Livewire/Source/Github/Change.php +++ b/app/Livewire/Source/Github/Change.php @@ -130,14 +130,14 @@ public function mount() } else { $parameters = data_get(session('from'), 'parameters'); $back = data_get(session('from'), 'back'); - $environment_name = data_get($parameters, 'environment_name'); + $environment_uuid = data_get($parameters, 'environment_uuid'); $project_uuid = data_get($parameters, 'project_uuid'); $type = data_get($parameters, 'type'); $destination = data_get($parameters, 'destination'); session()->forget('from'); return redirect()->route($back, [ - 'environment_name' => $environment_name, + 'environment_uuid' => $environment_uuid, 'project_uuid' => $project_uuid, 'type' => $type, 'destination' => $destination, diff --git a/app/Models/Application.php b/app/Models/Application.php index bfb2a10418..56faf6c311 100644 --- a/app/Models/Application.php +++ b/app/Models/Application.php @@ -325,7 +325,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.application.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'application_uuid' => data_get($this, 'uuid'), ]); } @@ -338,7 +338,7 @@ public function taskLink($task_uuid) if (data_get($this, 'environment.project.uuid')) { $route = route('project.application.scheduled-tasks', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'application_uuid' => data_get($this, 'uuid'), 'task_uuid' => $task_uuid, ]); @@ -610,7 +610,7 @@ public function status(): Attribute }, get: function ($value) { if ($this->additional_servers->count() === 0) { - //running (healthy) + // running (healthy) if (str($value)->contains('(')) { $status = str($value)->before('(')->trim()->value(); $health = str($value)->after('(')->before(')')->trim()->value() ?? 'unhealthy'; @@ -695,46 +695,62 @@ public function main_port() return $this->settings->is_static ? [80] : $this->ports_exposes_array; } - public function environment_variables(): HasMany + public function environment_variables() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->orderBy('key', 'asc'); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', false) + ->orderBy('key', 'asc'); } - public function runtime_environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->where('key', 'not like', 'NIXPACKS_%'); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', false) + ->where('key', 'not like', 'NIXPACKS_%'); } - // Preview Deployments - - public function build_environment_variables(): HasMany + public function build_environment_variables() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->where('is_build_time', true)->where('key', 'not like', 'NIXPACKS_%'); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', false) + ->where('is_build_time', true) + ->where('key', 'not like', 'NIXPACKS_%'); } - public function nixpacks_environment_variables(): HasMany + public function nixpacks_environment_variables() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', false)->where('key', 'like', 'NIXPACKS_%'); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', false) + ->where('key', 'like', 'NIXPACKS_%'); } - public function environment_variables_preview(): HasMany + public function environment_variables_preview() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderBy('key', 'asc'); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', true) + ->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC"); } - public function runtime_environment_variables_preview(): HasMany + public function runtime_environment_variables_preview() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->where('key', 'not like', 'NIXPACKS_%'); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', true) + ->where('key', 'not like', 'NIXPACKS_%'); } - public function build_environment_variables_preview(): HasMany + public function build_environment_variables_preview() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->where('is_build_time', true)->where('key', 'not like', 'NIXPACKS_%'); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', true) + ->where('is_build_time', true) + ->where('key', 'not like', 'NIXPACKS_%'); } - public function nixpacks_environment_variables_preview(): HasMany + public function nixpacks_environment_variables_preview() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->where('key', 'like', 'NIXPACKS_%'); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', true) + ->where('key', 'like', 'NIXPACKS_%'); } public function scheduled_tasks(): HasMany diff --git a/app/Models/Environment.php b/app/Models/Environment.php index 71e8bbd218..b8f1090d8d 100644 --- a/app/Models/Environment.php +++ b/app/Models/Environment.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; -use Illuminate\Database\Eloquent\Model; use OpenApi\Attributes as OA; #[OA\Schema( @@ -18,7 +17,7 @@ 'description' => ['type' => 'string'], ] )] -class Environment extends Model +class Environment extends BaseModel { protected $guarded = []; diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index 96c57e63e8..507ff0d7e2 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -14,9 +14,8 @@ properties: [ 'id' => ['type' => 'integer'], 'uuid' => ['type' => 'string'], - 'application_id' => ['type' => 'integer'], - 'service_id' => ['type' => 'integer'], - 'database_id' => ['type' => 'integer'], + 'resourceable_type' => ['type' => 'string'], + 'resourceable_id' => ['type' => 'integer'], 'is_build_time' => ['type' => 'boolean'], 'is_literal' => ['type' => 'boolean'], 'is_multiline' => ['type' => 'boolean'], @@ -42,6 +41,8 @@ class EnvironmentVariable extends Model 'is_multiline' => 'boolean', 'is_preview' => 'boolean', 'version' => 'string', + 'resourceable_type' => 'string', + 'resourceable_id' => 'integer', ]; protected $appends = ['real_value', 'is_shared', 'is_really_required']; @@ -53,18 +54,25 @@ protected static function booted() $model->uuid = (string) new Cuid2; } }); + static::created(function (EnvironmentVariable $environment_variable) { - if ($environment_variable->application_id && ! $environment_variable->is_preview) { - $found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first(); + if ($environment_variable->resourceable_type === Application::class && ! $environment_variable->is_preview) { + $found = ModelsEnvironmentVariable::where('key', $environment_variable->key) + ->where('resourceable_type', Application::class) + ->where('resourceable_id', $environment_variable->resourceable_id) + ->where('is_preview', true) + ->first(); + if (! $found) { - $application = Application::find($environment_variable->application_id); - if ($application->build_pack !== 'dockerfile') { + $application = Application::find($environment_variable->resourceable_id); + if ($application && $application->build_pack !== 'dockerfile') { ModelsEnvironmentVariable::create([ 'key' => $environment_variable->key, 'value' => $environment_variable->value, 'is_build_time' => $environment_variable->is_build_time, 'is_multiline' => $environment_variable->is_multiline ?? false, - 'application_id' => $environment_variable->application_id, + 'resourceable_type' => Application::class, + 'resourceable_id' => $environment_variable->resourceable_id, 'is_preview' => true, ]); } @@ -74,6 +82,7 @@ protected static function booted() 'version' => config('constants.coolify.version'), ]); }); + static::saving(function (EnvironmentVariable $environmentVariable) { $environmentVariable->updateIsShared(); }); @@ -92,43 +101,32 @@ protected function value(): Attribute ); } - public function resource() + /** + * Get the parent resourceable model. + */ + public function resourceable() { - $resource = null; - if ($this->application_id) { - $resource = Application::find($this->application_id); - } elseif ($this->service_id) { - $resource = Service::find($this->service_id); - } elseif ($this->standalone_postgresql_id) { - $resource = StandalonePostgresql::find($this->standalone_postgresql_id); - } elseif ($this->standalone_redis_id) { - $resource = StandaloneRedis::find($this->standalone_redis_id); - } elseif ($this->standalone_mongodb_id) { - $resource = StandaloneMongodb::find($this->standalone_mongodb_id); - } elseif ($this->standalone_mysql_id) { - $resource = StandaloneMysql::find($this->standalone_mysql_id); - } elseif ($this->standalone_mariadb_id) { - $resource = StandaloneMariadb::find($this->standalone_mariadb_id); - } elseif ($this->standalone_keydb_id) { - $resource = StandaloneKeydb::find($this->standalone_keydb_id); - } elseif ($this->standalone_dragonfly_id) { - $resource = StandaloneDragonfly::find($this->standalone_dragonfly_id); - } elseif ($this->standalone_clickhouse_id) { - $resource = StandaloneClickhouse::find($this->standalone_clickhouse_id); - } + return $this->morphTo(); + } - return $resource; + public function resource() + { + return $this->resourceable; } public function realValue(): Attribute { - $resource = $this->resource(); - return Attribute::make( - get: function () use ($resource) { - $env = $this->get_real_environment_variables($this->value, $resource); + get: function () { + if (! $this->relationLoaded('resourceable')) { + $this->load('resourceable'); + } + $resource = $this->resourceable; + if (! $resource) { + return null; + } - return data_get($env, 'value', $env); + return $this->get_real_environment_variables($this->value, $resource); } ); } @@ -164,7 +162,6 @@ private function get_real_environment_variables(?string $environment_variable = if ($sharedEnvsFound->isEmpty()) { return $environment_variable; } - foreach ($sharedEnvsFound as $sharedEnv) { $type = str($sharedEnv)->match('/(.*?)\./'); if (! collect(SHARED_VARIABLE_TYPES)->contains($type)) { diff --git a/app/Models/Project.php b/app/Models/Project.php index f27e6c2083..3b50b9b33e 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -3,6 +3,7 @@ namespace App\Models; use OpenApi\Attributes as OA; +use Visus\Cuid2\Cuid2; #[OA\Schema( description: 'Project model', @@ -24,8 +25,6 @@ class Project extends BaseModel { protected $guarded = []; - protected $appends = ['default_environment']; - public static function ownedByCurrentTeam() { return Project::whereTeamId(currentTeam()->id)->orderByRaw('LOWER(name)'); @@ -40,6 +39,7 @@ protected static function booted() Environment::create([ 'name' => 'production', 'project_id' => $project->id, + 'uuid' => (string) new Cuid2, ]); }); static::deleting(function ($project) { @@ -140,18 +140,4 @@ public function databases() { return $this->postgresqls()->get()->merge($this->redis()->get())->merge($this->mongodbs()->get())->merge($this->mysqls()->get())->merge($this->mariadbs()->get())->merge($this->keydbs()->get())->merge($this->dragonflies()->get())->merge($this->clickhouses()->get()); } - - public function getDefaultEnvironmentAttribute() - { - $default = $this->environments()->where('name', 'production')->first(); - if ($default) { - return $default->name; - } - $default = $this->environments()->get(); - if ($default->count() > 0) { - return $default->sortBy('created_at')->first()->name; - } - - return null; - } } diff --git a/app/Models/Service.php b/app/Models/Service.php index 5a2690490a..1df579802d 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -1120,7 +1120,8 @@ public function saveExtraFields($fields) 'key' => $key, 'value' => $value, 'is_build_time' => false, - 'service_id' => $this->id, + 'resourceable_id' => $this->id, + 'resourceable_type' => $this->getMorphClass(), 'is_preview' => false, ]); } @@ -1132,7 +1133,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.service.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'service_uuid' => data_get($this, 'uuid'), ]); } @@ -1145,7 +1146,7 @@ public function taskLink($task_uuid) if (data_get($this, 'environment.project.uuid')) { $route = route('project.service.scheduled-tasks', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'service_uuid' => data_get($this, 'uuid'), 'task_uuid' => $task_uuid, ]); @@ -1232,14 +1233,17 @@ public function scheduled_tasks(): HasMany return $this->hasMany(ScheduledTask::class)->orderBy('name', 'asc'); } - public function environment_variables(): HasMany + public function environment_variables() { - return $this->hasMany(EnvironmentVariable::class)->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC"); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); } - public function environment_variables_preview(): HasMany + public function environment_variables_preview() { - return $this->hasMany(EnvironmentVariable::class)->where('is_preview', true)->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC"); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->where('is_preview', true) + ->orderByRaw("LOWER(key) LIKE LOWER('SERVICE%') DESC, LOWER(key) ASC"); } public function workdir() diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php index 6d66c68546..60198115da 100644 --- a/app/Models/StandaloneClickhouse.php +++ b/app/Models/StandaloneClickhouse.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneClickhouse extends BaseModel @@ -169,7 +168,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.database.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'database_uuid' => data_get($this, 'uuid'), ]); } @@ -251,14 +250,15 @@ public function destination() return $this->morphTo(); } - public function environment_variables(): HasMany + public function environment_variables() { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); } - public function runtime_environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable'); } public function persistentStorages() diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php index f7d83f0a31..3c1127d8de 100644 --- a/app/Models/StandaloneDragonfly.php +++ b/app/Models/StandaloneDragonfly.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneDragonfly extends BaseModel @@ -174,7 +173,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.database.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'database_uuid' => data_get($this, 'uuid'), ]); } @@ -251,14 +250,9 @@ public function destination() return $this->morphTo(); } - public function environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class); - } - - public function runtime_environment_variables(): HasMany - { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable'); } public function persistentStorages() @@ -319,4 +313,10 @@ public function isBackupSolutionAvailable() { return false; } + + public function environment_variables() + { + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); + } } diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php index 083c743d95..ebf1c22e95 100644 --- a/app/Models/StandaloneKeydb.php +++ b/app/Models/StandaloneKeydb.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneKeydb extends BaseModel @@ -174,7 +173,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.database.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'database_uuid' => data_get($this, 'uuid'), ]); } @@ -251,14 +250,9 @@ public function destination() return $this->morphTo(); } - public function environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class); - } - - public function runtime_environment_variables(): HasMany - { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable'); } public function persistentStorages() @@ -319,4 +313,10 @@ public function isBackupSolutionAvailable() { return false; } + + public function environment_variables() + { + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); + } } diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php index 833dad6c4f..004ead4d95 100644 --- a/app/Models/StandaloneMariadb.php +++ b/app/Models/StandaloneMariadb.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneMariadb extends BaseModel @@ -174,7 +173,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.database.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'database_uuid' => data_get($this, 'uuid'), ]); } @@ -251,14 +250,15 @@ public function destination() return $this->morphTo(); } - public function environment_variables(): HasMany + public function environment_variables() { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); } - public function runtime_environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable'); } public function persistentStorages() diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php index dd8893180b..aba0f6123e 100644 --- a/app/Models/StandaloneMongodb.php +++ b/app/Models/StandaloneMongodb.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneMongodb extends BaseModel @@ -183,7 +182,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.database.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'database_uuid' => data_get($this, 'uuid'), ]); } @@ -271,14 +270,9 @@ public function destination() return $this->morphTo(); } - public function environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class); - } - - public function runtime_environment_variables(): HasMany - { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable'); } public function persistentStorages() @@ -339,4 +333,10 @@ public function isBackupSolutionAvailable() { return true; } + + public function environment_variables() + { + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); + } } diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php index 710fea1bc8..9ae0fdcaeb 100644 --- a/app/Models/StandaloneMysql.php +++ b/app/Models/StandaloneMysql.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneMysql extends BaseModel @@ -175,7 +174,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.database.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'database_uuid' => data_get($this, 'uuid'), ]); } @@ -252,14 +251,9 @@ public function destination() return $this->morphTo(); } - public function environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class); - } - - public function runtime_environment_variables(): HasMany - { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable'); } public function persistentStorages() @@ -320,4 +314,10 @@ public function isBackupSolutionAvailable() { return true; } + + public function environment_variables() + { + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); + } } diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php index 4a457a6cf9..dd92ae7c91 100644 --- a/app/Models/StandalonePostgresql.php +++ b/app/Models/StandalonePostgresql.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class StandalonePostgresql extends BaseModel @@ -170,7 +169,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.database.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'database_uuid' => data_get($this, 'uuid'), ]); } @@ -252,14 +251,9 @@ public function destination() return $this->morphTo(); } - public function environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class); - } - - public function runtime_environment_variables(): HasMany - { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable'); } public function persistentStorages() @@ -320,4 +314,10 @@ public function getMemoryMetrics(int $mins = 5) return $parsedCollection->toArray(); } + + public function environment_variables() + { + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); + } } diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php index 826bb951c5..ed5cf98708 100644 --- a/app/Models/StandaloneRedis.php +++ b/app/Models/StandaloneRedis.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class StandaloneRedis extends BaseModel @@ -170,7 +169,7 @@ public function link() if (data_get($this, 'environment.project.uuid')) { return route('project.database.configuration', [ 'project_uuid' => data_get($this, 'environment.project.uuid'), - 'environment_name' => data_get($this, 'environment.name'), + 'environment_uuid' => data_get($this, 'environment.uuid'), 'database_uuid' => data_get($this, 'uuid'), ]); } @@ -262,14 +261,9 @@ public function destination() return $this->morphTo(); } - public function environment_variables(): HasMany + public function runtime_environment_variables() { - return $this->hasMany(EnvironmentVariable::class); - } - - public function runtime_environment_variables(): HasMany - { - return $this->hasMany(EnvironmentVariable::class); + return $this->morphMany(EnvironmentVariable::class, 'resourceable'); } public function persistentStorages() @@ -359,4 +353,10 @@ public function redisUsername(): Attribute } ); } + + public function environment_variables() + { + return $this->morphMany(EnvironmentVariable::class, 'resourceable') + ->orderBy('key', 'asc'); + } } diff --git a/app/Models/Team.php b/app/Models/Team.php index 33847a3c83..8651df3c8b 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -120,22 +120,10 @@ public function limits(): Attribute return Attribute::make( get: function () { if (config('constants.coolify.self_hosted') || $this->id === 0) { - $subscription = 'self-hosted'; - } else { - $subscription = data_get($this, 'subscription'); - if (is_null($subscription)) { - $subscription = 'zero'; - } else { - $subscription = $subscription->type(); - } - } - if ($this->custom_server_limit) { - $serverLimit = $this->custom_server_limit; - } else { - $serverLimit = config('constants.limits.server')[strtolower($subscription)]; + return 999999999999; } - return $serverLimit ?? 2; + return $this->custom_server_limit ?? 2; } ); } diff --git a/app/Notifications/Application/DeploymentFailed.php b/app/Notifications/Application/DeploymentFailed.php index 80c1c421c1..0c09b1dbd0 100644 --- a/app/Notifications/Application/DeploymentFailed.php +++ b/app/Notifications/Application/DeploymentFailed.php @@ -22,6 +22,8 @@ class DeploymentFailed extends CustomEmailNotification public string $project_uuid; + public string $environment_uuid; + public string $environment_name; public ?string $deployment_url = null; @@ -36,12 +38,13 @@ public function __construct(Application $application, string $deployment_uuid, ? $this->preview = $preview; $this->application_name = data_get($application, 'name'); $this->project_uuid = data_get($application, 'environment.project.uuid'); + $this->environment_uuid = data_get($application, 'environment.uuid'); $this->environment_name = data_get($application, 'environment.name'); $this->fqdn = data_get($application, 'fqdn'); if (str($this->fqdn)->explode(',')->count() > 1) { $this->fqdn = str($this->fqdn)->explode(',')->first(); } - $this->deployment_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; + $this->deployment_url = base_url()."/project/{$this->project_uuid}/environments/{$this->environment_uuid}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; } public function via(object $notifiable): array diff --git a/app/Notifications/Application/DeploymentSuccess.php b/app/Notifications/Application/DeploymentSuccess.php index b1a3d5225d..e1067e9bc4 100644 --- a/app/Notifications/Application/DeploymentSuccess.php +++ b/app/Notifications/Application/DeploymentSuccess.php @@ -22,6 +22,8 @@ class DeploymentSuccess extends CustomEmailNotification public string $project_uuid; + public string $environment_uuid; + public string $environment_name; public ?string $deployment_url = null; @@ -36,12 +38,13 @@ public function __construct(Application $application, string $deployment_uuid, ? $this->preview = $preview; $this->application_name = data_get($application, 'name'); $this->project_uuid = data_get($application, 'environment.project.uuid'); + $this->environment_uuid = data_get($application, 'environment.uuid'); $this->environment_name = data_get($application, 'environment.name'); $this->fqdn = data_get($application, 'fqdn'); if (str($this->fqdn)->explode(',')->count() > 1) { $this->fqdn = str($this->fqdn)->explode(',')->first(); } - $this->deployment_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; + $this->deployment_url = base_url()."/project/{$this->project_uuid}/environments/{$this->environment_uuid}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; } public function via(object $notifiable): array @@ -144,7 +147,7 @@ public function toPushover(): PushoverMessage { if ($this->preview) { $title = "Pull request #{$this->preview->pull_request_id} successfully deployed"; - $message = 'New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . ''; + $message = 'New PR'.$this->preview->pull_request_id.' version successfully deployed of '.$this->application_name.''; if ($this->preview->fqdn) { $buttons[] = [ 'text' => 'Open Application', @@ -153,7 +156,7 @@ public function toPushover(): PushoverMessage } } else { $title = 'New version successfully deployed'; - $message = 'New version successfully deployed of ' . $this->application_name . ''; + $message = 'New version successfully deployed of '.$this->application_name.''; if ($this->fqdn) { $buttons[] = [ 'text' => 'Open Application', diff --git a/app/Notifications/Application/StatusChanged.php b/app/Notifications/Application/StatusChanged.php index c9c7344c42..669f6e5842 100644 --- a/app/Notifications/Application/StatusChanged.php +++ b/app/Notifications/Application/StatusChanged.php @@ -15,6 +15,8 @@ class StatusChanged extends CustomEmailNotification public string $project_uuid; + public string $environment_uuid; + public string $environment_name; public ?string $resource_url = null; @@ -26,12 +28,13 @@ public function __construct(public Application $resource) $this->onQueue('high'); $this->resource_name = data_get($resource, 'name'); $this->project_uuid = data_get($resource, 'environment.project.uuid'); + $this->environment_uuid = data_get($resource, 'environment.uuid'); $this->environment_name = data_get($resource, 'environment.name'); $this->fqdn = data_get($resource, 'fqdn', null); if (str($this->fqdn)->explode(',')->count() > 1) { $this->fqdn = str($this->fqdn)->explode(',')->first(); } - $this->resource_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->resource->uuid}"; + $this->resource_url = base_url()."/project/{$this->project_uuid}/environments/{$this->environment_uuid}/application/{$this->resource->uuid}"; } public function via(object $notifiable): array @@ -80,7 +83,7 @@ public function toTelegram(): array public function toPushover(): PushoverMessage { - $message = $this->resource_name . ' has been stopped.'; + $message = $this->resource_name.' has been stopped.'; return new PushoverMessage( title: 'Application stopped', diff --git a/app/Notifications/Container/ContainerStopped.php b/app/Notifications/Container/ContainerStopped.php index 49aea196d4..59ad7ae4e4 100644 --- a/app/Notifications/Container/ContainerStopped.php +++ b/app/Notifications/Container/ContainerStopped.php @@ -87,7 +87,6 @@ public function toPushover(): PushoverMessage ); } - public function toSlack(): SlackMessage { $title = 'Resource stopped'; diff --git a/app/Notifications/Database/BackupSuccess.php b/app/Notifications/Database/BackupSuccess.php index 4c3e8e060d..585f7cce19 100644 --- a/app/Notifications/Database/BackupSuccess.php +++ b/app/Notifications/Database/BackupSuccess.php @@ -63,7 +63,6 @@ public function toTelegram(): array ]; } - public function toPushover(): PushoverMessage { return new PushoverMessage( @@ -73,7 +72,6 @@ public function toPushover(): PushoverMessage ); } - public function toSlack(): SlackMessage { $title = 'Database backup successful'; diff --git a/app/Notifications/Server/HighDiskUsage.php b/app/Notifications/Server/HighDiskUsage.php index aea9abd031..4c9da12e35 100644 --- a/app/Notifications/Server/HighDiskUsage.php +++ b/app/Notifications/Server/HighDiskUsage.php @@ -65,8 +65,8 @@ public function toPushover(): PushoverMessage level: 'warning', message: "Server '{$this->server->name}' high disk usage detected!

Disk usage: {$this->disk_usage}%.
Threshold: {$this->server_disk_usage_notification_threshold}%.
Please cleanup your disk to prevent data-loss.", buttons: [ - 'Change settings' => base_url().'/server/'.$this->server->uuid."#advanced", - 'Tips for cleanup' => "https://coolify.io/docs/knowledge-base/server/automated-cleanup", + 'Change settings' => base_url().'/server/'.$this->server->uuid.'#advanced', + 'Tips for cleanup' => 'https://coolify.io/docs/knowledge-base/server/automated-cleanup', ], ); } diff --git a/app/Notifications/Test.php b/app/Notifications/Test.php index 65971a0eee..ebb8735f53 100644 --- a/app/Notifications/Test.php +++ b/app/Notifications/Test.php @@ -4,9 +4,9 @@ use App\Notifications\Channels\DiscordChannel; use App\Notifications\Channels\EmailChannel; +use App\Notifications\Channels\PushoverChannel; use App\Notifications\Channels\SlackChannel; use App\Notifications\Channels\TelegramChannel; -use App\Notifications\Channels\PushoverChannel; use App\Notifications\Dto\DiscordMessage; use App\Notifications\Dto\PushoverMessage; use App\Notifications\Dto\SlackMessage; diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index aa3579f8da..428f78cb5a 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -11,6 +11,7 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use SocialiteProviders\Authentik\AuthentikExtendSocialite; use SocialiteProviders\Azure\AzureExtendSocialite; +use SocialiteProviders\Infomaniak\InfomaniakExtendSocialite; use SocialiteProviders\Manager\SocialiteWasCalled; class EventServiceProvider extends ServiceProvider @@ -25,6 +26,7 @@ class EventServiceProvider extends ServiceProvider SocialiteWasCalled::class => [ AzureExtendSocialite::class.'@handle', AuthentikExtendSocialite::class.'@handle', + InfomaniakExtendSocialite::class.'@handle', ], ProxyStarted::class => [ ProxyStartedNotification::class, diff --git a/app/View/Components/Forms/Button.php b/app/View/Components/Forms/Button.php index da8b46dec5..bf88d3f885 100644 --- a/app/View/Components/Forms/Button.php +++ b/app/View/Components/Forms/Button.php @@ -15,7 +15,8 @@ public function __construct( public bool $disabled = false, public bool $noStyle = false, public ?string $modalId = null, - public string $defaultClass = 'button' + public string $defaultClass = 'button', + public bool $showLoadingIndicator = true, ) { if ($this->noStyle) { $this->defaultClass = ''; diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php index 875866e2ff..307c7ed1b7 100644 --- a/bootstrap/helpers/api.php +++ b/bootstrap/helpers/api.php @@ -166,6 +166,7 @@ function removeUnnecessaryFieldsFromRequest(Request $request) { $request->offsetUnset('project_uuid'); $request->offsetUnset('environment_name'); + $request->offsetUnset('environment_uuid'); $request->offsetUnset('destination_uuid'); $request->offsetUnset('server_uuid'); $request->offsetUnset('type'); diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index eda2133a75..74d26e2f59 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -188,7 +188,22 @@ function get_port_from_dockerfile($dockerfile): ?int return null; } -function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'application', $subType = null, $subId = null) +function defaultDatabaseLabels($database) +{ + $labels = collect([]); + $labels->push('coolify.managed=true'); + $labels->push('coolify.type=database'); + $labels->push('coolify.databaseId='.$database->id); + $labels->push('coolify.resourceName='.Str::slug($database->name)); + $labels->push('coolify.serviceName='.Str::slug($database->name)); + $labels->push('coolify.projectName='.Str::slug($database->project()->name)); + $labels->push('coolify.environmentName='.Str::slug($database->environment->name)); + $labels->push('coolify.database.subType='.$database->type()); + + return $labels; +} + +function defaultLabels($id, $name, string $projectName, string $resourceName, string $environment, $pull_request_id = 0, string $type = 'application', $subType = null, $subId = null, $subName = null) { $labels = collect([]); $labels->push('coolify.managed=true'); @@ -196,14 +211,21 @@ function defaultLabels($id, $name, $pull_request_id = 0, string $type = 'applica $labels->push('coolify.'.$type.'Id='.$id); $labels->push("coolify.type=$type"); $labels->push('coolify.name='.$name); + $labels->push('coolify.resourceName='.Str::slug($resourceName)); + $labels->push('coolify.projectName='.Str::slug($projectName)); + $labels->push('coolify.serviceName='.Str::slug($subName ?? $resourceName)); + $labels->push('coolify.environmentName='.Str::slug($environment)); + $labels->push('coolify.pullRequestId='.$pull_request_id); if ($type === 'service') { $subId && $labels->push('coolify.service.subId='.$subId); $subType && $labels->push('coolify.service.subType='.$subType); + $subName && $labels->push('coolify.service.subName='.Str::slug($subName)); } return $labels; } + function generateServiceSpecificFqdns(ServiceApplication|Application $resource) { if ($resource->getMorphClass() === \App\Models\ServiceApplication::class) { diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php index fd2e1231fb..cd99713a27 100644 --- a/bootstrap/helpers/services.php +++ b/bootstrap/helpers/services.php @@ -2,6 +2,7 @@ use App\Models\Application; use App\Models\EnvironmentVariable; +use App\Models\Service; use App\Models\ServiceApplication; use App\Models\ServiceDatabase; use Illuminate\Support\Stringable; @@ -119,7 +120,10 @@ 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', $variableName)->first(); + $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', $variableName) + ->first(); $fqdn = Url::fromString($resourceFqdns); $port = $fqdn->getPort(); $path = $fqdn->getPath(); @@ -134,7 +138,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) } if ($port) { $variableName = $variableName."_$port"; - $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); + $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', $variableName) + ->first(); if ($generatedEnv) { if ($path === '/') { $generatedEnv->value = $fqdn; @@ -145,7 +152,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) } } $variableName = 'SERVICE_URL_'.str($resource->name)->upper()->replace('-', ''); - $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); + $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', $variableName) + ->first(); $url = Url::fromString($fqdn); $port = $url->getPort(); $path = $url->getPath(); @@ -161,7 +171,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) } if ($port) { $variableName = $variableName."_$port"; - $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); + $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', $variableName) + ->first(); if ($generatedEnv) { if ($path === '/') { $generatedEnv->value = $url; @@ -179,10 +192,16 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) $path = $host->getPath(); $host = $host->getScheme().'://'.$host->getHost(); if ($port) { - $port_envs = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'like', "SERVICE_FQDN_%_$port")->get(); + $port_envs = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', 'like', "SERVICE_FQDN_%_$port") + ->get(); foreach ($port_envs as $port_env) { $service_fqdn = str($port_env->key)->beforeLast('_')->after('SERVICE_FQDN_'); - $env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_FQDN_'.$service_fqdn)->first(); + $env = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', 'SERVICE_FQDN_'.$service_fqdn) + ->first(); if ($env) { if ($path === '/') { $env->value = $host; @@ -198,10 +217,16 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) } $port_env->save(); } - $port_envs_url = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'like', "SERVICE_URL_%_$port")->get(); + $port_envs_url = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', 'like', "SERVICE_URL_%_$port") + ->get(); foreach ($port_envs_url as $port_env_url) { $service_url = str($port_env_url->key)->beforeLast('_')->after('SERVICE_URL_'); - $env = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', 'SERVICE_URL_'.$service_url)->first(); + $env = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', 'SERVICE_URL_'.$service_url) + ->first(); if ($env) { if ($path === '/') { $env->value = $url; @@ -219,7 +244,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) } } else { $variableName = 'SERVICE_FQDN_'.str($resource->name)->upper()->replace('-', ''); - $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); + $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', $variableName) + ->first(); $fqdn = Url::fromString($fqdn); $fqdn = $fqdn->getScheme().'://'.$fqdn->getHost().$fqdn->getPath(); if ($generatedEnv) { @@ -227,7 +255,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) $generatedEnv->save(); } $variableName = 'SERVICE_URL_'.str($resource->name)->upper()->replace('-', ''); - $generatedEnv = EnvironmentVariable::where('service_id', $resource->service_id)->where('key', $variableName)->first(); + $generatedEnv = EnvironmentVariable::where('resourceable_type', Service::class) + ->where('resourceable_id', $resource->service_id) + ->where('key', $variableName) + ->first(); $url = Url::fromString($fqdn); $url = $url->getHost().$url->getPath(); if ($generatedEnv) { diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 32b7463def..5d34c63b6e 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1828,7 +1828,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'key' => $key, 'value' => $fqdn, 'is_build_time' => false, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, 'is_preview' => false, ]); } @@ -1840,7 +1841,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } $env = EnvironmentVariable::where([ 'key' => $key, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ])->first(); if ($env) { $env_url = Url::fromString($savedService->fqdn); @@ -1863,14 +1865,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal if ($value?->startsWith('$')) { $foundEnv = EnvironmentVariable::where([ 'key' => $key, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ])->first(); $value = replaceVariables($value); $key = $value; if ($value->startsWith('SERVICE_')) { $foundEnv = EnvironmentVariable::where([ 'key' => $key, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ])->first(); ['command' => $command, 'forService' => $forService, 'generatedValue' => $generatedValue, 'port' => $port] = parseEnvVariable($value); if (! is_null($command)) { @@ -1904,7 +1908,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'key' => $key, 'value' => $fqdn, 'is_build_time' => false, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, 'is_preview' => false, ]); } @@ -1921,7 +1926,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } $env = EnvironmentVariable::where([ 'key' => $key, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ])->first(); if ($env) { $env_url = Url::fromString($env->value); @@ -1941,7 +1947,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'key' => $key, 'value' => $generatedValue, 'is_build_time' => false, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, 'is_preview' => false, ]); } @@ -1966,18 +1973,21 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } $foundEnv = EnvironmentVariable::where([ 'key' => $key, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ])->first(); if ($foundEnv) { $defaultValue = data_get($foundEnv, 'value'); } EnvironmentVariable::updateOrCreate([ 'key' => $key, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $defaultValue, 'is_build_time' => false, - 'service_id' => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, 'is_preview' => false, ]); } @@ -1989,7 +1999,17 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } else { $fqdns = collect(data_get($savedService, 'fqdns'))->filter(); } - $defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id); + $defaultLabels = defaultLabels( + id: $resource->id, + name: $containerName, + projectName: $resource->project()->name, + resourceName: $resource->name, + type: 'service', + subType: $isDatabase ? 'database' : 'application', + subId: $savedService->id, + subName: $savedService->name, + environment: $resource->environment->name, + ); $serviceLabels = $serviceLabels->merge($defaultLabels); if (! $isDatabase && $fqdns->count() > 0) { if ($fqdns) { @@ -2817,7 +2837,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal } } } - $defaultLabels = defaultLabels($resource->id, $containerName, $pull_request_id, type: 'application'); + + $defaultLabels = defaultLabels( + id: $resource->id, + name: $containerName, + projectName: $resource->project()->name, + resourceName: $resource->name, + environment: $resource->environment->name, + pull_request_id: $pull_request_id, + type: 'application' + ); $serviceLabels = $serviceLabels->merge($defaultLabels); if ($server->isLogDrainEnabled()) { @@ -2840,6 +2869,10 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal data_set($service, 'container_name', $containerName); data_forget($service, 'volumes.*.content'); data_forget($service, 'volumes.*.isDirectory'); + data_forget($service, 'volumes.*.is_directory'); + data_forget($service, 'exclude_from_hc'); + data_set($service, 'environment', $serviceVariables->toArray()); + updateCompose($savedService); return $service; }); @@ -2878,13 +2911,11 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } if ($isApplication) { - $nameOfId = 'application_id'; $pullRequestId = $pull_request_id; $isPullRequest = $pullRequestId == 0 ? false : true; $server = data_get($resource, 'destination.server'); $fileStorages = $resource->fileStorages(); } elseif ($isService) { - $nameOfId = 'service_id'; $server = data_get($resource, 'server'); $allServices = get_service_templates(); } else { @@ -3051,9 +3082,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } if (substr_count(str($key)->value(), '_') === 2) { - $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $key->value(), - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $fqdn, 'is_build_time' => false, @@ -3062,9 +3094,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } if (substr_count(str($key)->value(), '_') === 3) { $newKey = str($key)->beforeLast('_'); - $resource->environment_variables()->where('key', $newKey->value())->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $newKey->value(), - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $fqdn, 'is_build_time' => false, @@ -3080,7 +3113,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $key = str($key); $value = replaceVariables($value); $command = parseCommandFromMagicEnvVariable($key); - $found = $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->first(); + $found = $resource->environment_variables()->where('key', $key->value())->where('resourceable_type', get_class($resource))->where('resourceable_id', $resource->id)->first(); if ($found) { continue; } @@ -3094,9 +3127,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } elseif ($isService) { $fqdn = generateFqdn($server, "$fqdnFor-$uuid"); } - $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $key->value(), - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $fqdn, 'is_build_time' => false, @@ -3113,9 +3147,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $fqdn = generateFqdn($server, "$fqdnFor-$uuid"); } $fqdn = str($fqdn)->replace('http://', '')->replace('https://', ''); - $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $key->value(), - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $fqdn, 'is_build_time' => false, @@ -3123,9 +3158,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int ]); } else { $value = generateEnvValue($command, $resource); - $resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $key->value(), - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $value, 'is_build_time' => false, @@ -3473,9 +3509,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $originalValue = $value; $parsedValue = replaceVariables($value); if ($value->startsWith('$SERVICE_')) { - $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $key, - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $value, 'is_build_time' => false, @@ -3489,9 +3526,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } if ($key->value() === $parsedValue->value()) { $value = null; - $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $key, - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $value, 'is_build_time' => false, @@ -3525,22 +3563,24 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int if ($originalValue->value() === $value->value()) { // This means the variable does not have a default value, so it needs to be created in Coolify $parsedKeyValue = replaceVariables($value); - $resource->environment_variables()->where('key', $parsedKeyValue)->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $parsedKeyValue, - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'is_build_time' => false, 'is_preview' => false, 'is_required' => $isRequired, ]); // Add the variable to the environment so it will be shown in the deployable compose file - $environment[$parsedKeyValue->value()] = $resource->environment_variables()->where('key', $parsedKeyValue)->where($nameOfId, $resource->id)->first()->value; + $environment[$parsedKeyValue->value()] = $resource->environment_variables()->where('key', $parsedKeyValue)->where('resourceable_type', get_class($resource))->where('resourceable_id', $resource->id)->first()->value; continue; } - $resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([ + $resource->environment_variables()->firstOrCreate([ 'key' => $key, - $nameOfId => $resource->id, + 'resourceable_type' => get_class($resource), + 'resourceable_id' => $resource->id, ], [ 'value' => $value, 'is_build_time' => false, @@ -3600,11 +3640,15 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } } } + $defaultLabels = defaultLabels( id: $resource->id, name: $containerName, + projectName: $resource->project()->name, + resourceName: $resource->name, pull_request_id: $pullRequestId, - type: 'application' + type: 'application', + environment: $resource->environment->name, ); } elseif ($isService) { if ($savedService->serviceType()) { @@ -3612,7 +3656,18 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int } else { $fqdns = collect(data_get($savedService, 'fqdns'))->filter(); } - $defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id); + + $defaultLabels = defaultLabels( + id: $resource->id, + name: $containerName, + projectName: $resource->project()->name, + resourceName: $resource->name, + type: 'service', + subType: $isDatabase ? 'database' : 'application', + subId: $savedService->id, + subName: $savedService->human_name ?? $savedService->name, + environment: $resource->environment->name, + ); } // Add COOLIFY_FQDN & COOLIFY_URL to environment if (! $isDatabase && $fqdns instanceof Collection && $fqdns->count() > 0) { diff --git a/bootstrap/helpers/socialite.php b/bootstrap/helpers/socialite.php index 130227e815..09dffb78ab 100644 --- a/bootstrap/helpers/socialite.php +++ b/bootstrap/helpers/socialite.php @@ -40,6 +40,7 @@ function get_socialite_provider(string $provider) 'github' => \Laravel\Socialite\Two\GithubProvider::class, 'gitlab' => \Laravel\Socialite\Two\GitlabProvider::class, 'google' => \Laravel\Socialite\Two\GoogleProvider::class, + 'infomaniak' => \SocialiteProviders\Infomaniak\Provider::class, ]; return Socialite::buildProvider( diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php index ab9ee9b9d6..510516a2f9 100644 --- a/bootstrap/helpers/subscriptions.php +++ b/bootstrap/helpers/subscriptions.php @@ -69,6 +69,7 @@ function allowedPathsForUnsubscribedAccounts() 'logout', 'force-password-reset', 'livewire/update', + 'admin', ]; } function allowedPathsForBoardingAccounts() diff --git a/composer.json b/composer.json index 3a06a5beb2..055059a56a 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "server" ], "require": { - "php": "^8.2", + "php": "^8.4", "3sidedcube/laravel-redoc": "^1.0", "danharrin/livewire-rate-limiting": "2.0.0", "doctrine/dbal": "^4.2", @@ -40,6 +40,7 @@ "resend/resend-laravel": "^0.15.0", "sentry/sentry-laravel": "^4.6", "socialiteproviders/authentik": "^5.2", + "socialiteproviders/infomaniak": "^4.0", "socialiteproviders/microsoft-azure": "^5.1", "spatie/laravel-activitylog": "^4.7.3", "spatie/laravel-data": "^4.11", @@ -50,10 +51,11 @@ "symfony/yaml": "^7.1.6", "visus/cuid2": "^4.1.0", "yosymfony/toml": "^1.0", - "zircote/swagger-php": "^4.10" + "zircote/swagger-php": "^5.0" }, "require-dev": { "barryvdh/laravel-debugbar": "^3.13", + "driftingly/rector-laravel": "^2.0", "fakerphp/faker": "^1.21.0", "laravel/dusk": "^8.0", "laravel/pint": "^1.16", @@ -61,9 +63,10 @@ "mockery/mockery": "^1.5.1", "nunomaduro/collision": "^8.1", "pestphp/pest": "^3.5", - "phpstan/phpstan": "^1.12.10", - "phpunit/phpunit": "^11.4", - "serversideup/spin": "^2.3", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^11.5", + "rector/rector": "^2.0", + "serversideup/spin": "^3.0", "spatie/laravel-ignition": "^2.1.0", "symfony/http-client": "^7.1" }, diff --git a/composer.lock b/composer.lock index 7bb7f24e06..7ca8219c7c 100644 --- a/composer.lock +++ b/composer.lock @@ -7415,6 +7415,57 @@ }, "time": "2023-11-07T22:21:16+00:00" }, + { + "name": "socialiteproviders/infomaniak", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/SocialiteProviders/Infomaniak.git", + "reference": "9796ad686204443bfdf3ff19a6c409e8771667e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SocialiteProviders/Infomaniak/zipball/9796ad686204443bfdf3ff19a6c409e8771667e1", + "reference": "9796ad686204443bfdf3ff19a6c409e8771667e1", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^8.0", + "socialiteproviders/manager": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "SocialiteProviders\\Infomaniak\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Leopold Jacquot", + "email": "leopold.jacquot@infomaniak.com" + } + ], + "description": "Infomaniak OAuth2 Provider for Laravel Socialite", + "keywords": [ + "infomaniak", + "laravel", + "oauth", + "oauth2", + "provider", + "socialite" + ], + "support": { + "docs": "https://socialiteproviders.com/qq", + "issues": "https://github.com/socialiteproviders/providers/issues", + "source": "https://github.com/socialiteproviders/providers" + }, + "time": "2024-11-20T05:42:36+00:00" + }, { "name": "socialiteproviders/manager", "version": "v4.8.0", @@ -11578,36 +11629,41 @@ }, { "name": "zircote/swagger-php", - "version": "4.11.1", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/zircote/swagger-php.git", - "reference": "7df10e8ec47db07c031db317a25bef962b4e5de1" + "reference": "2d365300e9bc64b935fca99a8f0bc8fe4314deb4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zircote/swagger-php/zipball/7df10e8ec47db07c031db317a25bef962b4e5de1", - "reference": "7df10e8ec47db07c031db317a25bef962b4e5de1", + "url": "https://api.github.com/repos/zircote/swagger-php/zipball/2d365300e9bc64b935fca99a8f0bc8fe4314deb4", + "reference": "2d365300e9bc64b935fca99a8f0bc8fe4314deb4", "shasum": "" }, "require": { "ext-json": "*", - "php": ">=7.2", + "nikic/php-parser": "^4.19 || ^5.0", + "php": ">=7.4", "psr/log": "^1.1 || ^2.0 || ^3.0", "symfony/deprecation-contracts": "^2 || ^3", - "symfony/finder": ">=2.2", - "symfony/yaml": ">=3.3" + "symfony/finder": "^5.0 || ^6.0 || ^7.0", + "symfony/yaml": "^5.0 || ^6.0 || ^7.0" + }, + "conflict": { + "symfony/process": ">=6, <6.4.14" }, "require-dev": { "composer/package-versions-deprecated": "^1.11", - "doctrine/annotations": "^1.7 || ^2.0", - "friendsofphp/php-cs-fixer": "^2.17 || 3.62.0", - "phpstan/phpstan": "^1.6", - "phpunit/phpunit": ">=8", - "vimeo/psalm": "^4.23" + "doctrine/annotations": "^2.0", + "friendsofphp/php-cs-fixer": "^3.62.0", + "phpstan/phpstan": "^1.6 || ^2.0", + "phpunit/phpunit": "^9.0", + "rector/rector": "^1.0 || ^2.0", + "vimeo/psalm": "^4.30 || ^5.0" }, "suggest": { - "doctrine/annotations": "^1.7 || ^2.0" + "doctrine/annotations": "^2.0" }, "bin": [ "bin/openapi" @@ -11615,7 +11671,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.x-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -11653,9 +11709,9 @@ ], "support": { "issues": "https://github.com/zircote/swagger-php/issues", - "source": "https://github.com/zircote/swagger-php/tree/4.11.1" + "source": "https://github.com/zircote/swagger-php/tree/5.0.1" }, - "time": "2024-10-15T19:20:02+00:00" + "time": "2025-01-09T02:31:15+00:00" } ], "packages-dev": [ @@ -11836,6 +11892,41 @@ ], "time": "2024-12-11T14:50:44+00:00" }, + { + "name": "driftingly/rector-laravel", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/driftingly/rector-laravel.git", + "reference": "973d87d51c1a0d42340758bbddaef15a14155a54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/973d87d51c1a0d42340758bbddaef15a14155a54", + "reference": "973d87d51c1a0d42340758bbddaef15a14155a54", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "rector/rector": "^2.0" + }, + "type": "rector-extension", + "autoload": { + "psr-4": { + "RectorLaravel\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Rector upgrades rules for Laravel Framework", + "support": { + "issues": "https://github.com/driftingly/rector-laravel/issues", + "source": "https://github.com/driftingly/rector-laravel/tree/2.0.1" + }, + "time": "2025-01-03T16:28:38+00:00" + }, { "name": "fakerphp/faker", "version": "v1.24.1", @@ -13120,7 +13211,7 @@ "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -13587,6 +13678,65 @@ ], "time": "2024-12-11T10:52:48+00:00" }, + { + "name": "rector/rector", + "version": "2.0.6", + "source": { + "type": "git", + "url": "https://github.com/rectorphp/rector.git", + "reference": "fa0cb009dc3df084bf549032ae4080a0481a2036" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/fa0cb009dc3df084bf549032ae4080a0481a2036", + "reference": "fa0cb009dc3df084bf549032ae4080a0481a2036", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0", + "phpstan/phpstan": "^2.1.1" + }, + "conflict": { + "rector/rector-doctrine": "*", + "rector/rector-downgrade-php": "*", + "rector/rector-phpunit": "*", + "rector/rector-symfony": "*" + }, + "suggest": { + "ext-dom": "To manipulate phpunit.xml via the custom-rule command" + }, + "bin": [ + "bin/rector" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Instant Upgrade and Automated Refactoring of any PHP code", + "keywords": [ + "automation", + "dev", + "migration", + "refactoring" + ], + "support": { + "issues": "https://github.com/rectorphp/rector/issues", + "source": "https://github.com/rectorphp/rector/tree/2.0.6" + }, + "funding": [ + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2025-01-06T10:38:36+00:00" + }, { "name": "sebastian/cli-parser", "version": "3.0.2", @@ -14515,16 +14665,16 @@ }, { "name": "serversideup/spin", - "version": "v2.3.0", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/serversideup/spin.git", - "reference": "e7f742dfe54146196da26876670f368c11852df3" + "reference": "ed3ee8f2b9eeaf9e2a0654438441a1eb8ccf5d3d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/serversideup/spin/zipball/e7f742dfe54146196da26876670f368c11852df3", - "reference": "e7f742dfe54146196da26876670f368c11852df3", + "url": "https://api.github.com/repos/serversideup/spin/zipball/ed3ee8f2b9eeaf9e2a0654438441a1eb8ccf5d3d", + "reference": "ed3ee8f2b9eeaf9e2a0654438441a1eb8ccf5d3d", "shasum": "" }, "bin": [ @@ -14548,7 +14698,7 @@ "description": "Replicate your production environment locally using Docker. Just run \"spin up\". It's really that easy.", "support": { "issues": "https://github.com/serversideup/spin/issues", - "source": "https://github.com/serversideup/spin/tree/v2.3.0" + "source": "https://github.com/serversideup/spin/tree/v3.0.1" }, "funding": [ { @@ -14556,7 +14706,7 @@ "type": "github" } ], - "time": "2024-10-15T15:12:28+00:00" + "time": "2024-12-20T17:23:06+00:00" }, { "name": "spatie/error-solutions", @@ -15212,12 +15362,12 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.2" + "php": "^8.4" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/config/constants.php b/config/constants.php index e220eacfd6..537fbfc6ca 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.380', + 'version' => '4.0.0-beta.381', 'self_hosted' => env('SELF_HOSTED', true), 'autoupdate' => env('AUTOUPDATE'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), @@ -65,26 +65,6 @@ ], ], - 'limits' => [ - 'trial_period' => 0, - 'server' => [ - 'zero' => 0, - 'self-hosted' => 999999999999, - 'basic' => env('LIMIT_SERVER_BASIC', 2), - 'pro' => env('LIMIT_SERVER_PRO', 10), - 'ultimate' => env('LIMIT_SERVER_ULTIMATE', 25), - 'dynamic' => env('LIMIT_SERVER_DYNAMIC', 2), - ], - 'email' => [ - 'zero' => true, - 'self-hosted' => true, - 'basic' => true, - 'pro' => true, - 'ultimate' => true, - 'dynamic' => true, - ], - ], - 'sentry' => [ 'sentry_dsn' => env('SENTRY_DSN'), ], diff --git a/database/migrations/2024_11_22_124742_add_uuid_to_environments_table.php b/database/migrations/2024_11_22_124742_add_uuid_to_environments_table.php new file mode 100644 index 0000000000..b106427afe --- /dev/null +++ b/database/migrations/2024_11_22_124742_add_uuid_to_environments_table.php @@ -0,0 +1,38 @@ +string('uuid')->after('id')->nullable()->unique(); + }); + + DB::table('environments') + ->whereNull('uuid') + ->chunkById(100, function ($environments) { + foreach ($environments as $environment) { + DB::table('environments') + ->where('id', $environment->id) + ->update(['uuid' => (string) new Cuid2]); + } + }); + + Schema::table('environments', function (Blueprint $table) { + $table->string('uuid')->nullable(false)->change(); + }); + } + + public function down(): void + { + Schema::table('environments', function (Blueprint $table) { + $table->dropColumn('uuid'); + }); + } +}; diff --git a/database/migrations/2024_12_16_134437_add_resourceable_columns_to_environment_variables_table.php b/database/migrations/2024_12_16_134437_add_resourceable_columns_to_environment_variables_table.php new file mode 100644 index 0000000000..c4b7186380 --- /dev/null +++ b/database/migrations/2024_12_16_134437_add_resourceable_columns_to_environment_variables_table.php @@ -0,0 +1,165 @@ +string('resourceable_type')->nullable(); + $table->unsignedBigInteger('resourceable_id')->nullable(); + $table->index(['resourceable_type', 'resourceable_id']); + }); + + // Populate the new columns + DB::table('environment_variables')->whereNotNull('application_id') + ->update([ + 'resourceable_type' => 'App\\Models\\Application', + 'resourceable_id' => DB::raw('application_id'), + ]); + + DB::table('environment_variables')->whereNotNull('service_id') + ->update([ + 'resourceable_type' => 'App\\Models\\Service', + 'resourceable_id' => DB::raw('service_id'), + ]); + + DB::table('environment_variables')->whereNotNull('standalone_postgresql_id') + ->update([ + 'resourceable_type' => 'App\\Models\\StandalonePostgresql', + 'resourceable_id' => DB::raw('standalone_postgresql_id'), + ]); + + DB::table('environment_variables')->whereNotNull('standalone_redis_id') + ->update([ + 'resourceable_type' => 'App\\Models\\StandaloneRedis', + 'resourceable_id' => DB::raw('standalone_redis_id'), + ]); + + DB::table('environment_variables')->whereNotNull('standalone_mongodb_id') + ->update([ + 'resourceable_type' => 'App\\Models\\StandaloneMongodb', + 'resourceable_id' => DB::raw('standalone_mongodb_id'), + ]); + + DB::table('environment_variables')->whereNotNull('standalone_mysql_id') + ->update([ + 'resourceable_type' => 'App\\Models\\StandaloneMysql', + 'resourceable_id' => DB::raw('standalone_mysql_id'), + ]); + + DB::table('environment_variables')->whereNotNull('standalone_mariadb_id') + ->update([ + 'resourceable_type' => 'App\\Models\\StandaloneMariadb', + 'resourceable_id' => DB::raw('standalone_mariadb_id'), + ]); + + DB::table('environment_variables')->whereNotNull('standalone_keydb_id') + ->update([ + 'resourceable_type' => 'App\\Models\\StandaloneKeydb', + 'resourceable_id' => DB::raw('standalone_keydb_id'), + ]); + + DB::table('environment_variables')->whereNotNull('standalone_dragonfly_id') + ->update([ + 'resourceable_type' => 'App\\Models\\StandaloneDragonfly', + 'resourceable_id' => DB::raw('standalone_dragonfly_id'), + ]); + + DB::table('environment_variables')->whereNotNull('standalone_clickhouse_id') + ->update([ + 'resourceable_type' => 'App\\Models\\StandaloneClickhouse', + 'resourceable_id' => DB::raw('standalone_clickhouse_id'), + ]); + + // After successful migration, we can drop the old foreign key columns + Schema::table('environment_variables', function (Blueprint $table) { + $table->dropColumn([ + 'application_id', + 'service_id', + 'standalone_postgresql_id', + 'standalone_redis_id', + 'standalone_mongodb_id', + 'standalone_mysql_id', + 'standalone_mariadb_id', + 'standalone_keydb_id', + 'standalone_dragonfly_id', + 'standalone_clickhouse_id', + ]); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('environment_variables', function (Blueprint $table) { + // Restore the old columns + $table->unsignedBigInteger('application_id')->nullable(); + $table->unsignedBigInteger('service_id')->nullable(); + $table->unsignedBigInteger('standalone_postgresql_id')->nullable(); + $table->unsignedBigInteger('standalone_redis_id')->nullable(); + $table->unsignedBigInteger('standalone_mongodb_id')->nullable(); + $table->unsignedBigInteger('standalone_mysql_id')->nullable(); + $table->unsignedBigInteger('standalone_mariadb_id')->nullable(); + $table->unsignedBigInteger('standalone_keydb_id')->nullable(); + $table->unsignedBigInteger('standalone_dragonfly_id')->nullable(); + $table->unsignedBigInteger('standalone_clickhouse_id')->nullable(); + }); + + Schema::table('environment_variables', function (Blueprint $table) { + // Restore data from polymorphic relationship + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\Application') + ->update(['application_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\Service') + ->update(['service_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\StandalonePostgresql') + ->update(['standalone_postgresql_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\StandaloneRedis') + ->update(['standalone_redis_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\StandaloneMongodb') + ->update(['standalone_mongodb_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\StandaloneMysql') + ->update(['standalone_mysql_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\StandaloneMariadb') + ->update(['standalone_mariadb_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\StandaloneKeydb') + ->update(['standalone_keydb_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\StandaloneDragonfly') + ->update(['standalone_dragonfly_id' => DB::raw('resourceable_id')]); + + DB::table('environment_variables') + ->where('resourceable_type', 'App\\Models\\StandaloneClickhouse') + ->update(['standalone_clickhouse_id' => DB::raw('resourceable_id')]); + + // Drop the polymorphic columns + $table->dropIndex(['resourceable_type', 'resourceable_id']); + $table->dropColumn(['resourceable_type', 'resourceable_id']); + }); + } +}; diff --git a/database/migrations/2024_12_17_140637_add_server_disk_usage_check_frequency_to_server_settings_table.php b/database/migrations/2024_12_17_140637_add_server_disk_usage_check_frequency_to_server_settings_table.php new file mode 100644 index 0000000000..be0f4bc0f4 --- /dev/null +++ b/database/migrations/2024_12_17_140637_add_server_disk_usage_check_frequency_to_server_settings_table.php @@ -0,0 +1,28 @@ +string('server_disk_usage_check_frequency')->default('0 23 * * *'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('server_settings', function (Blueprint $table) { + $table->dropColumn('server_disk_usage_check_frequency'); + }); + } +}; diff --git a/database/seeders/OauthSettingSeeder.php b/database/seeders/OauthSettingSeeder.php index df7619fec5..fa692d2dc6 100644 --- a/database/seeders/OauthSettingSeeder.php +++ b/database/seeders/OauthSettingSeeder.php @@ -21,6 +21,7 @@ public function run(): void 'gitlab', 'google', 'authentik', + 'infomaniak', ]); $isOauthSeeded = OauthSetting::count() > 0; diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 459b93ac60..e651b4add5 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -13,46 +13,14 @@ services: - /data/coolify/backups:/var/www/html/storage/app/backups - /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance environment: - - APP_ENV=production - - APP_NAME - - APP_ID - - APP_KEY - - APP_URL - - APP_DEBUG - - DB_DATABASE - - DB_USERNAME - - DB_PASSWORD - - DB_HOST - - DB_PORT - - DB_CONNECTION - - QUEUE_CONNECTION - - REDIS_HOST - - REDIS_PASSWORD - - HORIZON_BALANCE - - HORIZON_MIN_PROCESSES - - HORIZON_MAX_PROCESSES - - HORIZON_BALANCE_MAX_SHIFT - - HORIZON_BALANCE_COOLDOWN - - SSL_MODE=off - - PHP_MEMORY_LIMIT - - PHP_PM_CONTROL=dynamic - - PHP_PM_START_SERVERS=1 - - PHP_PM_MIN_SPARE_SERVERS=1 - - PHP_PM_MAX_SPARE_SERVERS=10 - - PUSHER_HOST - - PUSHER_BACKEND_HOST - - PUSHER_PORT - - PUSHER_BACKEND_PORT - - PUSHER_SCHEME - - PUSHER_APP_ID - - PUSHER_APP_KEY - - PUSHER_APP_SECRET - - TERMINAL_PROTOCOL - - TERMINAL_HOST - - TERMINAL_PORT - - AUTOUPDATE - - SSH_MUX_ENABLED - - SSH_MUX_PERSIST_TIME + - APP_ENV=${APP_ENV:-production} + - PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-128M} + - PHP_FPM_PM_CONTROL=${PHP_FPM_PM_CONTROL:-dynamic} + - PHP_FPM_PM_START_SERVERS=${PHP_FPM_PM_START_SERVERS:-1} + - PHP_FPM_PM_MIN_SPARE_SERVERS=${PHP_FPM_PM_MIN_SPARE_SERVERS:-1} + - PHP_FPM_PM_MAX_SPARE_SERVERS=${PHP_FPM_PM_MAX_SPARE_SERVERS:-10} + env_file: + - /data/coolify/source/.env ports: - "${APP_PORT:-8000}:8080" expose: diff --git a/docker/coolify-helper/Dockerfile b/docker/coolify-helper/Dockerfile index 741fff764e..acc44d02a0 100644 --- a/docker/coolify-helper/Dockerfile +++ b/docker/coolify-helper/Dockerfile @@ -1,19 +1,19 @@ # Versions # https://hub.docker.com/_/alpine -ARG BASE_IMAGE=alpine:3.20 +ARG BASE_IMAGE=alpine:3.21 # https://download.docker.com/linux/static/stable/ -ARG DOCKER_VERSION=27.3.1 +ARG DOCKER_VERSION=27.4.1 # https://github.com/docker/compose/releases -ARG DOCKER_COMPOSE_VERSION=2.30.3 +ARG DOCKER_COMPOSE_VERSION=2.32.2 # https://github.com/docker/buildx/releases -ARG DOCKER_BUILDX_VERSION=0.18.0 +ARG DOCKER_BUILDX_VERSION=0.19.3 # https://github.com/buildpacks/pack/releases -ARG PACK_VERSION=0.35.1 +ARG PACK_VERSION=0.36.2 # https://github.com/railwayapp/nixpacks/releases -ARG NIXPACKS_VERSION=1.29.0 -# https://hub.docker.com/r/minio/mc/tags -ARG MINIO_VERSION=RELEASE.2024-03-07T00-31-49Z +ARG NIXPACKS_VERSION=1.30.0 +# https://github.com/minio/mc/releases +ARG MINIO_VERSION=RELEASE.2024-11-21T17-21-54Z FROM minio/mc:${MINIO_VERSION} AS minio-client diff --git a/docker/coolify-realtime/Dockerfile b/docker/coolify-realtime/Dockerfile index b9f3c1e621..be72bd836d 100644 --- a/docker/coolify-realtime/Dockerfile +++ b/docker/coolify-realtime/Dockerfile @@ -1,9 +1,13 @@ -FROM quay.io/soketi/soketi:1.6-16-alpine +# Versions +# https://github.com/soketi/soketi/releases +ARG SOKETI_VERSION=1.6-16-alpine +# https://github.com/cloudflare/cloudflared/releases +ARG CLOUDFLARED_VERSION=2025.1.0 +FROM quay.io/soketi/soketi:${SOKETI_VERSION} ARG TARGETPLATFORM -# https://github.com/cloudflare/cloudflared/releases -ARG CLOUDFLARED_VERSION=2024.4.1 +ARG CLOUDFLARED_VERSION WORKDIR /terminal RUN apk add --no-cache openssh-client make g++ python3 curl @@ -13,14 +17,12 @@ RUN npm rebuild node-pty --update-binary COPY docker/coolify-realtime/soketi-entrypoint.sh /soketi-entrypoint.sh COPY docker/coolify-realtime/terminal-server.js /terminal/terminal-server.js -RUN /bin/sh -c "if [[ ${TARGETPLATFORM} == 'linux/amd64' ]]; then \ - echo 'amd64' && \ - curl -sSL https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \ - ;fi" - -RUN /bin/sh -c "if [[ ${TARGETPLATFORM} == 'linux/arm64' ]]; then \ - echo 'arm64' && \ - curl -L https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared \ - ;fi" +# Install Cloudflared based on architecture +RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ]; then \ + curl -sSL "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-amd64" -o /usr/local/bin/cloudflared; \ + elif [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \ + curl -sSL "https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARED_VERSION}/cloudflared-linux-arm64" -o /usr/local/bin/cloudflared; \ + fi && \ + chmod +x /usr/local/bin/cloudflared ENTRYPOINT ["/bin/sh", "/soketi-entrypoint.sh"] diff --git a/docker/coolify-realtime/package-lock.json b/docker/coolify-realtime/package-lock.json index 9316442aef..37f0c73eb3 100644 --- a/docker/coolify-realtime/package-lock.json +++ b/docker/coolify-realtime/package-lock.json @@ -7,9 +7,9 @@ "dependencies": { "@xterm/addon-fit": "0.10.0", "@xterm/xterm": "5.5.0", - "axios": "1.7.7", - "cookie": "1.0.1", - "dotenv": "16.4.5", + "axios": "1.7.9", + "cookie": "1.0.2", + "dotenv": "16.4.7", "node-pty": "1.0.0", "ws": "8.18.0" } @@ -36,9 +36,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -59,9 +59,9 @@ } }, "node_modules/cookie": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.1.tgz", - "integrity": "sha512-Xd8lFX4LM9QEEwxQpF9J9NTUh8pmdJO0cyRJhFiDoLTk2eH8FXlRv2IFGYVadZpqI3j8fhNrSdKCeYPxiAhLXw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", "license": "MIT", "engines": { "node": ">=18" @@ -77,9 +77,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "license": "BSD-2-Clause", "engines": { "node": ">=12" diff --git a/docker/coolify-realtime/package.json b/docker/coolify-realtime/package.json index 8b2076f8d6..fbf2fcd344 100644 --- a/docker/coolify-realtime/package.json +++ b/docker/coolify-realtime/package.json @@ -4,9 +4,9 @@ "dependencies": { "@xterm/addon-fit": "0.10.0", "@xterm/xterm": "5.5.0", - "cookie": "1.0.1", - "axios": "1.7.7", - "dotenv": "16.4.5", + "cookie": "1.0.2", + "axios": "1.7.9", + "dotenv": "16.4.7", "node-pty": "1.0.0", "ws": "8.18.0" } diff --git a/docker/development/Dockerfile b/docker/development/Dockerfile index 7d78e2854e..95933c0c23 100644 --- a/docker/development/Dockerfile +++ b/docker/development/Dockerfile @@ -2,9 +2,9 @@ # https://hub.docker.com/r/serversideup/php/tags?name=8.4-fpm-nginx-alpine ARG SERVERSIDEUP_PHP_VERSION=8.4-fpm-nginx-alpine # https://github.com/minio/mc/releases -ARG MINIO_VERSION=RELEASE.2024-11-17T19-35-25Z +ARG MINIO_VERSION=RELEASE.2024-11-21T17-21-54Z # https://github.com/cloudflare/cloudflared/releases -ARG CLOUDFLARED_VERSION=2024.11.1 +ARG CLOUDFLARED_VERSION=2025.1.0 # https://www.postgresql.org/support/versioning/ ARG POSTGRES_VERSION=15 diff --git a/docker/development/etc/nginx/site-opts.d/http.conf b/docker/development/etc/nginx/site-opts.d/http.conf index e740918a5b..a5bbd78a3a 100644 --- a/docker/development/etc/nginx/site-opts.d/http.conf +++ b/docker/development/etc/nginx/site-opts.d/http.conf @@ -37,7 +37,7 @@ location ~ \.php$ { fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; - fastcgi_buffers 8 8k; - fastcgi_buffer_size 8k; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; fastcgi_read_timeout 99; } diff --git a/docker/development/etc/php/conf.d/zzz-custom-php.ini b/docker/development/etc/php/conf.d/zzz-custom-php.ini index dcf0489045..e09eda5386 100644 --- a/docker/development/etc/php/conf.d/zzz-custom-php.ini +++ b/docker/development/etc/php/conf.d/zzz-custom-php.ini @@ -7,4 +7,4 @@ ignore_repeated_source = On upload_max_filesize = 256M post_max_size = 256M -memory_limit = ${PHP_MEMORY_LIMIT:-256M} +memory_limit = ${PHP_MEMORY_LIMIT:-512M} diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile index 09281a666e..10bd80c2b1 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile @@ -2,9 +2,9 @@ # https://hub.docker.com/r/serversideup/php/tags?name=8.4-fpm-nginx-alpine ARG SERVERSIDEUP_PHP_VERSION=8.4-fpm-nginx-alpine # https://github.com/minio/mc/releases -ARG MINIO_VERSION=RELEASE.2024-11-17T19-35-25Z +ARG MINIO_VERSION=RELEASE.2024-11-21T17-21-54Z # https://github.com/cloudflare/cloudflared/releases -ARG CLOUDFLARED_VERSION=2024.11.1 +ARG CLOUDFLARED_VERSION=2025.1.0 # https://www.postgresql.org/support/versioning/ ARG POSTGRES_VERSION=15 @@ -99,6 +99,9 @@ RUN mkdir -p /usr/local/bin && \ COPY docker/production/etc/php/conf.d/zzz-custom-php.ini /usr/local/etc/php/conf.d/zzz-custom-php.ini ENV PHP_OPCACHE_ENABLE=1 +# Configure entrypoint +COPY --chmod=755 docker/production/entrypoint.d/ /etc/entrypoint.d + # Copy application files from previous stages COPY --from=base --chown=www-data:www-data /var/www/html/vendor ./vendor COPY --from=static-assets --chown=www-data:www-data /app/public/build ./public/build diff --git a/docker/production/entrypoint.d/99-debug-mode.sh b/docker/production/entrypoint.d/99-debug-mode.sh new file mode 100644 index 0000000000..e0a1813735 --- /dev/null +++ b/docker/production/entrypoint.d/99-debug-mode.sh @@ -0,0 +1,8 @@ +# Debug mode +if [ "$APP_DEBUG" = "true" ]; then + echo "Debug mode is enabled" + echo "Installing development dependencies..." + composer install --dev --no-scripts + echo "Clearing optimized classes..." + php artisan optimize:clear +fi diff --git a/docker/production/etc/nginx/site-opts.d/http.conf b/docker/production/etc/nginx/site-opts.d/http.conf index e740918a5b..a5bbd78a3a 100644 --- a/docker/production/etc/nginx/site-opts.d/http.conf +++ b/docker/production/etc/nginx/site-opts.d/http.conf @@ -37,7 +37,7 @@ location ~ \.php$ { fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; - fastcgi_buffers 8 8k; - fastcgi_buffer_size 8k; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; fastcgi_read_timeout 99; } diff --git a/docker/production/etc/php/conf.d/zzz-custom-php.ini b/docker/production/etc/php/conf.d/zzz-custom-php.ini index ee18b77e95..4002b8f166 100644 --- a/docker/production/etc/php/conf.d/zzz-custom-php.ini +++ b/docker/production/etc/php/conf.d/zzz-custom-php.ini @@ -7,4 +7,4 @@ ignore_repeated_source = On upload_max_filesize = 256M post_max_size = 256M -memory_limit = ${PHP_MEMORY_LIMIT:-256M} +memory_limit = ${PHP_MEMORY_LIMIT:-512M} diff --git a/docker/testing-host/Dockerfile b/docker/testing-host/Dockerfile index d98fcc8215..4b424279dd 100644 --- a/docker/testing-host/Dockerfile +++ b/docker/testing-host/Dockerfile @@ -1,10 +1,10 @@ # Versions # https://download.docker.com/linux/static/stable/ -ARG DOCKER_VERSION=27.3.1 +ARG DOCKER_VERSION=27.4.1 # https://github.com/docker/compose/releases -ARG DOCKER_COMPOSE_VERSION=2.30.3 +ARG DOCKER_COMPOSE_VERSION=2.32.2 # https://github.com/docker/buildx/releases -ARG DOCKER_BUILDX_VERSION=0.18.0 +ARG DOCKER_BUILDX_VERSION=0.19.3 FROM debian:12-slim diff --git a/lang/ar.json b/lang/ar.json index 4b9afbe996..b473318d3a 100644 --- a/lang/ar.json +++ b/lang/ar.json @@ -5,6 +5,7 @@ "auth.login.github": "تسجيل الدخول باستخدام GitHub", "auth.login.gitlab": "تسجيل الدخول باستخدام Gitlab", "auth.login.google": "تسجيل الدخول باستخدام Google", + "auth.login.infomaniak": "تسجيل الدخول باستخدام Infomaniak", "auth.already_registered": "هل سبق لك التسجيل؟", "auth.confirm_password": "تأكيد كلمة المرور", "auth.forgot_password": "نسيت كلمة المرور", diff --git a/lang/cs.json b/lang/cs.json index 48b47b06a3..270fd272ba 100644 --- a/lang/cs.json +++ b/lang/cs.json @@ -5,6 +5,7 @@ "auth.login.github": "Přihlásit se pomocí GitHubu", "auth.login.gitlab": "Přihlásit se pomocí Gitlabu", "auth.login.google": "Přihlásit se pomocí Google", + "auth.login.infomaniak": "Přihlásit se pomocí Infomaniak", "auth.already_registered": "Již jste registrováni?", "auth.confirm_password": "Potvrďte heslo", "auth.forgot_password": "Zapomněli jste heslo", diff --git a/lang/de.json b/lang/de.json index 29fec629f2..c5644e3a7a 100644 --- a/lang/de.json +++ b/lang/de.json @@ -5,6 +5,7 @@ "auth.login.github": "Mit GitHub anmelden", "auth.login.gitlab": "Mit GitLab anmelden", "auth.login.google": "Mit Google anmelden", + "auth.login.infomaniak": "Mit Infomaniak anmelden", "auth.already_registered": "Bereits registriert?", "auth.confirm_password": "Passwort bestätigen", "auth.forgot_password": "Passwort vergessen", diff --git a/lang/en.json b/lang/en.json index 4e0749ece8..cdca686013 100644 --- a/lang/en.json +++ b/lang/en.json @@ -6,6 +6,7 @@ "auth.login.github": "Login with GitHub", "auth.login.gitlab": "Login with Gitlab", "auth.login.google": "Login with Google", + "auth.login.infomaniak": "Login with Infomaniak", "auth.already_registered": "Already registered?", "auth.confirm_password": "Confirm password", "auth.forgot_password": "Forgot password", diff --git a/lang/es.json b/lang/es.json index 0d8c0c9406..aceacd4625 100644 --- a/lang/es.json +++ b/lang/es.json @@ -5,6 +5,7 @@ "auth.login.github": "Acceder con GitHub", "auth.login.gitlab": "Acceder con Gitlab", "auth.login.google": "Acceder con Google", + "auth.login.infomaniak": "Acceder con Infomaniak", "auth.already_registered": "¿Ya estás registrado?", "auth.confirm_password": "Confirmar contraseña", "auth.forgot_password": "¿Olvidaste tu contraseña?", @@ -27,4 +28,4 @@ "input.recovery_code": "Código de recuperación", "button.save": "Guardar", "repository.url": "Examples
Para repositorios públicos, usar https://....
Para repositorios privados, usar git@....

https://github.com/coollabsio/coolify-examples main la rama 'main' será seleccionada.
https://github.com/coollabsio/coolify-examples/tree/nodejs-fastify nodejs-fastify la rama 'nodejs-fastify' será seleccionada.
https://gitea.com/sedlav/expressjs.git main la rama 'main' será seleccionada.
https://gitlab.com/andrasbacsai/nodejs-example.git main la rama 'main' será seleccionada." -} \ No newline at end of file +} diff --git a/lang/fa.json b/lang/fa.json index d0ecc4a3b8..7a714e6266 100644 --- a/lang/fa.json +++ b/lang/fa.json @@ -5,6 +5,7 @@ "auth.login.github": "ورود با گیت هاب", "auth.login.gitlab": "ورود با گیت لب", "auth.login.google": "ورود با گوگل", + "auth.login.infomaniak": "ورود با Infomaniak", "auth.already_registered": "قبلاً ثبت نام کرده‌اید؟", "auth.confirm_password": "تایید رمز عبور", "auth.forgot_password": "فراموشی رمز عبور", diff --git a/lang/fr.json b/lang/fr.json index dbd5a1bf7a..a94d633d39 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -5,6 +5,7 @@ "auth.login.github": "Connexion avec GitHub", "auth.login.gitlab": "Connexion avec Gitlab", "auth.login.google": "Connexion avec Google", + "auth.login.infomaniak": "Connexion avec Infomaniak", "auth.already_registered": "Déjà enregistré ?", "auth.confirm_password": "Confirmer le mot de passe", "auth.forgot_password": "Mot de passe oublié", diff --git a/lang/it.json b/lang/it.json index 6e4feb9ccb..30b9f39026 100644 --- a/lang/it.json +++ b/lang/it.json @@ -5,6 +5,7 @@ "auth.login.github": "Accedi con GitHub", "auth.login.gitlab": "Accedi con Gitlab", "auth.login.google": "Accedi con Google", + "auth.login.infomaniak": "Accedi con Infomaniak", "auth.already_registered": "Già registrato?", "auth.confirm_password": "Conferma password", "auth.forgot_password": "Password dimenticata", diff --git a/lang/ja.json b/lang/ja.json index 4652a3b178..4d45899001 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -5,6 +5,7 @@ "auth.login.github": "GitHubでログイン", "auth.login.gitlab": "Gitlabでログイン", "auth.login.google": "Googleでログイン", + "auth.login.infomaniak": "Infomaniakでログイン", "auth.already_registered": "すでに登録済みですか?", "auth.confirm_password": "パスワードを確認", "auth.forgot_password": "パスワードを忘れた", diff --git a/lang/pt.json b/lang/pt.json index b5dd5c434e..c5f393e65c 100644 --- a/lang/pt.json +++ b/lang/pt.json @@ -5,6 +5,7 @@ "auth.login.github": "Entrar com GitHub", "auth.login.gitlab": "Entrar com Gitlab", "auth.login.google": "Entrar com Google", + "auth.login.infomaniak": "Entrar com Infomaniak", "auth.already_registered": "Já tem uma conta?", "auth.confirm_password": "Confirmar senha", "auth.forgot_password": "Esqueceu a senha?", diff --git a/lang/ro.json b/lang/ro.json index db1aa85db5..4c7968cfa2 100644 --- a/lang/ro.json +++ b/lang/ro.json @@ -5,6 +5,7 @@ "auth.login.github": "Autentificare prin GitHub", "auth.login.gitlab": "Autentificare prin Gitlab", "auth.login.google": "Autentificare prin Google", + "auth.login.infomaniak": "Autentificare prin Infomaniak", "auth.already_registered": "Sunteți deja înregistrat?", "auth.confirm_password": "Confirmați parola", "auth.forgot_password": "Ați uitat parola", diff --git a/lang/tr.json b/lang/tr.json index 255b0d15be..3cbcee4098 100644 --- a/lang/tr.json +++ b/lang/tr.json @@ -5,6 +5,7 @@ "auth.login.github": "GitHub ile Giriş Yap", "auth.login.gitlab": "GitLab ile Giriş Yap", "auth.login.google": "Google ile Giriş Yap", + "auth.login.infomaniak": "Infomaniak ile Giriş Yap", "auth.already_registered": "Zaten kayıtlı mısınız?", "auth.confirm_password": "Şifreyi Onayla", "auth.forgot_password": "Şifremi Unuttum", diff --git a/lang/vi.json b/lang/vi.json index 548dbe8b72..bb43fd34d4 100644 --- a/lang/vi.json +++ b/lang/vi.json @@ -5,6 +5,7 @@ "auth.login.github": "Đăng Nhập Bằng GitHub", "auth.login.gitlab": "Đăng Nhập Bằng Gitlab", "auth.login.google": "Đăng Nhập Bằng Google", + "auth.login.infomaniak": "Đăng Nhập Bằng Infomaniak", "auth.already_registered": "Đã đăng ký?", "auth.confirm_password": "Nhập lại mật khẩu", "auth.forgot_password": "Quên mật khẩu", diff --git a/lang/zh-cn.json b/lang/zh-cn.json index 70c457fa8b..944887a5fc 100644 --- a/lang/zh-cn.json +++ b/lang/zh-cn.json @@ -5,6 +5,7 @@ "auth.login.github": "使用 GitHub 登录", "auth.login.gitlab": "使用 Gitlab 登录", "auth.login.google": "使用 Google 登录", + "auth.login.infomaniak": "使用 Infomaniak 登录", "auth.already_registered": "已经注册?", "auth.confirm_password": "确认密码", "auth.forgot_password": "忘记密码", diff --git a/lang/zh-tw.json b/lang/zh-tw.json index 63956f7a13..c42ebb33e3 100644 --- a/lang/zh-tw.json +++ b/lang/zh-tw.json @@ -5,6 +5,7 @@ "auth.login.github": "使用 GitHub 登入", "auth.login.gitlab": "使用 Gitlab 登入", "auth.login.google": "使用 Google 登入", + "auth.login.infomaniak": "使用 Infomaniak 登入", "auth.already_registered": "已經註冊?", "auth.confirm_password": "確認密碼", "auth.forgot_password": "忘記密碼", diff --git a/openapi.json b/openapi.json index 5d35331ec6..819f229cce 100644 --- a/openapi.json +++ b/openapi.json @@ -65,6 +65,7 @@ "project_uuid", "server_uuid", "environment_name", + "environment_uuid", "git_repository", "git_branch", "build_pack", @@ -81,7 +82,11 @@ }, "environment_name": { "type": "string", - "description": "The environment name." + "description": "The environment name. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "The environment UUID. You need to provide at least one of environment_name or environment_uuid." }, "git_repository": { "type": "string", @@ -342,8 +347,20 @@ } }, "responses": { - "200": { - "description": "Application created successfully." + "201": { + "description": "Application created successfully.", + "content": { + "application\/json": { + "schema": { + "properties": { + "uuid": { + "type": "string" + } + }, + "type": "object" + } + } + } }, "401": { "$ref": "#\/components\/responses\/401" @@ -377,6 +394,7 @@ "project_uuid", "server_uuid", "environment_name", + "environment_uuid", "github_app_uuid", "git_repository", "git_branch", @@ -394,7 +412,11 @@ }, "environment_name": { "type": "string", - "description": "The environment name." + "description": "The environment name. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "The environment UUID. You need to provide at least one of environment_name or environment_uuid." }, "github_app_uuid": { "type": "string", @@ -659,8 +681,20 @@ } }, "responses": { - "200": { - "description": "Application created successfully." + "201": { + "description": "Application created successfully.", + "content": { + "application\/json": { + "schema": { + "properties": { + "uuid": { + "type": "string" + } + }, + "type": "object" + } + } + } }, "401": { "$ref": "#\/components\/responses\/401" @@ -694,6 +728,7 @@ "project_uuid", "server_uuid", "environment_name", + "environment_uuid", "private_key_uuid", "git_repository", "git_branch", @@ -711,7 +746,11 @@ }, "environment_name": { "type": "string", - "description": "The environment name." + "description": "The environment name. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "The environment UUID. You need to provide at least one of environment_name or environment_uuid." }, "private_key_uuid": { "type": "string", @@ -976,8 +1015,20 @@ } }, "responses": { - "200": { - "description": "Application created successfully." + "201": { + "description": "Application created successfully.", + "content": { + "application\/json": { + "schema": { + "properties": { + "uuid": { + "type": "string" + } + }, + "type": "object" + } + } + } }, "401": { "$ref": "#\/components\/responses\/401" @@ -1011,6 +1062,7 @@ "project_uuid", "server_uuid", "environment_name", + "environment_uuid", "dockerfile" ], "properties": { @@ -1024,7 +1076,11 @@ }, "environment_name": { "type": "string", - "description": "The environment name." + "description": "The environment name. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "The environment UUID. You need to provide at least one of environment_name or environment_uuid." }, "dockerfile": { "type": "string", @@ -1222,8 +1278,20 @@ } }, "responses": { - "200": { - "description": "Application created successfully." + "201": { + "description": "Application created successfully.", + "content": { + "application\/json": { + "schema": { + "properties": { + "uuid": { + "type": "string" + } + }, + "type": "object" + } + } + } }, "401": { "$ref": "#\/components\/responses\/401" @@ -1257,6 +1325,7 @@ "project_uuid", "server_uuid", "environment_name", + "environment_uuid", "docker_registry_image_name", "ports_exposes" ], @@ -1271,7 +1340,11 @@ }, "environment_name": { "type": "string", - "description": "The environment name." + "description": "The environment name. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "The environment UUID. You need to provide at least one of environment_name or environment_uuid." }, "docker_registry_image_name": { "type": "string", @@ -1451,8 +1524,20 @@ } }, "responses": { - "200": { - "description": "Application created successfully." + "201": { + "description": "Application created successfully.", + "content": { + "application\/json": { + "schema": { + "properties": { + "uuid": { + "type": "string" + } + }, + "type": "object" + } + } + } }, "401": { "$ref": "#\/components\/responses\/401" @@ -1486,6 +1571,7 @@ "project_uuid", "server_uuid", "environment_name", + "environment_uuid", "docker_compose_raw" ], "properties": { @@ -1499,7 +1585,11 @@ }, "environment_name": { "type": "string", - "description": "The environment name." + "description": "The environment name. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "The environment UUID. You need to provide at least one of environment_name or environment_uuid." }, "docker_compose_raw": { "type": "string", @@ -1533,8 +1623,20 @@ } }, "responses": { - "200": { - "description": "Application created successfully." + "201": { + "description": "Application created successfully.", + "content": { + "application\/json": { + "schema": { + "properties": { + "uuid": { + "type": "string" + } + }, + "type": "object" + } + } + } }, "401": { "$ref": "#\/components\/responses\/401" @@ -3079,7 +3181,8 @@ "required": [ "server_uuid", "project_uuid", - "environment_name" + "environment_name", + "environment_uuid" ], "properties": { "server_uuid": { @@ -3092,7 +3195,11 @@ }, "environment_name": { "type": "string", - "description": "Name of the environment" + "description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid." }, "postgres_user": { "type": "string", @@ -3215,7 +3322,8 @@ "required": [ "server_uuid", "project_uuid", - "environment_name" + "environment_name", + "environment_uuid" ], "properties": { "server_uuid": { @@ -3228,7 +3336,11 @@ }, "environment_name": { "type": "string", - "description": "Name of the environment" + "description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid." }, "destination_uuid": { "type": "string", @@ -3335,7 +3447,8 @@ "required": [ "server_uuid", "project_uuid", - "environment_name" + "environment_name", + "environment_uuid" ], "properties": { "server_uuid": { @@ -3348,7 +3461,11 @@ }, "environment_name": { "type": "string", - "description": "Name of the environment" + "description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid." }, "destination_uuid": { "type": "string", @@ -3451,7 +3568,8 @@ "required": [ "server_uuid", "project_uuid", - "environment_name" + "environment_name", + "environment_uuid" ], "properties": { "server_uuid": { @@ -3464,7 +3582,11 @@ }, "environment_name": { "type": "string", - "description": "Name of the environment" + "description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid." }, "destination_uuid": { "type": "string", @@ -3571,7 +3693,8 @@ "required": [ "server_uuid", "project_uuid", - "environment_name" + "environment_name", + "environment_uuid" ], "properties": { "server_uuid": { @@ -3584,7 +3707,11 @@ }, "environment_name": { "type": "string", - "description": "Name of the environment" + "description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid." }, "destination_uuid": { "type": "string", @@ -3691,7 +3818,8 @@ "required": [ "server_uuid", "project_uuid", - "environment_name" + "environment_name", + "environment_uuid" ], "properties": { "server_uuid": { @@ -3704,7 +3832,11 @@ }, "environment_name": { "type": "string", - "description": "Name of the environment" + "description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid." }, "destination_uuid": { "type": "string", @@ -3823,7 +3955,8 @@ "required": [ "server_uuid", "project_uuid", - "environment_name" + "environment_name", + "environment_uuid" ], "properties": { "server_uuid": { @@ -3836,7 +3969,11 @@ }, "environment_name": { "type": "string", - "description": "Name of the environment" + "description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid." }, "destination_uuid": { "type": "string", @@ -3955,7 +4092,8 @@ "required": [ "server_uuid", "project_uuid", - "environment_name" + "environment_name", + "environment_uuid" ], "properties": { "server_uuid": { @@ -3968,7 +4106,11 @@ }, "environment_name": { "type": "string", - "description": "Name of the environment" + "description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid." }, "destination_uuid": { "type": "string", @@ -4808,14 +4950,14 @@ ] } }, - "\/projects\/{uuid}\/{environment_name}": { + "\/projects\/{uuid}\/{environment_name_or_uuid}": { "get": { "tags": [ "Projects" ], "summary": "Environment", - "description": "Get environment by name.", - "operationId": "get-environment-by-name", + "description": "Get environment by name or UUID.", + "operationId": "get-environment-by-name-or-uuid", "parameters": [ { "name": "uuid", @@ -4827,9 +4969,9 @@ } }, { - "name": "environment_name", + "name": "environment_name_or_uuid", "in": "path", - "description": "Environment name", + "description": "Environment name or UUID", "required": true, "schema": { "type": "string" @@ -5724,6 +5866,7 @@ "server_uuid", "project_uuid", "environment_name", + "environment_uuid", "type" ], "properties": { @@ -5835,7 +5978,11 @@ }, "environment_name": { "type": "string", - "description": "Environment name." + "description": "Environment name. You need to provide at least one of environment_name or environment_uuid." + }, + "environment_uuid": { + "type": "string", + "description": "Environment UUID. You need to provide at least one of environment_name or environment_uuid." }, "server_uuid": { "type": "string", @@ -7282,13 +7429,10 @@ "uuid": { "type": "string" }, - "application_id": { - "type": "integer" - }, - "service_id": { - "type": "integer" + "resourceable_type": { + "type": "string" }, - "database_id": { + "resourceable_id": { "type": "integer" }, "is_build_time": { @@ -7684,174 +7828,14 @@ "type": "string", "description": "The date and time the team was last updated." }, - "smtp_enabled": { - "type": "boolean", - "description": "Whether SMTP is enabled or not." - }, - "smtp_from_address": { - "type": "string", - "description": "The email address to send emails from." - }, - "smtp_from_name": { - "type": "string", - "description": "The name to send emails from." - }, - "smtp_recipients": { - "type": "string", - "description": "The email addresses to send emails to." - }, - "smtp_host": { - "type": "string", - "description": "The SMTP host." - }, - "smtp_port": { - "type": "string", - "description": "The SMTP port." - }, - "smtp_encryption": { - "type": "string", - "description": "The SMTP encryption." - }, - "smtp_username": { - "type": "string", - "description": "The SMTP username." - }, - "smtp_password": { - "type": "string", - "description": "The SMTP password." - }, - "smtp_timeout": { - "type": "string", - "description": "The SMTP timeout." - }, - "smtp_notifications_test": { - "type": "boolean", - "description": "Whether to send test notifications via SMTP." - }, - "smtp_notifications_deployments": { - "type": "boolean", - "description": "Whether to send deployment notifications via SMTP." - }, - "smtp_notifications_status_changes": { - "type": "boolean", - "description": "Whether to send status change notifications via SMTP." - }, - "smtp_notifications_scheduled_tasks": { - "type": "boolean", - "description": "Whether to send scheduled task notifications via SMTP." - }, - "smtp_notifications_database_backups": { - "type": "boolean", - "description": "Whether to send database backup notifications via SMTP." - }, - "smtp_notifications_server_disk_usage": { - "type": "boolean", - "description": "Whether to send server disk usage notifications via SMTP." - }, - "discord_enabled": { - "type": "boolean", - "description": "Whether Discord is enabled or not." - }, - "discord_webhook_url": { - "type": "string", - "description": "The Discord webhook URL." - }, - "discord_notifications_test": { - "type": "boolean", - "description": "Whether to send test notifications via Discord." - }, - "discord_notifications_deployments": { - "type": "boolean", - "description": "Whether to send deployment notifications via Discord." - }, - "discord_notifications_status_changes": { - "type": "boolean", - "description": "Whether to send status change notifications via Discord." - }, - "discord_notifications_database_backups": { - "type": "boolean", - "description": "Whether to send database backup notifications via Discord." - }, - "discord_notifications_scheduled_tasks": { - "type": "boolean", - "description": "Whether to send scheduled task notifications via Discord." - }, - "discord_notifications_server_disk_usage": { - "type": "boolean", - "description": "Whether to send server disk usage notifications via Discord." - }, "show_boarding": { "type": "boolean", "description": "Whether to show the boarding screen or not." }, - "resend_enabled": { - "type": "boolean", - "description": "Whether to enable resending or not." - }, - "resend_api_key": { - "type": "string", - "description": "The resending API key." - }, - "use_instance_email_settings": { - "type": "boolean", - "description": "Whether to use instance email settings or not." - }, - "telegram_enabled": { - "type": "boolean", - "description": "Whether Telegram is enabled or not." - }, - "telegram_token": { - "type": "string", - "description": "The Telegram token." - }, - "telegram_chat_id": { - "type": "string", - "description": "The Telegram chat ID." - }, - "telegram_notifications_test": { - "type": "boolean", - "description": "Whether to send test notifications via Telegram." - }, - "telegram_notifications_deployments": { - "type": "boolean", - "description": "Whether to send deployment notifications via Telegram." - }, - "telegram_notifications_status_changes": { - "type": "boolean", - "description": "Whether to send status change notifications via Telegram." - }, - "telegram_notifications_database_backups": { - "type": "boolean", - "description": "Whether to send database backup notifications via Telegram." - }, - "telegram_notifications_test_message_thread_id": { - "type": "string", - "description": "The Telegram test message thread ID." - }, - "telegram_notifications_deployments_message_thread_id": { - "type": "string", - "description": "The Telegram deployment message thread ID." - }, - "telegram_notifications_status_changes_message_thread_id": { - "type": "string", - "description": "The Telegram status change message thread ID." - }, - "telegram_notifications_database_backups_message_thread_id": { - "type": "string", - "description": "The Telegram database backup message thread ID." - }, "custom_server_limit": { "type": "string", "description": "The custom server limit." }, - "telegram_notifications_scheduled_tasks": { - "type": "boolean", - "description": "Whether to send scheduled task notifications via Telegram." - }, - "telegram_notifications_scheduled_tasks_thread_id": { - "type": "string", - "description": "The Telegram scheduled task message thread ID." - }, "members": { "description": "The members of the team.", "type": "array", diff --git a/openapi.yaml b/openapi.yaml index 20bf348730..2d18031133 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -47,6 +47,7 @@ paths: - project_uuid - server_uuid - environment_name + - environment_uuid - git_repository - git_branch - build_pack @@ -60,7 +61,10 @@ paths: description: 'The server UUID.' environment_name: type: string - description: 'The environment name.' + description: 'The environment name. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.' git_repository: type: string description: 'The git repository URL.' @@ -246,8 +250,14 @@ paths: description: 'Use build server.' type: object responses: - '200': + '201': description: 'Application created successfully.' + content: + application/json: + schema: + properties: + uuid: { type: string } + type: object '401': $ref: '#/components/responses/401' '400': @@ -272,6 +282,7 @@ paths: - project_uuid - server_uuid - environment_name + - environment_uuid - github_app_uuid - git_repository - git_branch @@ -286,7 +297,10 @@ paths: description: 'The server UUID.' environment_name: type: string - description: 'The environment name.' + description: 'The environment name. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.' github_app_uuid: type: string description: 'The Github App UUID.' @@ -475,8 +489,14 @@ paths: description: 'Use build server.' type: object responses: - '200': + '201': description: 'Application created successfully.' + content: + application/json: + schema: + properties: + uuid: { type: string } + type: object '401': $ref: '#/components/responses/401' '400': @@ -501,6 +521,7 @@ paths: - project_uuid - server_uuid - environment_name + - environment_uuid - private_key_uuid - git_repository - git_branch @@ -515,7 +536,10 @@ paths: description: 'The server UUID.' environment_name: type: string - description: 'The environment name.' + description: 'The environment name. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.' private_key_uuid: type: string description: 'The private key UUID.' @@ -704,8 +728,14 @@ paths: description: 'Use build server.' type: object responses: - '200': + '201': description: 'Application created successfully.' + content: + application/json: + schema: + properties: + uuid: { type: string } + type: object '401': $ref: '#/components/responses/401' '400': @@ -730,6 +760,7 @@ paths: - project_uuid - server_uuid - environment_name + - environment_uuid - dockerfile properties: project_uuid: @@ -740,7 +771,10 @@ paths: description: 'The server UUID.' environment_name: type: string - description: 'The environment name.' + description: 'The environment name. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.' dockerfile: type: string description: 'The Dockerfile content.' @@ -880,8 +914,14 @@ paths: description: 'Use build server.' type: object responses: - '200': + '201': description: 'Application created successfully.' + content: + application/json: + schema: + properties: + uuid: { type: string } + type: object '401': $ref: '#/components/responses/401' '400': @@ -906,6 +946,7 @@ paths: - project_uuid - server_uuid - environment_name + - environment_uuid - docker_registry_image_name - ports_exposes properties: @@ -917,7 +958,10 @@ paths: description: 'The server UUID.' environment_name: type: string - description: 'The environment name.' + description: 'The environment name. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.' docker_registry_image_name: type: string description: 'The docker registry image name.' @@ -1047,8 +1091,14 @@ paths: description: 'Use build server.' type: object responses: - '200': + '201': description: 'Application created successfully.' + content: + application/json: + schema: + properties: + uuid: { type: string } + type: object '401': $ref: '#/components/responses/401' '400': @@ -1073,6 +1123,7 @@ paths: - project_uuid - server_uuid - environment_name + - environment_uuid - docker_compose_raw properties: project_uuid: @@ -1083,7 +1134,10 @@ paths: description: 'The server UUID.' environment_name: type: string - description: 'The environment name.' + description: 'The environment name. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'The environment UUID. You need to provide at least one of environment_name or environment_uuid.' docker_compose_raw: type: string description: 'The Docker Compose raw content.' @@ -1105,8 +1159,14 @@ paths: description: 'Use build server.' type: object responses: - '200': + '201': description: 'Application created successfully.' + content: + application/json: + schema: + properties: + uuid: { type: string } + type: object '401': $ref: '#/components/responses/401' '400': @@ -2137,6 +2197,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid properties: server_uuid: type: string @@ -2146,7 +2207,10 @@ paths: description: 'UUID of the project' environment_name: type: string - description: 'Name of the environment' + description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.' postgres_user: type: string description: 'PostgreSQL user' @@ -2235,6 +2299,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid properties: server_uuid: type: string @@ -2244,7 +2309,10 @@ paths: description: 'UUID of the project' environment_name: type: string - description: 'Name of the environment' + description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.' destination_uuid: type: string description: 'UUID of the destination if the server has multiple destinations' @@ -2321,6 +2389,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid properties: server_uuid: type: string @@ -2330,7 +2399,10 @@ paths: description: 'UUID of the project' environment_name: type: string - description: 'Name of the environment' + description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.' destination_uuid: type: string description: 'UUID of the destination if the server has multiple destinations' @@ -2404,6 +2476,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid properties: server_uuid: type: string @@ -2413,7 +2486,10 @@ paths: description: 'UUID of the project' environment_name: type: string - description: 'Name of the environment' + description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.' destination_uuid: type: string description: 'UUID of the destination if the server has multiple destinations' @@ -2490,6 +2566,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid properties: server_uuid: type: string @@ -2499,7 +2576,10 @@ paths: description: 'UUID of the project' environment_name: type: string - description: 'Name of the environment' + description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.' destination_uuid: type: string description: 'UUID of the destination if the server has multiple destinations' @@ -2576,6 +2656,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid properties: server_uuid: type: string @@ -2585,7 +2666,10 @@ paths: description: 'UUID of the project' environment_name: type: string - description: 'Name of the environment' + description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.' destination_uuid: type: string description: 'UUID of the destination if the server has multiple destinations' @@ -2671,6 +2755,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid properties: server_uuid: type: string @@ -2680,7 +2765,10 @@ paths: description: 'UUID of the project' environment_name: type: string - description: 'Name of the environment' + description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.' destination_uuid: type: string description: 'UUID of the destination if the server has multiple destinations' @@ -2766,6 +2854,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid properties: server_uuid: type: string @@ -2775,7 +2864,10 @@ paths: description: 'UUID of the project' environment_name: type: string - description: 'Name of the environment' + description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.' destination_uuid: type: string description: 'UUID of the destination if the server has multiple destinations' @@ -3293,13 +3385,13 @@ paths: security: - bearerAuth: [] - '/projects/{uuid}/{environment_name}': + '/projects/{uuid}/{environment_name_or_uuid}': get: tags: - Projects summary: Environment - description: 'Get environment by name.' - operationId: get-environment-by-name + description: 'Get environment by name or UUID.' + operationId: get-environment-by-name-or-uuid parameters: - name: uuid @@ -3309,9 +3401,9 @@ paths: schema: type: string - - name: environment_name + name: environment_name_or_uuid in: path - description: 'Environment name' + description: 'Environment name or UUID' required: true schema: type: string @@ -3872,6 +3964,7 @@ paths: - server_uuid - project_uuid - environment_name + - environment_uuid - type properties: type: @@ -3891,7 +3984,10 @@ paths: description: 'Project UUID.' environment_name: type: string - description: 'Environment name.' + description: 'Environment name. You need to provide at least one of environment_name or environment_uuid.' + environment_uuid: + type: string + description: 'Environment UUID. You need to provide at least one of environment_name or environment_uuid.' server_uuid: type: string description: 'Server UUID.' @@ -4856,11 +4952,9 @@ components: type: integer uuid: type: string - application_id: - type: integer - service_id: - type: integer - database_id: + resourceable_type: + type: string + resourceable_id: type: integer is_build_time: type: boolean @@ -5139,132 +5233,12 @@ components: updated_at: type: string description: 'The date and time the team was last updated.' - smtp_enabled: - type: boolean - description: 'Whether SMTP is enabled or not.' - smtp_from_address: - type: string - description: 'The email address to send emails from.' - smtp_from_name: - type: string - description: 'The name to send emails from.' - smtp_recipients: - type: string - description: 'The email addresses to send emails to.' - smtp_host: - type: string - description: 'The SMTP host.' - smtp_port: - type: string - description: 'The SMTP port.' - smtp_encryption: - type: string - description: 'The SMTP encryption.' - smtp_username: - type: string - description: 'The SMTP username.' - smtp_password: - type: string - description: 'The SMTP password.' - smtp_timeout: - type: string - description: 'The SMTP timeout.' - smtp_notifications_test: - type: boolean - description: 'Whether to send test notifications via SMTP.' - smtp_notifications_deployments: - type: boolean - description: 'Whether to send deployment notifications via SMTP.' - smtp_notifications_status_changes: - type: boolean - description: 'Whether to send status change notifications via SMTP.' - smtp_notifications_scheduled_tasks: - type: boolean - description: 'Whether to send scheduled task notifications via SMTP.' - smtp_notifications_database_backups: - type: boolean - description: 'Whether to send database backup notifications via SMTP.' - smtp_notifications_server_disk_usage: - type: boolean - description: 'Whether to send server disk usage notifications via SMTP.' - discord_enabled: - type: boolean - description: 'Whether Discord is enabled or not.' - discord_webhook_url: - type: string - description: 'The Discord webhook URL.' - discord_notifications_test: - type: boolean - description: 'Whether to send test notifications via Discord.' - discord_notifications_deployments: - type: boolean - description: 'Whether to send deployment notifications via Discord.' - discord_notifications_status_changes: - type: boolean - description: 'Whether to send status change notifications via Discord.' - discord_notifications_database_backups: - type: boolean - description: 'Whether to send database backup notifications via Discord.' - discord_notifications_scheduled_tasks: - type: boolean - description: 'Whether to send scheduled task notifications via Discord.' - discord_notifications_server_disk_usage: - type: boolean - description: 'Whether to send server disk usage notifications via Discord.' show_boarding: type: boolean description: 'Whether to show the boarding screen or not.' - resend_enabled: - type: boolean - description: 'Whether to enable resending or not.' - resend_api_key: - type: string - description: 'The resending API key.' - use_instance_email_settings: - type: boolean - description: 'Whether to use instance email settings or not.' - telegram_enabled: - type: boolean - description: 'Whether Telegram is enabled or not.' - telegram_token: - type: string - description: 'The Telegram token.' - telegram_chat_id: - type: string - description: 'The Telegram chat ID.' - telegram_notifications_test: - type: boolean - description: 'Whether to send test notifications via Telegram.' - telegram_notifications_deployments: - type: boolean - description: 'Whether to send deployment notifications via Telegram.' - telegram_notifications_status_changes: - type: boolean - description: 'Whether to send status change notifications via Telegram.' - telegram_notifications_database_backups: - type: boolean - description: 'Whether to send database backup notifications via Telegram.' - telegram_notifications_test_message_thread_id: - type: string - description: 'The Telegram test message thread ID.' - telegram_notifications_deployments_message_thread_id: - type: string - description: 'The Telegram deployment message thread ID.' - telegram_notifications_status_changes_message_thread_id: - type: string - description: 'The Telegram status change message thread ID.' - telegram_notifications_database_backups_message_thread_id: - type: string - description: 'The Telegram database backup message thread ID.' custom_server_limit: type: string description: 'The custom server limit.' - telegram_notifications_scheduled_tasks: - type: boolean - description: 'Whether to send scheduled task notifications via Telegram.' - telegram_notifications_scheduled_tasks_thread_id: - type: string - description: 'The Telegram scheduled task message thread ID.' members: description: 'The members of the team.' type: array diff --git a/other/nightly/.env.production b/other/nightly/.env.production index 099ec7c250..d3a1b17c5f 100644 --- a/other/nightly/.env.production +++ b/other/nightly/.env.production @@ -1,16 +1,12 @@ -# Coolify Configuration APP_ID= APP_NAME=Coolify APP_KEY= -# PostgreSQL Database Configuration DB_USERNAME=coolify DB_PASSWORD= -# Redis Configuration REDIS_PASSWORD= -# Pusher Configuration PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= diff --git a/other/nightly/docker-compose.prod.yml b/other/nightly/docker-compose.prod.yml index 459b93ac60..e651b4add5 100644 --- a/other/nightly/docker-compose.prod.yml +++ b/other/nightly/docker-compose.prod.yml @@ -13,46 +13,14 @@ services: - /data/coolify/backups:/var/www/html/storage/app/backups - /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance environment: - - APP_ENV=production - - APP_NAME - - APP_ID - - APP_KEY - - APP_URL - - APP_DEBUG - - DB_DATABASE - - DB_USERNAME - - DB_PASSWORD - - DB_HOST - - DB_PORT - - DB_CONNECTION - - QUEUE_CONNECTION - - REDIS_HOST - - REDIS_PASSWORD - - HORIZON_BALANCE - - HORIZON_MIN_PROCESSES - - HORIZON_MAX_PROCESSES - - HORIZON_BALANCE_MAX_SHIFT - - HORIZON_BALANCE_COOLDOWN - - SSL_MODE=off - - PHP_MEMORY_LIMIT - - PHP_PM_CONTROL=dynamic - - PHP_PM_START_SERVERS=1 - - PHP_PM_MIN_SPARE_SERVERS=1 - - PHP_PM_MAX_SPARE_SERVERS=10 - - PUSHER_HOST - - PUSHER_BACKEND_HOST - - PUSHER_PORT - - PUSHER_BACKEND_PORT - - PUSHER_SCHEME - - PUSHER_APP_ID - - PUSHER_APP_KEY - - PUSHER_APP_SECRET - - TERMINAL_PROTOCOL - - TERMINAL_HOST - - TERMINAL_PORT - - AUTOUPDATE - - SSH_MUX_ENABLED - - SSH_MUX_PERSIST_TIME + - APP_ENV=${APP_ENV:-production} + - PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-128M} + - PHP_FPM_PM_CONTROL=${PHP_FPM_PM_CONTROL:-dynamic} + - PHP_FPM_PM_START_SERVERS=${PHP_FPM_PM_START_SERVERS:-1} + - PHP_FPM_PM_MIN_SPARE_SERVERS=${PHP_FPM_PM_MIN_SPARE_SERVERS:-1} + - PHP_FPM_PM_MAX_SPARE_SERVERS=${PHP_FPM_PM_MAX_SPARE_SERVERS:-10} + env_file: + - /data/coolify/source/.env ports: - "${APP_PORT:-8000}:8080" expose: diff --git a/package-lock.json b/package-lock.json index e88e191b28..56cfa15865 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,12 +6,11 @@ "": { "name": "coolify", "dependencies": { - "@tailwindcss/forms": "0.5.9", - "@tailwindcss/typography": "0.5.15", + "@tailwindcss/forms": "0.5.10", + "@tailwindcss/typography": "0.5.16", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", - "alpinejs": "3.14.7", - "ioredis": "5.4.1" + "ioredis": "5.4.2" }, "devDependencies": { "@vitejs/plugin-vue": "5.2.1", @@ -22,8 +21,8 @@ "postcss": "8.4.49", "pusher-js": "8.4.0-rc2", "tailwind-scrollbar": "^3.1.0", - "tailwindcss": "3.4.16", - "vite": "6.0.3", + "tailwindcss": "3.4.17", + "vite": "6.0.7", "vue": "3.5.13" } }, @@ -89,9 +88,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", - "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", "cpu": [ "ppc64" ], @@ -106,9 +105,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", - "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", "cpu": [ "arm" ], @@ -123,9 +122,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", - "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", "cpu": [ "arm64" ], @@ -140,9 +139,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", - "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", "cpu": [ "x64" ], @@ -157,9 +156,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", - "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", "cpu": [ "arm64" ], @@ -174,9 +173,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", - "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", "cpu": [ "x64" ], @@ -191,9 +190,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", - "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", "cpu": [ "arm64" ], @@ -208,9 +207,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", - "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", "cpu": [ "x64" ], @@ -225,9 +224,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", - "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", "cpu": [ "arm" ], @@ -242,9 +241,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", - "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", "cpu": [ "arm64" ], @@ -259,9 +258,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", - "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", "cpu": [ "ia32" ], @@ -276,9 +275,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", - "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", "cpu": [ "loong64" ], @@ -293,9 +292,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", - "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", "cpu": [ "mips64el" ], @@ -310,9 +309,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", - "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", "cpu": [ "ppc64" ], @@ -327,9 +326,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", - "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", "cpu": [ "riscv64" ], @@ -344,9 +343,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", - "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", "cpu": [ "s390x" ], @@ -361,9 +360,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", - "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", "cpu": [ "x64" ], @@ -377,10 +376,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", - "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", "cpu": [ "x64" ], @@ -395,9 +411,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", - "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", "cpu": [ "arm64" ], @@ -412,9 +428,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", - "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", "cpu": [ "x64" ], @@ -429,9 +445,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", - "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", "cpu": [ "x64" ], @@ -446,9 +462,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", - "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", "cpu": [ "arm64" ], @@ -463,9 +479,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", - "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", "cpu": [ "ia32" ], @@ -480,9 +496,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", - "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", "cpu": [ "x64" ], @@ -861,21 +877,21 @@ ] }, "node_modules/@tailwindcss/forms": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz", - "integrity": "sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", "license": "MIT", "dependencies": { "mini-svg-data-uri": "^1.2.3" }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20" + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" } }, "node_modules/@tailwindcss/typography": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.15.tgz", - "integrity": "sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", + "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==", "license": "MIT", "dependencies": { "lodash.castarray": "^4.4.0", @@ -884,7 +900,7 @@ "postcss-selector-parser": "6.0.10" }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20" + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { @@ -1002,14 +1018,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@vue/reactivity": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", - "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", - "dependencies": { - "@vue/shared": "3.1.5" - } - }, "node_modules/@vue/runtime-core": { "version": "3.5.13", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", @@ -1089,11 +1097,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@vue/shared": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", - "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==" - }, "node_modules/@xterm/addon-fit": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz", @@ -1107,15 +1110,6 @@ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" }, - "node_modules/alpinejs": { - "version": "3.14.7", - "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.7.tgz", - "integrity": "sha512-ScnbydNBcWVnCiVupD3wWUvoMPm8244xkvDNMxVCspgmap9m4QuJ7pjc+77UtByU+1+Ejg0wzYkP4mQaOMcvng==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "~3.1.1" - } - }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -1505,9 +1499,9 @@ } }, "node_modules/esbuild": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", - "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1518,30 +1512,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.0", - "@esbuild/android-arm": "0.24.0", - "@esbuild/android-arm64": "0.24.0", - "@esbuild/android-x64": "0.24.0", - "@esbuild/darwin-arm64": "0.24.0", - "@esbuild/darwin-x64": "0.24.0", - "@esbuild/freebsd-arm64": "0.24.0", - "@esbuild/freebsd-x64": "0.24.0", - "@esbuild/linux-arm": "0.24.0", - "@esbuild/linux-arm64": "0.24.0", - "@esbuild/linux-ia32": "0.24.0", - "@esbuild/linux-loong64": "0.24.0", - "@esbuild/linux-mips64el": "0.24.0", - "@esbuild/linux-ppc64": "0.24.0", - "@esbuild/linux-riscv64": "0.24.0", - "@esbuild/linux-s390x": "0.24.0", - "@esbuild/linux-x64": "0.24.0", - "@esbuild/netbsd-x64": "0.24.0", - "@esbuild/openbsd-arm64": "0.24.0", - "@esbuild/openbsd-x64": "0.24.0", - "@esbuild/sunos-x64": "0.24.0", - "@esbuild/win32-arm64": "0.24.0", - "@esbuild/win32-ia32": "0.24.0", - "@esbuild/win32-x64": "0.24.0" + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, "node_modules/escalade": { @@ -1743,9 +1738,10 @@ "peer": true }, "node_modules/ioredis": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz", - "integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.2.tgz", + "integrity": "sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg==", + "license": "MIT", "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", @@ -2671,9 +2667,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.16", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.16.tgz", - "integrity": "sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -2788,13 +2784,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/vite": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.3.tgz", - "integrity": "sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", + "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.24.0", + "esbuild": "^0.24.2", "postcss": "^8.4.49", "rollup": "^4.23.0" }, diff --git a/package.json b/package.json index 50197706a1..7220e55a59 100644 --- a/package.json +++ b/package.json @@ -15,16 +15,15 @@ "postcss": "8.4.49", "pusher-js": "8.4.0-rc2", "tailwind-scrollbar": "^3.1.0", - "tailwindcss": "3.4.16", - "vite": "6.0.3", + "tailwindcss": "3.4.17", + "vite": "6.0.7", "vue": "3.5.13" }, "dependencies": { - "@tailwindcss/forms": "0.5.9", - "@tailwindcss/typography": "0.5.15", + "@tailwindcss/forms": "0.5.10", + "@tailwindcss/typography": "0.5.16", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", - "alpinejs": "3.14.7", - "ioredis": "5.4.1" + "ioredis": "5.4.2" } } diff --git a/public/js/dropzone.js b/public/js/dropzone.js new file mode 100644 index 0000000000..58ec1972f7 --- /dev/null +++ b/public/js/dropzone.js @@ -0,0 +1 @@ +!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var r in n)("object"==typeof exports?exports:e)[r]=n[r]}}(self,(function(){return function(){var e={3099:function(e){e.exports=function(e){if("function"!=typeof e)throw TypeError(String(e)+" is not a function");return e}},6077:function(e,t,n){var r=n(111);e.exports=function(e){if(!r(e)&&null!==e)throw TypeError("Can't set "+String(e)+" as a prototype");return e}},1223:function(e,t,n){var r=n(5112),i=n(30),o=n(3070),a=r("unscopables"),u=Array.prototype;null==u[a]&&o.f(u,a,{configurable:!0,value:i(null)}),e.exports=function(e){u[a][e]=!0}},1530:function(e,t,n){"use strict";var r=n(8710).charAt;e.exports=function(e,t,n){return t+(n?r(e,t).length:1)}},5787:function(e){e.exports=function(e,t,n){if(!(e instanceof t))throw TypeError("Incorrect "+(n?n+" ":"")+"invocation");return e}},9670:function(e,t,n){var r=n(111);e.exports=function(e){if(!r(e))throw TypeError(String(e)+" is not an object");return e}},4019:function(e){e.exports="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof DataView},260:function(e,t,n){"use strict";var r,i=n(4019),o=n(9781),a=n(7854),u=n(111),s=n(6656),l=n(648),c=n(8880),f=n(1320),p=n(3070).f,h=n(9518),d=n(7674),v=n(5112),y=n(9711),g=a.Int8Array,m=g&&g.prototype,b=a.Uint8ClampedArray,x=b&&b.prototype,w=g&&h(g),E=m&&h(m),k=Object.prototype,A=k.isPrototypeOf,S=v("toStringTag"),F=y("TYPED_ARRAY_TAG"),T=i&&!!d&&"Opera"!==l(a.opera),C=!1,L={Int8Array:1,Uint8Array:1,Uint8ClampedArray:1,Int16Array:2,Uint16Array:2,Int32Array:4,Uint32Array:4,Float32Array:4,Float64Array:8},R={BigInt64Array:8,BigUint64Array:8},I=function(e){if(!u(e))return!1;var t=l(e);return s(L,t)||s(R,t)};for(r in L)a[r]||(T=!1);if((!T||"function"!=typeof w||w===Function.prototype)&&(w=function(){throw TypeError("Incorrect invocation")},T))for(r in L)a[r]&&d(a[r],w);if((!T||!E||E===k)&&(E=w.prototype,T))for(r in L)a[r]&&d(a[r].prototype,E);if(T&&h(x)!==E&&d(x,E),o&&!s(E,S))for(r in C=!0,p(E,S,{get:function(){return u(this)?this[F]:void 0}}),L)a[r]&&c(a[r],F,r);e.exports={NATIVE_ARRAY_BUFFER_VIEWS:T,TYPED_ARRAY_TAG:C&&F,aTypedArray:function(e){if(I(e))return e;throw TypeError("Target is not a typed array")},aTypedArrayConstructor:function(e){if(d){if(A.call(w,e))return e}else for(var t in L)if(s(L,r)){var n=a[t];if(n&&(e===n||A.call(n,e)))return e}throw TypeError("Target is not a typed array constructor")},exportTypedArrayMethod:function(e,t,n){if(o){if(n)for(var r in L){var i=a[r];i&&s(i.prototype,e)&&delete i.prototype[e]}E[e]&&!n||f(E,e,n?t:T&&m[e]||t)}},exportTypedArrayStaticMethod:function(e,t,n){var r,i;if(o){if(d){if(n)for(r in L)(i=a[r])&&s(i,e)&&delete i[e];if(w[e]&&!n)return;try{return f(w,e,n?t:T&&g[e]||t)}catch(e){}}for(r in L)!(i=a[r])||i[e]&&!n||f(i,e,t)}},isView:function(e){if(!u(e))return!1;var t=l(e);return"DataView"===t||s(L,t)||s(R,t)},isTypedArray:I,TypedArray:w,TypedArrayPrototype:E}},3331:function(e,t,n){"use strict";var r=n(7854),i=n(9781),o=n(4019),a=n(8880),u=n(2248),s=n(7293),l=n(5787),c=n(9958),f=n(7466),p=n(7067),h=n(1179),d=n(9518),v=n(7674),y=n(8006).f,g=n(3070).f,m=n(1285),b=n(8003),x=n(9909),w=x.get,E=x.set,k="ArrayBuffer",A="DataView",S="Wrong index",F=r.ArrayBuffer,T=F,C=r.DataView,L=C&&C.prototype,R=Object.prototype,I=r.RangeError,U=h.pack,O=h.unpack,_=function(e){return[255&e]},M=function(e){return[255&e,e>>8&255]},z=function(e){return[255&e,e>>8&255,e>>16&255,e>>24&255]},P=function(e){return e[3]<<24|e[2]<<16|e[1]<<8|e[0]},j=function(e){return U(e,23,4)},D=function(e){return U(e,52,8)},N=function(e,t){g(e.prototype,t,{get:function(){return w(this)[t]}})},B=function(e,t,n,r){var i=p(n),o=w(e);if(i+t>o.byteLength)throw I(S);var a=w(o.buffer).bytes,u=i+o.byteOffset,s=a.slice(u,u+t);return r?s:s.reverse()},q=function(e,t,n,r,i,o){var a=p(n),u=w(e);if(a+t>u.byteLength)throw I(S);for(var s=w(u.buffer).bytes,l=a+u.byteOffset,c=r(+i),f=0;fG;)(W=Y[G++])in T||a(T,W,F[W]);H.constructor=T}v&&d(L)!==R&&v(L,R);var Q=new C(new T(2)),$=L.setInt8;Q.setInt8(0,2147483648),Q.setInt8(1,2147483649),!Q.getInt8(0)&&Q.getInt8(1)||u(L,{setInt8:function(e,t){$.call(this,e,t<<24>>24)},setUint8:function(e,t){$.call(this,e,t<<24>>24)}},{unsafe:!0})}else T=function(e){l(this,T,k);var t=p(e);E(this,{bytes:m.call(new Array(t),0),byteLength:t}),i||(this.byteLength=t)},C=function(e,t,n){l(this,C,A),l(e,T,A);var r=w(e).byteLength,o=c(t);if(o<0||o>r)throw I("Wrong offset");if(o+(n=void 0===n?r-o:f(n))>r)throw I("Wrong length");E(this,{buffer:e,byteLength:n,byteOffset:o}),i||(this.buffer=e,this.byteLength=n,this.byteOffset=o)},i&&(N(T,"byteLength"),N(C,"buffer"),N(C,"byteLength"),N(C,"byteOffset")),u(C.prototype,{getInt8:function(e){return B(this,1,e)[0]<<24>>24},getUint8:function(e){return B(this,1,e)[0]},getInt16:function(e){var t=B(this,2,e,arguments.length>1?arguments[1]:void 0);return(t[1]<<8|t[0])<<16>>16},getUint16:function(e){var t=B(this,2,e,arguments.length>1?arguments[1]:void 0);return t[1]<<8|t[0]},getInt32:function(e){return P(B(this,4,e,arguments.length>1?arguments[1]:void 0))},getUint32:function(e){return P(B(this,4,e,arguments.length>1?arguments[1]:void 0))>>>0},getFloat32:function(e){return O(B(this,4,e,arguments.length>1?arguments[1]:void 0),23)},getFloat64:function(e){return O(B(this,8,e,arguments.length>1?arguments[1]:void 0),52)},setInt8:function(e,t){q(this,1,e,_,t)},setUint8:function(e,t){q(this,1,e,_,t)},setInt16:function(e,t){q(this,2,e,M,t,arguments.length>2?arguments[2]:void 0)},setUint16:function(e,t){q(this,2,e,M,t,arguments.length>2?arguments[2]:void 0)},setInt32:function(e,t){q(this,4,e,z,t,arguments.length>2?arguments[2]:void 0)},setUint32:function(e,t){q(this,4,e,z,t,arguments.length>2?arguments[2]:void 0)},setFloat32:function(e,t){q(this,4,e,j,t,arguments.length>2?arguments[2]:void 0)},setFloat64:function(e,t){q(this,8,e,D,t,arguments.length>2?arguments[2]:void 0)}});b(T,k),b(C,A),e.exports={ArrayBuffer:T,DataView:C}},1048:function(e,t,n){"use strict";var r=n(7908),i=n(1400),o=n(7466),a=Math.min;e.exports=[].copyWithin||function(e,t){var n=r(this),u=o(n.length),s=i(e,u),l=i(t,u),c=arguments.length>2?arguments[2]:void 0,f=a((void 0===c?u:i(c,u))-l,u-s),p=1;for(l0;)l in n?n[s]=n[l]:delete n[s],s+=p,l+=p;return n}},1285:function(e,t,n){"use strict";var r=n(7908),i=n(1400),o=n(7466);e.exports=function(e){for(var t=r(this),n=o(t.length),a=arguments.length,u=i(a>1?arguments[1]:void 0,n),s=a>2?arguments[2]:void 0,l=void 0===s?n:i(s,n);l>u;)t[u++]=e;return t}},8533:function(e,t,n){"use strict";var r=n(2092).forEach,i=n(9341)("forEach");e.exports=i?[].forEach:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}},8457:function(e,t,n){"use strict";var r=n(9974),i=n(7908),o=n(3411),a=n(7659),u=n(7466),s=n(6135),l=n(1246);e.exports=function(e){var t,n,c,f,p,h,d=i(e),v="function"==typeof this?this:Array,y=arguments.length,g=y>1?arguments[1]:void 0,m=void 0!==g,b=l(d),x=0;if(m&&(g=r(g,y>2?arguments[2]:void 0,2)),null==b||v==Array&&a(b))for(n=new v(t=u(d.length));t>x;x++)h=m?g(d[x],x):d[x],s(n,x,h);else for(p=(f=b.call(d)).next,n=new v;!(c=p.call(f)).done;x++)h=m?o(f,g,[c.value,x],!0):c.value,s(n,x,h);return n.length=x,n}},1318:function(e,t,n){var r=n(5656),i=n(7466),o=n(1400),a=function(e){return function(t,n,a){var u,s=r(t),l=i(s.length),c=o(a,l);if(e&&n!=n){for(;l>c;)if((u=s[c++])!=u)return!0}else for(;l>c;c++)if((e||c in s)&&s[c]===n)return e||c||0;return!e&&-1}};e.exports={includes:a(!0),indexOf:a(!1)}},2092:function(e,t,n){var r=n(9974),i=n(8361),o=n(7908),a=n(7466),u=n(5417),s=[].push,l=function(e){var t=1==e,n=2==e,l=3==e,c=4==e,f=6==e,p=7==e,h=5==e||f;return function(d,v,y,g){for(var m,b,x=o(d),w=i(x),E=r(v,y,3),k=a(w.length),A=0,S=g||u,F=t?S(d,k):n||p?S(d,0):void 0;k>A;A++)if((h||A in w)&&(b=E(m=w[A],A,x),e))if(t)F[A]=b;else if(b)switch(e){case 3:return!0;case 5:return m;case 6:return A;case 2:s.call(F,m)}else switch(e){case 4:return!1;case 7:s.call(F,m)}return f?-1:l||c?c:F}};e.exports={forEach:l(0),map:l(1),filter:l(2),some:l(3),every:l(4),find:l(5),findIndex:l(6),filterOut:l(7)}},6583:function(e,t,n){"use strict";var r=n(5656),i=n(9958),o=n(7466),a=n(9341),u=Math.min,s=[].lastIndexOf,l=!!s&&1/[1].lastIndexOf(1,-0)<0,c=a("lastIndexOf"),f=l||!c;e.exports=f?function(e){if(l)return s.apply(this,arguments)||0;var t=r(this),n=o(t.length),a=n-1;for(arguments.length>1&&(a=u(a,i(arguments[1]))),a<0&&(a=n+a);a>=0;a--)if(a in t&&t[a]===e)return a||0;return-1}:s},1194:function(e,t,n){var r=n(7293),i=n(5112),o=n(7392),a=i("species");e.exports=function(e){return o>=51||!r((function(){var t=[];return(t.constructor={})[a]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},9341:function(e,t,n){"use strict";var r=n(7293);e.exports=function(e,t){var n=[][e];return!!n&&r((function(){n.call(null,t||function(){throw 1},1)}))}},3671:function(e,t,n){var r=n(3099),i=n(7908),o=n(8361),a=n(7466),u=function(e){return function(t,n,u,s){r(n);var l=i(t),c=o(l),f=a(l.length),p=e?f-1:0,h=e?-1:1;if(u<2)for(;;){if(p in c){s=c[p],p+=h;break}if(p+=h,e?p<0:f<=p)throw TypeError("Reduce of empty array with no initial value")}for(;e?p>=0:f>p;p+=h)p in c&&(s=n(s,c[p],p,l));return s}};e.exports={left:u(!1),right:u(!0)}},5417:function(e,t,n){var r=n(111),i=n(3157),o=n(5112)("species");e.exports=function(e,t){var n;return i(e)&&("function"!=typeof(n=e.constructor)||n!==Array&&!i(n.prototype)?r(n)&&null===(n=n[o])&&(n=void 0):n=void 0),new(void 0===n?Array:n)(0===t?0:t)}},3411:function(e,t,n){var r=n(9670),i=n(9212);e.exports=function(e,t,n,o){try{return o?t(r(n)[0],n[1]):t(n)}catch(t){throw i(e),t}}},7072:function(e,t,n){var r=n(5112)("iterator"),i=!1;try{var o=0,a={next:function(){return{done:!!o++}},return:function(){i=!0}};a[r]=function(){return this},Array.from(a,(function(){throw 2}))}catch(e){}e.exports=function(e,t){if(!t&&!i)return!1;var n=!1;try{var o={};o[r]=function(){return{next:function(){return{done:n=!0}}}},e(o)}catch(e){}return n}},4326:function(e){var t={}.toString;e.exports=function(e){return t.call(e).slice(8,-1)}},648:function(e,t,n){var r=n(1694),i=n(4326),o=n(5112)("toStringTag"),a="Arguments"==i(function(){return arguments}());e.exports=r?i:function(e){var t,n,r;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),o))?n:a?i(t):"Object"==(r=i(t))&&"function"==typeof t.callee?"Arguments":r}},9920:function(e,t,n){var r=n(6656),i=n(3887),o=n(1236),a=n(3070);e.exports=function(e,t){for(var n=i(t),u=a.f,s=o.f,l=0;l=74)&&(r=a.match(/Chrome\/(\d+)/))&&(i=r[1]),e.exports=i&&+i},748:function(e){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(e,t,n){var r=n(7854),i=n(1236).f,o=n(8880),a=n(1320),u=n(3505),s=n(9920),l=n(4705);e.exports=function(e,t){var n,c,f,p,h,d=e.target,v=e.global,y=e.stat;if(n=v?r:y?r[d]||u(d,{}):(r[d]||{}).prototype)for(c in t){if(p=t[c],f=e.noTargetGet?(h=i(n,c))&&h.value:n[c],!l(v?c:d+(y?".":"#")+c,e.forced)&&void 0!==f){if(typeof p==typeof f)continue;s(p,f)}(e.sham||f&&f.sham)&&o(p,"sham",!0),a(n,c,p,e)}}},7293:function(e){e.exports=function(e){try{return!!e()}catch(e){return!0}}},7007:function(e,t,n){"use strict";n(4916);var r=n(1320),i=n(7293),o=n(5112),a=n(2261),u=n(8880),s=o("species"),l=!i((function(){var e=/./;return e.exec=function(){var e=[];return e.groups={a:"7"},e},"7"!=="".replace(e,"$")})),c="$0"==="a".replace(/./,"$0"),f=o("replace"),p=!!/./[f]&&""===/./[f]("a","$0"),h=!i((function(){var e=/(?:)/,t=e.exec;e.exec=function(){return t.apply(this,arguments)};var n="ab".split(e);return 2!==n.length||"a"!==n[0]||"b"!==n[1]}));e.exports=function(e,t,n,f){var d=o(e),v=!i((function(){var t={};return t[d]=function(){return 7},7!=""[e](t)})),y=v&&!i((function(){var t=!1,n=/a/;return"split"===e&&((n={}).constructor={},n.constructor[s]=function(){return n},n.flags="",n[d]=/./[d]),n.exec=function(){return t=!0,null},n[d](""),!t}));if(!v||!y||"replace"===e&&(!l||!c||p)||"split"===e&&!h){var g=/./[d],m=n(d,""[e],(function(e,t,n,r,i){return t.exec===a?v&&!i?{done:!0,value:g.call(t,n,r)}:{done:!0,value:e.call(n,t,r)}:{done:!1}}),{REPLACE_KEEPS_$0:c,REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE:p}),b=m[0],x=m[1];r(String.prototype,e,b),r(RegExp.prototype,d,2==t?function(e,t){return x.call(e,this,t)}:function(e){return x.call(e,this)})}f&&u(RegExp.prototype[d],"sham",!0)}},9974:function(e,t,n){var r=n(3099);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 0:return function(){return e.call(t)};case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,i){return e.call(t,n,r,i)}}return function(){return e.apply(t,arguments)}}},5005:function(e,t,n){var r=n(857),i=n(7854),o=function(e){return"function"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?o(r[e])||o(i[e]):r[e]&&r[e][t]||i[e]&&i[e][t]}},1246:function(e,t,n){var r=n(648),i=n(7497),o=n(5112)("iterator");e.exports=function(e){if(null!=e)return e[o]||e["@@iterator"]||i[r(e)]}},8554:function(e,t,n){var r=n(9670),i=n(1246);e.exports=function(e){var t=i(e);if("function"!=typeof t)throw TypeError(String(e)+" is not iterable");return r(t.call(e))}},647:function(e,t,n){var r=n(7908),i=Math.floor,o="".replace,a=/\$([$&'`]|\d\d?|<[^>]*>)/g,u=/\$([$&'`]|\d\d?)/g;e.exports=function(e,t,n,s,l,c){var f=n+e.length,p=s.length,h=u;return void 0!==l&&(l=r(l),h=a),o.call(c,h,(function(r,o){var a;switch(o.charAt(0)){case"$":return"$";case"&":return e;case"`":return t.slice(0,n);case"'":return t.slice(f);case"<":a=l[o.slice(1,-1)];break;default:var u=+o;if(0===u)return r;if(u>p){var c=i(u/10);return 0===c?r:c<=p?void 0===s[c-1]?o.charAt(1):s[c-1]+o.charAt(1):r}a=s[u-1]}return void 0===a?"":a}))}},7854:function(e,t,n){var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n.g&&n.g)||function(){return this}()||Function("return this")()},6656:function(e){var t={}.hasOwnProperty;e.exports=function(e,n){return t.call(e,n)}},3501:function(e){e.exports={}},490:function(e,t,n){var r=n(5005);e.exports=r("document","documentElement")},4664:function(e,t,n){var r=n(9781),i=n(7293),o=n(317);e.exports=!r&&!i((function(){return 7!=Object.defineProperty(o("div"),"a",{get:function(){return 7}}).a}))},1179:function(e){var t=Math.abs,n=Math.pow,r=Math.floor,i=Math.log,o=Math.LN2;e.exports={pack:function(e,a,u){var s,l,c,f=new Array(u),p=8*u-a-1,h=(1<>1,v=23===a?n(2,-24)-n(2,-77):0,y=e<0||0===e&&1/e<0?1:0,g=0;for((e=t(e))!=e||e===1/0?(l=e!=e?1:0,s=h):(s=r(i(e)/o),e*(c=n(2,-s))<1&&(s--,c*=2),(e+=s+d>=1?v/c:v*n(2,1-d))*c>=2&&(s++,c/=2),s+d>=h?(l=0,s=h):s+d>=1?(l=(e*c-1)*n(2,a),s+=d):(l=e*n(2,d-1)*n(2,a),s=0));a>=8;f[g++]=255&l,l/=256,a-=8);for(s=s<0;f[g++]=255&s,s/=256,p-=8);return f[--g]|=128*y,f},unpack:function(e,t){var r,i=e.length,o=8*i-t-1,a=(1<>1,s=o-7,l=i-1,c=e[l--],f=127&c;for(c>>=7;s>0;f=256*f+e[l],l--,s-=8);for(r=f&(1<<-s)-1,f>>=-s,s+=t;s>0;r=256*r+e[l],l--,s-=8);if(0===f)f=1-u;else{if(f===a)return r?NaN:c?-1/0:1/0;r+=n(2,t),f-=u}return(c?-1:1)*r*n(2,f-t)}}},8361:function(e,t,n){var r=n(7293),i=n(4326),o="".split;e.exports=r((function(){return!Object("z").propertyIsEnumerable(0)}))?function(e){return"String"==i(e)?o.call(e,""):Object(e)}:Object},9587:function(e,t,n){var r=n(111),i=n(7674);e.exports=function(e,t,n){var o,a;return i&&"function"==typeof(o=t.constructor)&&o!==n&&r(a=o.prototype)&&a!==n.prototype&&i(e,a),e}},2788:function(e,t,n){var r=n(5465),i=Function.toString;"function"!=typeof r.inspectSource&&(r.inspectSource=function(e){return i.call(e)}),e.exports=r.inspectSource},9909:function(e,t,n){var r,i,o,a=n(8536),u=n(7854),s=n(111),l=n(8880),c=n(6656),f=n(5465),p=n(6200),h=n(3501),d=u.WeakMap;if(a){var v=f.state||(f.state=new d),y=v.get,g=v.has,m=v.set;r=function(e,t){return t.facade=e,m.call(v,e,t),t},i=function(e){return y.call(v,e)||{}},o=function(e){return g.call(v,e)}}else{var b=p("state");h[b]=!0,r=function(e,t){return t.facade=e,l(e,b,t),t},i=function(e){return c(e,b)?e[b]:{}},o=function(e){return c(e,b)}}e.exports={set:r,get:i,has:o,enforce:function(e){return o(e)?i(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!s(t)||(n=i(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return n}}}},7659:function(e,t,n){var r=n(5112),i=n(7497),o=r("iterator"),a=Array.prototype;e.exports=function(e){return void 0!==e&&(i.Array===e||a[o]===e)}},3157:function(e,t,n){var r=n(4326);e.exports=Array.isArray||function(e){return"Array"==r(e)}},4705:function(e,t,n){var r=n(7293),i=/#|\.prototype\./,o=function(e,t){var n=u[a(e)];return n==l||n!=s&&("function"==typeof t?r(t):!!t)},a=o.normalize=function(e){return String(e).replace(i,".").toLowerCase()},u=o.data={},s=o.NATIVE="N",l=o.POLYFILL="P";e.exports=o},111:function(e){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},1913:function(e){e.exports=!1},7850:function(e,t,n){var r=n(111),i=n(4326),o=n(5112)("match");e.exports=function(e){var t;return r(e)&&(void 0!==(t=e[o])?!!t:"RegExp"==i(e))}},9212:function(e,t,n){var r=n(9670);e.exports=function(e){var t=e.return;if(void 0!==t)return r(t.call(e)).value}},3383:function(e,t,n){"use strict";var r,i,o,a=n(7293),u=n(9518),s=n(8880),l=n(6656),c=n(5112),f=n(1913),p=c("iterator"),h=!1;[].keys&&("next"in(o=[].keys())?(i=u(u(o)))!==Object.prototype&&(r=i):h=!0);var d=null==r||a((function(){var e={};return r[p].call(e)!==e}));d&&(r={}),f&&!d||l(r,p)||s(r,p,(function(){return this})),e.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:h}},7497:function(e){e.exports={}},133:function(e,t,n){var r=n(7293);e.exports=!!Object.getOwnPropertySymbols&&!r((function(){return!String(Symbol())}))},590:function(e,t,n){var r=n(7293),i=n(5112),o=n(1913),a=i("iterator");e.exports=!r((function(){var e=new URL("b?a=1&b=2&c=3","http://a"),t=e.searchParams,n="";return e.pathname="c%20d",t.forEach((function(e,r){t.delete("b"),n+=r+e})),o&&!e.toJSON||!t.sort||"http://a/c%20d?a=1&c=3"!==e.href||"3"!==t.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!t[a]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("http://тест").host||"#%D0%B1"!==new URL("http://a#б").hash||"a1c3"!==n||"x"!==new URL("http://x",void 0).host}))},8536:function(e,t,n){var r=n(7854),i=n(2788),o=r.WeakMap;e.exports="function"==typeof o&&/native code/.test(i(o))},1574:function(e,t,n){"use strict";var r=n(9781),i=n(7293),o=n(1956),a=n(5181),u=n(5296),s=n(7908),l=n(8361),c=Object.assign,f=Object.defineProperty;e.exports=!c||i((function(){if(r&&1!==c({b:1},c(f({},"a",{enumerable:!0,get:function(){f(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var e={},t={},n=Symbol(),i="abcdefghijklmnopqrst";return e[n]=7,i.split("").forEach((function(e){t[e]=e})),7!=c({},e)[n]||o(c({},t)).join("")!=i}))?function(e,t){for(var n=s(e),i=arguments.length,c=1,f=a.f,p=u.f;i>c;)for(var h,d=l(arguments[c++]),v=f?o(d).concat(f(d)):o(d),y=v.length,g=0;y>g;)h=v[g++],r&&!p.call(d,h)||(n[h]=d[h]);return n}:c},30:function(e,t,n){var r,i=n(9670),o=n(6048),a=n(748),u=n(3501),s=n(490),l=n(317),c=n(6200)("IE_PROTO"),f=function(){},p=function(e){return" diff --git a/resources/views/livewire/profile/index.blade.php b/resources/views/livewire/profile/index.blade.php index fc367e6f25..087aba3199 100644 --- a/resources/views/livewire/profile/index.blade.php +++ b/resources/views/livewire/profile/index.blade.php @@ -59,6 +59,7 @@ class="font-mono pr-10" /> - + @if (!$application->destination->server->isSwarm()) - + @endif diff --git a/resources/views/livewire/project/database/backup-edit.blade.php b/resources/views/livewire/project/database/backup-edit.blade.php index b91b35802a..59628636d7 100644 --- a/resources/views/livewire/project/database/backup-edit.blade.php +++ b/resources/views/livewire/project/database/backup-edit.blade.php @@ -70,6 +70,8 @@
+
diff --git a/resources/views/livewire/project/database/backup-now.blade.php b/resources/views/livewire/project/database/backup-now.blade.php index 9fa698c31a..e5b1daf8d8 100644 --- a/resources/views/livewire/project/database/backup-now.blade.php +++ b/resources/views/livewire/project/database/backup-now.blade.php @@ -1 +1 @@ -Backup Now +Backup Now diff --git a/resources/views/livewire/project/database/configuration.blade.php b/resources/views/livewire/project/database/configuration.blade.php index 652aec3eb0..d3649f94a5 100644 --- a/resources/views/livewire/project/database/configuration.blade.php +++ b/resources/views/livewire/project/database/configuration.blade.php @@ -5,57 +5,44 @@

Configuration

-
+
-
+ @if ($currentRoute === 'project.database.configuration') @if ($database->type() === 'standalone-postgresql') @elseif ($database->type() === 'standalone-redis') @@ -73,37 +60,27 @@ @elseif ($database->type() === 'standalone-clickhouse') @endif -
-
+ @elseif ($currentRoute === 'project.database.environment-variables') -
-
+ @elseif ($currentRoute === 'project.database.servers') -
-
+ @elseif ($currentRoute === 'project.database.persistent-storage') -
-
+ @elseif ($currentRoute === 'project.database.import-backups') + + @elseif ($currentRoute === 'project.database.webhooks') -
-
+ @elseif ($currentRoute === 'project.database.resource-limits') -
-
- -
-
+ @elseif ($currentRoute === 'project.database.resource-operations') -
-
+ @elseif ($currentRoute === 'project.database.metrics') -
-
- -
-
+ @elseif ($currentRoute === 'project.database.tags') + + @elseif ($currentRoute === 'project.database.danger') -
+ @endif
diff --git a/resources/views/livewire/project/database/heading.blade.php b/resources/views/livewire/project/database/heading.blade.php index e334b2ce99..237d679c64 100644 --- a/resources/views/livewire/project/database/heading.blade.php +++ b/resources/views/livewire/project/database/heading.blade.php @@ -3,7 +3,7 @@ Database Startup - +