From 9e8d9e6a9ea55ca24991862095d13eda3c11adf5 Mon Sep 17 00:00:00 2001 From: SierraJC <7351311+SierraJC@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:52:47 +1100 Subject: [PATCH 1/4] fix: invalid API response on missing project API was returning 500 instead of 404 due to attempting to load environments for a non-existent project --- app/Http/Controllers/Api/ProjectController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index 1d89c82ed..e7b0af785 100644 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -90,11 +90,12 @@ class ProjectController extends Controller 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), ); From e7ffeda8e0e14997b7ac43ec9f5d4b1a3d923a93 Mon Sep 17 00:00:00 2001 From: SierraJC <7351311+SierraJC@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:54:36 +1100 Subject: [PATCH 2/4] fix: applications API response code + schema --- .../Api/ApplicationsController.php | 78 ++++++++++++++++--- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index f02c4255d..6094276e0 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -185,8 +185,17 @@ class ApplicationsController extends Controller ), 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, @@ -291,8 +300,17 @@ class ApplicationsController extends Controller ), 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, @@ -397,8 +415,17 @@ class ApplicationsController extends Controller ), 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, @@ -487,8 +514,17 @@ class ApplicationsController extends Controller ), 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, @@ -574,8 +610,17 @@ class ApplicationsController extends Controller ), 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, @@ -627,8 +672,17 @@ class ApplicationsController extends Controller ), 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, @@ -815,7 +869,7 @@ class ApplicationsController extends Controller 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)); @@ -914,7 +968,7 @@ class ApplicationsController extends Controller 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)); @@ -1010,7 +1064,7 @@ class ApplicationsController extends Controller 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); @@ -1095,7 +1149,7 @@ class ApplicationsController extends Controller 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); @@ -1159,7 +1213,7 @@ class ApplicationsController extends Controller 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']; @@ -1241,7 +1295,7 @@ class ApplicationsController extends Controller return response()->json(serializeApiResponse([ 'uuid' => data_get($service, 'uuid'), 'domains' => data_get($service, 'domains'), - ])); + ]))->setStatusCode(201); } return response()->json(['message' => 'Invalid type.'], 400); From 00c93aa8b0786ad30c777e3dcc8d6032bb2211cd Mon Sep 17 00:00:00 2001 From: SierraJC <7351311+SierraJC@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:58:11 +1100 Subject: [PATCH 3/4] fix: applications API writing to unavailable models when $useBuildServer is set, $application->settings model does not yet exist. This change ensures the models exist before accessing them. --- .../Api/ApplicationsController.php | 27 +++--- openapi.json | 96 ++++++++++++++++--- openapi.yaml | 48 ++++++++-- 3 files changed, 140 insertions(+), 31 deletions(-) diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 6094276e0..782a23a89 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -938,12 +938,12 @@ class ApplicationsController extends Controller $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(); @@ -1034,12 +1034,12 @@ class ApplicationsController extends Controller $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(); @@ -1120,15 +1120,16 @@ class ApplicationsController extends Controller $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(); @@ -1184,15 +1185,15 @@ class ApplicationsController extends Controller $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(); diff --git a/openapi.json b/openapi.json index 5d35331ec..95fd95730 100644 --- a/openapi.json +++ b/openapi.json @@ -342,8 +342,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" @@ -659,8 +671,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" @@ -976,8 +1000,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" @@ -1222,8 +1258,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" @@ -1451,8 +1499,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" @@ -1533,8 +1593,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" diff --git a/openapi.yaml b/openapi.yaml index 20bf34873..a027a6296 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -246,8 +246,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': @@ -475,8 +481,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': @@ -704,8 +716,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': @@ -880,8 +898,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': @@ -1047,8 +1071,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': @@ -1105,8 +1135,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': From 7cee6519f6a88e70f80658e80af5c5979306e7fd Mon Sep 17 00:00:00 2001 From: SierraJC <7351311+SierraJC@users.noreply.github.com> Date: Sat, 14 Dec 2024 12:59:43 +1100 Subject: [PATCH 4/4] chore: regenerate API spec, removing notification fields --- openapi.json | 160 --------------------------------------------------- openapi.yaml | 120 -------------------------------------- 2 files changed, 280 deletions(-) diff --git a/openapi.json b/openapi.json index 95fd95730..659adc62f 100644 --- a/openapi.json +++ b/openapi.json @@ -7756,174 +7756,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 a027a6296..b20fc0fdc 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -5175,132 +5175,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