diff --git a/app/Actions/Proxy/CheckConfiguration.php b/app/Actions/Proxy/CheckConfiguration.php index 3cbd5669d..f4fe650c5 100644 --- a/app/Actions/Proxy/CheckConfiguration.php +++ b/app/Actions/Proxy/CheckConfiguration.php @@ -21,7 +21,6 @@ public function handle(Server $server, bool $reset = false) "cat $proxy_path/docker-compose.yml", ]; $proxy_configuration = instant_remote_process($payload, $server, false); - if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) { $proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value; } diff --git a/app/Actions/Server/ValidateServer.php b/app/Actions/Server/ValidateServer.php new file mode 100644 index 000000000..d0a4cd6be --- /dev/null +++ b/app/Actions/Server/ValidateServer.php @@ -0,0 +1,67 @@ +update([ + 'validation_logs' => null, + ]); + ['uptime' => $this->uptime, 'error' => $error] = $server->validateConnection(); + if (! $this->uptime) { + $this->error = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.

Error: '.$error.'
'; + $server->update([ + 'validation_logs' => $this->error, + ]); + throw new \Exception($this->error); + } + $this->supported_os_type = $server->validateOS(); + if (! $this->supported_os_type) { + $this->error = 'Server OS type is not supported. Please install Docker manually before continuing: documentation.'; + $server->update([ + 'validation_logs' => $this->error, + ]); + throw new \Exception($this->error); + } + + $this->docker_installed = $server->validateDockerEngine(); + $this->docker_compose_installed = $server->validateDockerCompose(); + if (! $this->docker_installed || ! $this->docker_compose_installed) { + $this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: documentation.'; + $server->update([ + 'validation_logs' => $this->error, + ]); + throw new \Exception($this->error); + } + $this->docker_version = $server->validateDockerEngineVersion(); + + if ($this->docker_version) { + return 'OK'; + } else { + $this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: documentation.'; + $server->update([ + 'validation_logs' => $this->error, + ]); + throw new \Exception($this->error); + } + } +} diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index 6ae1825e9..1de8126d9 100644 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -167,7 +167,7 @@ public function environment_details(Request $request) schema: new OA\Schema( type: 'object', properties: [ - 'name' => ['type' => 'string', 'description' => 'The name of the project.'], + 'uuid' => ['type' => 'string', 'description' => 'The name of the project.'], 'description' => ['type' => 'string', 'description' => 'The description of the project.'], ], ), @@ -183,9 +183,7 @@ public function environment_details(Request $request) schema: new OA\Schema( type: 'object', properties: [ - 'uuid' => ['type' => 'string', 'example' => 'og888os'], - 'name' => ['type' => 'string', 'example' => 'Project Name'], - 'description' => ['type' => 'string', 'example' => 'Project Description'], + 'uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the project.'], ] ) ), @@ -218,7 +216,7 @@ public function create_project(Request $request) return $return; } $validator = customApiValidator($request->all(), [ - 'name' => 'string|max:255', + 'name' => 'string|max:255|required', 'description' => 'string|nullable', ]); @@ -245,8 +243,6 @@ public function create_project(Request $request) return response()->json([ 'uuid' => $project->uuid, - 'name' => $project->name, - 'description' => $project->description, ])->setStatusCode(201); } diff --git a/app/Http/Controllers/Api/ServersController.php b/app/Http/Controllers/Api/ServersController.php index da9c3b2d8..b3c429270 100644 --- a/app/Http/Controllers/Api/ServersController.php +++ b/app/Http/Controllers/Api/ServersController.php @@ -2,8 +2,12 @@ namespace App\Http\Controllers\Api; +use App\Actions\Server\ValidateServer; +use App\Enums\ProxyStatus; +use App\Enums\ProxyTypes; use App\Http\Controllers\Controller; use App\Models\Application; +use App\Models\PrivateKey; use App\Models\Project; use App\Models\Server as ModelsServer; use Illuminate\Http\Request; @@ -75,7 +79,7 @@ public function servers(Request $request) if (is_null($teamId)) { return invalidTokenResponse(); } - $servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) { + $servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port', 'description')->get()->load(['settings'])->map(function ($server) { $server['is_reachable'] = $server->settings->is_reachable; $server['is_usable'] = $server->settings->is_usable; @@ -392,4 +396,390 @@ public function domains_by_server(Request $request) return response()->json(serializeApiResponse($domains)); } + + #[OA\Post( + summary: 'Create Server', + description: 'Create Server.', + path: '/servers', + security: [ + ['bearerAuth' => []], + ], + tags: ['Servers'], + requestBody: new OA\RequestBody( + required: true, + description: 'Server created.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'name' => ['type' => 'string', 'example' => 'My Server', 'description' => 'The name of the server.'], + 'description' => ['type' => 'string', 'example' => 'My Server Description', 'description' => 'The description of the server.'], + 'ip' => ['type' => 'string', 'example' => '127.0.0.1', 'description' => 'The IP of the server.'], + 'port' => ['type' => 'integer', 'example' => 22, 'description' => 'The port of the server.'], + 'user' => ['type' => 'string', 'example' => 'root', 'description' => 'The user of the server.'], + 'private_key_uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the private key.'], + 'is_build_server' => ['type' => 'boolean', 'example' => false, 'description' => 'Is build server.'], + 'instant_validate' => ['type' => 'boolean', 'example' => false, 'description' => 'Instant validate.'], + ], + ), + ), + ), + responses: [ + new OA\Response( + response: 201, + description: 'Server created.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string', 'example' => 'og888os', 'description' => 'The UUID of the server.'], + ] + ) + ), + ]), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 404, + ref: '#/components/responses/404', + ), + ] + )] + public function create_server(Request $request) + { + $allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate']; + + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $validator = customApiValidator($request->all(), [ + 'name' => 'string|max:255', + 'description' => 'string|nullable', + 'ip' => 'string|required', + 'port' => 'integer|nullable', + 'private_key_uuid' => 'string|required', + 'user' => 'string|nullable', + 'is_build_server' => 'boolean|nullable', + 'instant_validate' => 'boolean|nullable', + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + if (! $request->name) { + $request->offsetSet('name', generate_random_name()); + } + if (! $request->user) { + $request->offsetSet('user', 'root'); + } + if (is_null($request->port)) { + $request->offsetSet('port', 22); + } + if (is_null($request->is_build_server)) { + $request->offsetSet('is_build_server', false); + } + if (is_null($request->instant_validate)) { + $request->offsetSet('instant_validate', false); + } + $privateKey = PrivateKey::whereTeamId($teamId)->whereUuid($request->private_key_uuid)->first(); + if (! $privateKey) { + return response()->json(['message' => 'Private key not found.'], 404); + } + $allServers = ModelsServer::whereIp($request->ip)->get(); + if ($allServers->count() > 0) { + return response()->json(['message' => 'Server with this IP already exists.'], 400); + } + + $server = ModelsServer::create([ + 'name' => $request->name, + 'description' => $request->description, + 'ip' => $request->ip, + 'port' => $request->port, + 'user' => $request->user, + 'private_key_id' => $privateKey->id, + 'team_id' => $teamId, + 'proxy' => [ + 'type' => ProxyTypes::TRAEFIK_V2->value, + 'status' => ProxyStatus::EXITED->value, + ], + ]); + $server->settings()->update([ + 'is_build_server' => $request->is_build_server, + ]); + if ($request->instant_validate) { + ValidateServer::dispatch($server); + } + + return response()->json([ + 'uuid' => $server->uuid, + ])->setStatusCode(201); + } + + #[OA\Patch( + summary: 'Update Server', + description: 'Update Server.', + path: '/servers/{uuid}', + security: [ + ['bearerAuth' => []], + ], + tags: ['Servers'], + requestBody: new OA\RequestBody( + required: true, + description: 'Server updated.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'name' => ['type' => 'string', 'description' => 'The name of the server.'], + 'description' => ['type' => 'string', 'description' => 'The description of the server.'], + 'ip' => ['type' => 'string', 'description' => 'The IP of the server.'], + 'port' => ['type' => 'integer', 'description' => 'The port of the server.'], + 'user' => ['type' => 'string', 'description' => 'The user of the server.'], + 'private_key_uuid' => ['type' => 'string', 'description' => 'The UUID of the private key.'], + 'is_build_server' => ['type' => 'boolean', 'description' => 'Is build server.'], + 'instant_validate' => ['type' => 'boolean', 'description' => 'Instant validate.'], + ], + ), + ), + ), + responses: [ + new OA\Response( + response: 201, + description: 'Server updated.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'array', + items: new OA\Items(ref: '#/components/schemas/Server') + ) + ), + ]), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 404, + ref: '#/components/responses/404', + ), + ] + )] + public function update_server(Request $request) + { + $allowedFields = ['name', 'description', 'ip', 'port', 'user', 'private_key_uuid', 'is_build_server', 'instant_validate']; + + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + $validator = customApiValidator($request->all(), [ + 'name' => 'string|max:255|nullable', + 'description' => 'string|nullable', + 'ip' => 'string|nullable', + 'port' => 'integer|nullable', + 'private_key_uuid' => 'string|nullable', + 'user' => 'string|nullable', + 'is_build_server' => 'boolean|nullable', + 'instant_validate' => 'boolean|nullable', + ]); + + $extraFields = array_diff(array_keys($request->all()), $allowedFields); + if ($validator->fails() || ! empty($extraFields)) { + $errors = $validator->errors(); + if (! empty($extraFields)) { + foreach ($extraFields as $field) { + $errors->add($field, 'This field is not allowed.'); + } + } + + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $errors, + ], 422); + } + $server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first(); + if (! $server) { + return response()->json(['message' => 'Server not found.'], 404); + } + $server->update($request->only(['name', 'description', 'ip', 'port', 'user'])); + if ($request->is_build_server) { + $server->settings()->update([ + 'is_build_server' => $request->is_build_server, + ]); + } + if ($request->instant_validate) { + ValidateServer::dispatch($server); + } + + return response()->json(serializeApiResponse($server))->setStatusCode(201); + } + + #[OA\Delete( + summary: 'Delete', + description: 'Delete server by UUID.', + path: '/servers/{uuid}', + security: [ + ['bearerAuth' => []], + ], + tags: ['Servers'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the server.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Server deleted.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Server deleted.'], + ] + ) + ), + ]), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 404, + ref: '#/components/responses/404', + ), + ] + )] + public function delete_server(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + if (! $request->uuid) { + return response()->json(['message' => 'Uuid is required.'], 422); + } + $server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first(); + + if (! $server) { + return response()->json(['message' => 'Server not found.'], 404); + } + if ($server->definedResources()->count() > 0) { + return response()->json(['message' => 'Server has resources, so you need to delete them before.'], 400); + } + $server->delete(); + + return response()->json(['message' => 'Server deleted.']); + } + + #[OA\Get( + summary: 'Validate', + description: 'Validate server by UUID.', + path: '/servers/{uuid}/validate', + security: [ + ['bearerAuth' => []], + ], + tags: ['Servers'], + parameters: [ + new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server UUID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response( + response: 201, + description: 'Server validation started.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Validation started.'], + ] + ) + ), + ]), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 404, + ref: '#/components/responses/404', + ), + ] + )] + public function validate_server(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + if (! $request->uuid) { + return response()->json(['message' => 'Uuid is required.'], 422); + } + $server = ModelsServer::whereTeamId($teamId)->whereUuid($request->uuid)->first(); + + if (! $server) { + return response()->json(['message' => 'Server not found.'], 404); + } + ValidateServer::dispatch($server); + + return response()->json(['message' => 'Validation started.']); + } } diff --git a/app/Livewire/Boarding/Index.php b/app/Livewire/Boarding/Index.php index 7acf5ed87..8127ca009 100644 --- a/app/Livewire/Boarding/Index.php +++ b/app/Livewire/Boarding/Index.php @@ -257,7 +257,6 @@ public function saveServer() $this->createdServer->settings->is_swarm_manager = $this->isSwarmManager; $this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel; $this->createdServer->settings->save(); - $this->createdServer->addInitialNetwork(); $this->selectedExistingServer = $this->createdServer->id; $this->currentState = 'validate-server'; } diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php index c2a55afcb..9934ea345 100644 --- a/app/Livewire/Server/Form.php +++ b/app/Livewire/Server/Form.php @@ -164,6 +164,9 @@ public function checkLocalhostConnection() public function validateServer($install = true) { + $this->server->update([ + 'validation_logs' => null, + ]); $this->dispatch('init', $install); } diff --git a/app/Livewire/Server/New/ByIp.php b/app/Livewire/Server/New/ByIp.php index 0aad33b1c..0f4c1afea 100644 --- a/app/Livewire/Server/New/ByIp.php +++ b/app/Livewire/Server/New/ByIp.php @@ -124,7 +124,6 @@ public function submit() } $server->settings->is_build_server = $this->is_build_server; $server->settings->save(); - $server->addInitialNetwork(); return redirect()->route('server.show', $server->uuid); } catch (\Throwable $e) { diff --git a/app/Livewire/Server/ValidateAndInstall.php b/app/Livewire/Server/ValidateAndInstall.php index 422cae779..8c5bc23ed 100644 --- a/app/Livewire/Server/ValidateAndInstall.php +++ b/app/Livewire/Server/ValidateAndInstall.php @@ -87,7 +87,10 @@ public function validateConnection() { ['uptime' => $this->uptime, 'error' => $error] = $this->server->validateConnection(); if (! $this->uptime) { - $this->error = 'Server is not reachable. Please validate your configuration and connection.

Check this documentation for further help.

Error: '.$error; + $this->error = 'Server is not reachable. Please validate your configuration and connection.
Check this documentation for further help.

Error: '.$error.'
'; + $this->server->update([ + 'validation_logs' => $this->error, + ]); return; } @@ -99,6 +102,9 @@ public function validateOS() $this->supported_os_type = $this->server->validateOS(); if (! $this->supported_os_type) { $this->error = 'Server OS type is not supported. Please install Docker manually before continuing: documentation.'; + $this->server->update([ + 'validation_logs' => $this->error, + ]); return; } @@ -113,6 +119,9 @@ public function validateDockerEngine() if ($this->install) { if ($this->number_of_tries == $this->max_tries) { $this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: documentation.'; + $this->server->update([ + 'validation_logs' => $this->error, + ]); return; } else { @@ -126,6 +135,9 @@ public function validateDockerEngine() } } else { $this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: documentation.'; + $this->server->update([ + 'validation_logs' => $this->error, + ]); return; } @@ -148,6 +160,9 @@ public function validateDockerVersion() $this->dispatch('success', 'Server validated.'); } else { $this->error = 'Docker Engine version is not 22+. Please install Docker manually before continuing: documentation.'; + $this->server->update([ + 'validation_logs' => $this->error, + ]); return; } diff --git a/app/Models/Server.php b/app/Models/Server.php index e164f2e27..9166f5a0f 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -19,85 +19,23 @@ use Symfony\Component\Yaml\Yaml; #[OA\Schema( - description: 'Application model', + description: 'Server model', type: 'object', properties: [ 'id' => ['type' => 'integer'], - 'repository_project_id' => ['type' => 'integer', 'nullable' => true], 'uuid' => ['type' => 'string'], 'name' => ['type' => 'string'], - 'fqdn' => ['type' => 'string'], - 'config_hash' => ['type' => 'string'], - 'git_repository' => ['type' => 'string'], - 'git_branch' => ['type' => 'string'], - 'git_commit_sha' => ['type' => 'string'], - 'git_full_url' => ['type' => 'string', 'nullable' => true], - 'docker_registry_image_name' => ['type' => 'string', 'nullable' => true], - 'docker_registry_image_tag' => ['type' => 'string', 'nullable' => true], - 'build_pack' => ['type' => 'string'], - 'static_image' => ['type' => 'string'], - 'install_command' => ['type' => 'string'], - 'build_command' => ['type' => 'string'], - 'start_command' => ['type' => 'string'], - 'ports_exposes' => ['type' => 'string'], - 'ports_mappings' => ['type' => 'string', 'nullable' => true], - 'base_directory' => ['type' => 'string'], - 'publish_directory' => ['type' => 'string'], - 'health_check_path' => ['type' => 'string'], - 'health_check_port' => ['type' => 'string', 'nullable' => true], - 'health_check_host' => ['type' => 'string'], - 'health_check_method' => ['type' => 'string'], - 'health_check_return_code' => ['type' => 'integer'], - 'health_check_scheme' => ['type' => 'string'], - 'health_check_response_text' => ['type' => 'string', 'nullable' => true], - 'health_check_interval' => ['type' => 'integer'], - 'health_check_timeout' => ['type' => 'integer'], - 'health_check_retries' => ['type' => 'integer'], - 'health_check_start_period' => ['type' => 'integer'], - 'limits_memory' => ['type' => 'string'], - 'limits_memory_swap' => ['type' => 'string'], - 'limits_memory_swappiness' => ['type' => 'integer'], - 'limits_memory_reservation' => ['type' => 'string'], - 'limits_cpus' => ['type' => 'string'], - 'limits_cpuset' => ['type' => 'string', 'nullable' => true], - 'limits_cpu_shares' => ['type' => 'integer'], - 'status' => ['type' => 'string'], - 'preview_url_template' => ['type' => 'string'], - 'destination_type' => ['type' => 'string'], - 'destination_id' => ['type' => 'integer'], - 'source_type' => ['type' => 'string'], - 'source_id' => ['type' => 'integer'], - 'private_key_id' => ['type' => 'integer', 'nullable' => true], - 'environment_id' => ['type' => 'integer'], - 'created_at' => ['type' => 'string', 'format' => 'date-time'], - 'updated_at' => ['type' => 'string', 'format' => 'date-time'], - 'description' => ['type' => 'string', 'nullable' => true], - 'dockerfile' => ['type' => 'string', 'nullable' => true], - 'health_check_enabled' => ['type' => 'boolean'], - 'dockerfile_location' => ['type' => 'string'], - 'custom_labels' => ['type' => 'string'], - 'dockerfile_target_build' => ['type' => 'string', 'nullable' => true], - 'manual_webhook_secret_github' => ['type' => 'string', 'nullable' => true], - 'manual_webhook_secret_gitlab' => ['type' => 'string', 'nullable' => true], - 'docker_compose_location' => ['type' => 'string'], - 'docker_compose' => ['type' => 'string', 'nullable' => true], - 'docker_compose_raw' => ['type' => 'string', 'nullable' => true], - 'docker_compose_domains' => ['type' => 'string', 'nullable' => true], - 'deleted_at' => ['type' => 'string', 'format' => 'date-time', 'nullable' => true], - 'docker_compose_custom_start_command' => ['type' => 'string', 'nullable' => true], - 'docker_compose_custom_build_command' => ['type' => 'string', 'nullable' => true], - 'swarm_replicas' => ['type' => 'integer'], - 'swarm_placement_constraints' => ['type' => 'string', 'nullable' => true], - 'manual_webhook_secret_bitbucket' => ['type' => 'string', 'nullable' => true], - 'custom_docker_run_options' => ['type' => 'string', 'nullable' => true], - 'post_deployment_command' => ['type' => 'string', 'nullable' => true], - 'post_deployment_command_container' => ['type' => 'string', 'nullable' => true], - 'pre_deployment_command' => ['type' => 'string', 'nullable' => true], - 'pre_deployment_command_container' => ['type' => 'string', 'nullable' => true], - 'watch_paths' => ['type' => 'string', 'nullable' => true], - 'custom_healthcheck_found' => ['type' => 'boolean'], - 'manual_webhook_secret_gitea' => ['type' => 'string', 'nullable' => true], - 'redirect' => ['type' => 'string'], + 'description' => ['type' => 'string'], + 'ip' => ['type' => 'string'], + 'user' => ['type' => 'string'], + 'port' => ['type' => 'integer'], + 'proxy' => ['type' => 'object'], + 'high_disk_usage_notification_sent' => ['type' => 'boolean'], + 'unreachable_notification_sent' => ['type' => 'boolean'], + 'unreachable_count' => ['type' => 'integer'], + 'validation_logs' => ['type' => 'string'], + 'log_drain_notification_sent' => ['type' => 'boolean'], + 'swarm_cluster' => ['type' => 'string'], ] )] @@ -123,6 +61,37 @@ protected static function booted() ServerSetting::create([ 'server_id' => $server->id, ]); + if ($server->id === 0) { + if ($server->isSwarm()) { + SwarmDocker::create([ + 'id' => 0, + 'name' => 'coolify', + 'network' => 'coolify-overlay', + 'server_id' => $server->id, + ]); + } else { + StandaloneDocker::create([ + 'id' => 0, + 'name' => 'coolify', + 'network' => 'coolify', + 'server_id' => $server->id, + ]); + } + } else { + if ($server->isSwarm()) { + SwarmDocker::create([ + 'name' => 'coolify-overlay', + 'network' => 'coolify-overlay', + 'server_id' => $server->id, + ]); + } else { + StandaloneDocker::create([ + 'name' => 'coolify', + 'network' => 'coolify', + 'server_id' => $server->id, + ]); + } + } }); static::deleting(function ($server) { $server->destinations()->each(function ($destination) { @@ -176,41 +145,6 @@ public function settings() return $this->hasOne(ServerSetting::class); } - public function addInitialNetwork() - { - if ($this->id === 0) { - if ($this->isSwarm()) { - SwarmDocker::create([ - 'id' => 0, - 'name' => 'coolify', - 'network' => 'coolify-overlay', - 'server_id' => $this->id, - ]); - } else { - StandaloneDocker::create([ - 'id' => 0, - 'name' => 'coolify', - 'network' => 'coolify', - 'server_id' => $this->id, - ]); - } - } else { - if ($this->isSwarm()) { - SwarmDocker::create([ - 'name' => 'coolify-overlay', - 'network' => 'coolify-overlay', - 'server_id' => $this->id, - ]); - } else { - StandaloneDocker::create([ - 'name' => 'coolify', - 'network' => 'coolify', - 'server_id' => $this->id, - ]); - } - } - } - public function setupDefault404Redirect() { $dynamic_conf_path = $this->proxyPath().'/dynamic'; diff --git a/database/migrations/2024_07_23_112710_add_validation_logs_to_servers.php b/database/migrations/2024_07_23_112710_add_validation_logs_to_servers.php new file mode 100644 index 000000000..d3293620b --- /dev/null +++ b/database/migrations/2024_07_23_112710_add_validation_logs_to_servers.php @@ -0,0 +1,28 @@ +text('validation_logs')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('servers', function (Blueprint $table) { + $table->dropColumn('validation_logs'); + }); + } +}; diff --git a/openapi.yaml b/openapi.yaml index 23c00e3e4..92d6147cb 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3023,7 +3023,7 @@ paths: application/json: schema: properties: - name: + uuid: type: string description: 'The name of the project.' description: @@ -3037,9 +3037,7 @@ paths: application/json: schema: properties: - uuid: { type: string, example: og888os } - name: { type: string, example: 'Project Name' } - description: { type: string, example: 'Project Description' } + uuid: { type: string, example: og888os, description: 'The UUID of the project.' } type: object '401': $ref: '#/components/responses/401' @@ -3401,6 +3399,70 @@ paths: security: - bearerAuth: [] + post: + tags: + - Servers + summary: 'Create Server' + description: 'Create Server.' + operationId: fa44b42490379e428ba5b8747716a8d9 + requestBody: + description: 'Server created.' + required: true + content: + application/json: + schema: + properties: + name: + type: string + example: 'My Server' + description: 'The name of the server.' + description: + type: string + example: 'My Server Description' + description: 'The description of the server.' + ip: + type: string + example: 127.0.0.1 + description: 'The IP of the server.' + port: + type: integer + example: 22 + description: 'The port of the server.' + user: + type: string + example: root + description: 'The user of the server.' + private_key_uuid: + type: string + example: og888os + description: 'The UUID of the private key.' + is_build_server: + type: boolean + example: false + description: 'Is build server.' + instant_validate: + type: boolean + example: false + description: 'Instant validate.' + type: object + responses: + '201': + description: 'Server created.' + content: + application/json: + schema: + properties: + uuid: { type: string, example: og888os, description: 'The UUID of the server.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] '/servers/{uuid}': get: tags: @@ -3432,6 +3494,95 @@ paths: security: - bearerAuth: [] + delete: + tags: + - Servers + summary: Delete + description: 'Delete server by UUID.' + operationId: 0231fe0134f0306b21f006ce51b0a3dc + parameters: + - + name: uuid + in: path + description: 'UUID of the server.' + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'Server deleted.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Server deleted.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + patch: + tags: + - Servers + summary: 'Update Server' + description: 'Update Server.' + operationId: 41bbdaf79eb1938592494fc5494442a0 + requestBody: + description: 'Server updated.' + required: true + content: + application/json: + schema: + properties: + name: + type: string + description: 'The name of the server.' + description: + type: string + description: 'The description of the server.' + ip: + type: string + description: 'The IP of the server.' + port: + type: integer + description: 'The port of the server.' + user: + type: string + description: 'The user of the server.' + private_key_uuid: + type: string + description: 'The UUID of the private key.' + is_build_server: + type: boolean + description: 'Is build server.' + instant_validate: + type: boolean + description: 'Instant validate.' + type: object + responses: + '201': + description: 'Server updated.' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Server' + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] '/servers/{uuid}/resources': get: tags: @@ -3496,6 +3647,39 @@ paths: security: - bearerAuth: [] + '/servers/{uuid}/validate': + get: + tags: + - Servers + summary: Validate + description: 'Validate server by UUID.' + operationId: a543a12ef2cbc7a3dd22c3dbe6cbee89 + parameters: + - + name: uuid + in: path + description: 'Server UUID' + required: true + schema: + type: integer + responses: + '201': + description: 'Server validation started.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Validation started.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] /services: get: tags: @@ -4302,191 +4486,35 @@ components: $ref: '#/components/schemas/Environment' type: object Server: - description: 'Application model' + description: 'Server model' properties: id: type: integer - repository_project_id: - type: integer - nullable: true uuid: type: string name: type: string - fqdn: - type: string - config_hash: - type: string - git_repository: - type: string - git_branch: - type: string - git_commit_sha: - type: string - git_full_url: - type: string - nullable: true - docker_registry_image_name: - type: string - nullable: true - docker_registry_image_tag: - type: string - nullable: true - build_pack: - type: string - static_image: - type: string - install_command: - type: string - build_command: - type: string - start_command: - type: string - ports_exposes: - type: string - ports_mappings: - type: string - nullable: true - base_directory: - type: string - publish_directory: - type: string - health_check_path: - type: string - health_check_port: - type: string - nullable: true - health_check_host: - type: string - health_check_method: - type: string - health_check_return_code: - type: integer - health_check_scheme: - type: string - health_check_response_text: - type: string - nullable: true - health_check_interval: - type: integer - health_check_timeout: - type: integer - health_check_retries: - type: integer - health_check_start_period: - type: integer - limits_memory: - type: string - limits_memory_swap: - type: string - limits_memory_swappiness: - type: integer - limits_memory_reservation: - type: string - limits_cpus: - type: string - limits_cpuset: - type: string - nullable: true - limits_cpu_shares: - type: integer - status: - type: string - preview_url_template: - type: string - destination_type: - type: string - destination_id: - type: integer - source_type: - type: string - source_id: - type: integer - private_key_id: - type: integer - nullable: true - environment_id: - type: integer - created_at: - type: string - format: date-time - updated_at: - type: string - format: date-time description: type: string - nullable: true - dockerfile: + ip: type: string - nullable: true - health_check_enabled: - type: boolean - dockerfile_location: + user: type: string - custom_labels: - type: string - dockerfile_target_build: - type: string - nullable: true - manual_webhook_secret_github: - type: string - nullable: true - manual_webhook_secret_gitlab: - type: string - nullable: true - docker_compose_location: - type: string - docker_compose: - type: string - nullable: true - docker_compose_raw: - type: string - nullable: true - docker_compose_domains: - type: string - nullable: true - deleted_at: - type: string - format: date-time - nullable: true - docker_compose_custom_start_command: - type: string - nullable: true - docker_compose_custom_build_command: - type: string - nullable: true - swarm_replicas: + port: type: integer - swarm_placement_constraints: - type: string - nullable: true - manual_webhook_secret_bitbucket: - type: string - nullable: true - custom_docker_run_options: - type: string - nullable: true - post_deployment_command: - type: string - nullable: true - post_deployment_command_container: - type: string - nullable: true - pre_deployment_command: - type: string - nullable: true - pre_deployment_command_container: - type: string - nullable: true - watch_paths: - type: string - nullable: true - custom_healthcheck_found: + proxy: + type: object + high_disk_usage_notification_sent: type: boolean - manual_webhook_secret_gitea: + unreachable_notification_sent: + type: boolean + unreachable_count: + type: integer + validation_logs: type: string - nullable: true - redirect: + log_drain_notification_sent: + type: boolean + swarm_cluster: type: string type: object ServerSetting: diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index e10dec0d4..848758063 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -40,6 +40,12 @@ class="w-full mt-8 mb-4 font-bold box-without-bg bg-coollabs hover:bg-coollabs-1 Validate Server & Install Docker Engine + @if ($server->validation_logs) +

Previous Validation Logs

+
+ {!! $server->validation_logs !!} +
+ @endif @endif @if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id === 0)