From 22d99dec523abb74b52adc9c965f40e254eb4e7b Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 5 Sep 2024 14:53:10 +0200 Subject: [PATCH 1/7] Update coolify-helper.yml From 3ac52af6735c4a728a4d08f47a8d9a12bbd78f92 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Thu, 5 Sep 2024 14:53:49 +0200 Subject: [PATCH 2/7] Update coolify-helper.yml --- .github/workflows/coolify-helper.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coolify-helper.yml b/.github/workflows/coolify-helper.yml index a5100f4fb..37199919a 100644 --- a/.github/workflows/coolify-helper.yml +++ b/.github/workflows/coolify-helper.yml @@ -86,7 +86,7 @@ jobs: - name: Get Version id: version run: | - echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT + echo "VERSION=$(docker run --rm -v "$(pwd):/app" -w /app ghcr.io/jqlang/jq:latest '.coolify.helper.version' versions.json)"|xargs >> $GITHUB_OUTPUT - name: Create & publish manifest run: | docker buildx imagetools create --append ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64 --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }} --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest From 6c2717ad089b8d15a9c7332696fa712852163f17 Mon Sep 17 00:00:00 2001 From: Gabriel Galer Date: Thu, 5 Sep 2024 23:36:19 +0300 Subject: [PATCH 3/7] Update openapi spec --- openapi.yaml | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) diff --git a/openapi.yaml b/openapi.yaml index 40a6ea055..cbe41368a 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -3932,6 +3932,256 @@ paths: security: - bearerAuth: [] + '/services/{uuid}/envs': + get: + tags: + - Services + summary: 'List Envs' + description: 'List all envs by service UUID.' + operationId: list-envs-by-service-uuid + parameters: + - + name: uuid + in: path + description: 'UUID of the service.' + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'All environment variables by service UUID.' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/EnvironmentVariable' + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + post: + tags: + - Services + summary: 'Create Env' + description: 'Create env by service UUID.' + operationId: create-env-by-service-uuid + parameters: + - + name: uuid + in: path + description: 'UUID of the service.' + required: true + schema: + type: string + format: uuid + requestBody: + description: 'Env created.' + required: true + content: + application/json: + schema: + properties: + key: + type: string + description: 'The key of the environment variable.' + value: + type: string + description: 'The value of the environment variable.' + is_preview: + type: boolean + description: 'The flag to indicate if the environment variable is used in preview deployments.' + is_build_time: + type: boolean + description: 'The flag to indicate if the environment variable is used in build time.' + is_literal: + type: boolean + description: 'The flag to indicate if the environment variable is a literal, nothing espaced.' + is_multiline: + type: boolean + description: 'The flag to indicate if the environment variable is multiline.' + is_shown_once: + type: boolean + description: "The flag to indicate if the environment variable's value is shown on the UI." + type: object + responses: + '201': + description: 'Environment variable created.' + content: + application/json: + schema: + properties: + uuid: { type: string, example: nc0k04gk8g0cgsk440g0koko } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + patch: + tags: + - Services + summary: 'Update Env' + description: 'Update env by service UUID.' + operationId: update-env-by-service-uuid + parameters: + - + name: uuid + in: path + description: 'UUID of the service.' + required: true + schema: + type: string + format: uuid + requestBody: + description: 'Env updated.' + required: true + content: + application/json: + schema: + required: + - key + - value + properties: + key: + type: string + description: 'The key of the environment variable.' + value: + type: string + description: 'The value of the environment variable.' + is_preview: + type: boolean + description: 'The flag to indicate if the environment variable is used in preview deployments.' + is_build_time: + type: boolean + description: 'The flag to indicate if the environment variable is used in build time.' + is_literal: + type: boolean + description: 'The flag to indicate if the environment variable is a literal, nothing espaced.' + is_multiline: + type: boolean + description: 'The flag to indicate if the environment variable is multiline.' + is_shown_once: + type: boolean + description: "The flag to indicate if the environment variable's value is shown on the UI." + type: object + responses: + '201': + description: 'Environment variable updated.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Environment variable updated.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + '/services/{uuid}/envs/bulk': + patch: + tags: + - Services + summary: 'Update Envs (Bulk)' + description: 'Update multiple envs by service UUID.' + operationId: update-envs-by-service-uuid + parameters: + - + name: uuid + in: path + description: 'UUID of the service.' + required: true + schema: + type: string + format: uuid + requestBody: + description: 'Bulk envs updated.' + required: true + content: + application/json: + schema: + required: + - data + properties: + data: + type: array + items: { properties: { key: { type: string, description: 'The key of the environment variable.' }, value: { type: string, description: 'The value of the environment variable.' }, is_preview: { type: boolean, description: 'The flag to indicate if the environment variable is used in preview deployments.' }, is_build_time: { type: boolean, description: 'The flag to indicate if the environment variable is used in build time.' }, is_literal: { type: boolean, description: 'The flag to indicate if the environment variable is a literal, nothing espaced.' }, is_multiline: { type: boolean, description: 'The flag to indicate if the environment variable is multiline.' }, is_shown_once: { type: boolean, description: "The flag to indicate if the environment variable's value is shown on the UI." } }, type: object } + type: object + responses: + '201': + description: 'Environment variables updated.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Environment variables updated.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + '/services/{uuid}/envs/{env_uuid}': + delete: + tags: + - Services + summary: 'Delete Env' + description: 'Delete env by UUID.' + operationId: delete-env-by-service-uuid + parameters: + - + name: uuid + in: path + description: 'UUID of the service.' + required: true + schema: + type: string + format: uuid + - + name: env_uuid + in: path + description: 'UUID of the environment variable.' + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'Environment variable deleted.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Environment variable deleted.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] /teams: get: tags: From ab055e0692f75b29e68fd3d18e1ba8f06a52e8c4 Mon Sep 17 00:00:00 2001 From: Gabriel Galer Date: Thu, 5 Sep 2024 23:36:30 +0300 Subject: [PATCH 4/7] Update api.php routes --- routes/api.php | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/routes/api.php b/routes/api.php index c2c2f12ef..7e58e2cd8 100644 --- a/routes/api.php +++ b/routes/api.php @@ -69,27 +69,27 @@ Route::group([ Route::get('/resources', [ResourcesController::class, 'resources']); - Route::get('/applications', [ApplicationsController::class, 'applications']); - Route::post('/applications/public', [ApplicationsController::class, 'create_public_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/applications/private-github-app', [ApplicationsController::class, 'create_private_gh_app_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/applications/private-deploy-key', [ApplicationsController::class, 'create_private_deploy_key_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/applications/dockerfile', [ApplicationsController::class, 'create_dockerfile_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/applications/dockerimage', [ApplicationsController::class, 'create_dockerimage_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/applications/dockercompose', [ApplicationsController::class, 'create_dockercompose_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/services', [ServicesController::class, 'services']); + Route::post('/services/public', [ServicesController::class, 'create_public_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/services/private-github-app', [ServicesController::class, 'create_private_gh_app_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/services/private-deploy-key', [ServicesController::class, 'create_private_deploy_key_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/services/dockerfile', [ServicesController::class, 'create_dockerfile_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/services/dockerimage', [ServicesController::class, 'create_dockerimage_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/services/dockercompose', [ServicesController::class, 'create_dockercompose_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::get('/applications/{uuid}', [ApplicationsController::class, 'application_by_uuid']); - Route::patch('/applications/{uuid}', [ApplicationsController::class, 'update_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::delete('/applications/{uuid}', [ApplicationsController::class, 'delete_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/services/{uuid}', [ServicesController::class, 'application_by_uuid']); + Route::patch('/services/{uuid}', [ServicesController::class, 'update_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::delete('/services/{uuid}', [ServicesController::class, 'delete_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::get('/applications/{uuid}/envs', [ApplicationsController::class, 'envs']); - Route::post('/applications/{uuid}/envs', [ApplicationsController::class, 'create_env'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::patch('/applications/{uuid}/envs/bulk', [ApplicationsController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::patch('/applications/{uuid}/envs', [ApplicationsController::class, 'update_env_by_uuid']); - Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/services/{uuid}/envs', [ServicesController::class, 'envs']); + Route::post('/services/{uuid}/envs', [ServicesController::class, 'create_env'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::patch('/services/{uuid}/envs/bulk', [ServicesController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::patch('/services/{uuid}/envs', [ServicesController::class, 'update_env_by_uuid']); + Route::delete('/services/{uuid}/envs/{env_uuid}', [ServicesController::class, 'delete_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::match(['get', 'post'], '/applications/{uuid}/start', [ApplicationsController::class, 'action_deploy'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::match(['get', 'post'], '/applications/{uuid}/restart', [ApplicationsController::class, 'action_restart'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::match(['get', 'post'], '/applications/{uuid}/stop', [ApplicationsController::class, 'action_stop'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware([IgnoreReadOnlyApiToken::class]); Route::get('/databases', [DatabasesController::class, 'databases']); Route::post('/databases/postgresql', [DatabasesController::class, 'create_database_postgresql'])->middleware([IgnoreReadOnlyApiToken::class]); @@ -116,6 +116,12 @@ Route::group([ // Route::patch('/services/{uuid}', [ServicesController::class, 'update_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); Route::delete('/services/{uuid}', [ServicesController::class, 'delete_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/services/{uuid}/envs', [ServicesController::class, 'envs']); + Route::post('/services/{uuid}/envs', [ServicesController::class, 'create_env'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::patch('/services/{uuid}/envs/bulk', [ServicesController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::patch('/services/{uuid}/envs', [ServicesController::class, 'update_env_by_uuid']); + Route::delete('/services/{uuid}/envs/{env_uuid}', [ServicesController::class, 'delete_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware([IgnoreReadOnlyApiToken::class]); Route::match(['get', 'post'], '/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware([IgnoreReadOnlyApiToken::class]); Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware([IgnoreReadOnlyApiToken::class]); From 2e5ed5969d9d0c9f8ad1dab1a07bd350d0521cd0 Mon Sep 17 00:00:00 2001 From: Gabriel Galer Date: Thu, 5 Sep 2024 23:54:20 +0300 Subject: [PATCH 5/7] Update services controller to include new service envs commands --- .../Controllers/Api/ServicesController.php | 510 ++++++++++++++++++ 1 file changed, 510 insertions(+) diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index c39778698..f0ce354b8 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -483,6 +483,516 @@ class ServicesController extends Controller ]); } + + #[OA\Get( + summary: 'List Envs', + description: 'List all envs by service UUID.', + path: '/services/{uuid}/envs', + operationId: 'list-envs-by-service-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the service.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'All environment variables by service UUID.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'array', + items: new OA\Items(ref: '#/components/schemas/EnvironmentVariable') + ) + ), + ]), + 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 envs(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); + if (!$service) { + return response()->json(['message' => 'Service not found.'], 404); + } + $envs = $service->environment_variables->sortBy('id'); + + $envs = $envs->map(function ($env) { + $env->makeHidden([ + 'application_id', + 'standalone_clickhouse_id', + 'standalone_dragonfly_id', + 'standalone_keydb_id', + 'standalone_mariadb_id', + 'standalone_mongodb_id', + 'standalone_mysql_id', + 'standalone_postgresql_id', + 'standalone_redis_id', + ]); + $env = $this->removeSensitiveData($env); + + return $env; + }); + } + + #[OA\Patch( + summary: 'Update Env', + description: 'Update env by service UUID.', + path: '/services/{uuid}/envs', + operationId: 'update-env-by-service-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the service.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + ], + requestBody: new OA\RequestBody( + description: 'Env updated.', + required: true, + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + required: ['key', 'value'], + properties: [ + 'key' => ['type' => 'string', 'description' => 'The key of the environment variable.'], + 'value' => ['type' => 'string', 'description' => 'The value of the environment variable.'], + 'is_preview' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in preview deployments.'], + 'is_build_time' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in build time.'], + 'is_literal' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is a literal, nothing espaced.'], + 'is_multiline' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is multiline.'], + 'is_shown_once' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable\'s value is shown on the UI.'], + ], + ), + ), + ], + ), + responses: [ + new OA\Response( + response: 201, + description: 'Environment variable updated.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Environment variable updated.'], + ] + ) + ), + ]), + 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_env_by_uuid(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); + if (!$service) { + return response()->json(['message' => 'Service not found.'], 404); + } + + $validator = customApiValidator($request->all(), [ + 'key' => 'string|required', + 'value' => 'string|nullable', + 'is_build_time' => 'boolean', + 'is_literal' => 'boolean', + 'is_multiline' => 'boolean', + 'is_shown_once' => 'boolean', + ]); + + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + + $env = $service->environment_variables()->where('key', $request->key)->first(); + if (!$env) { + return response()->json(['message' => 'Environment variable not found.'], 404); + } + + $env->fill($request->all()); + $env->save(); + + return response()->json($this->removeSensitiveData($env))->setStatusCode(201); + } + + #[OA\Patch( + summary: 'Update Envs (Bulk)', + description: 'Update multiple envs by service UUID.', + path: '/services/{uuid}/envs/bulk', + operationId: 'update-envs-by-service-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the service.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + ], + requestBody: new OA\RequestBody( + description: 'Bulk envs updated.', + required: true, + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + required: ['data'], + properties: [ + 'data' => [ + 'type' => 'array', + 'items' => new OA\Schema( + type: 'object', + properties: [ + 'key' => ['type' => 'string', 'description' => 'The key of the environment variable.'], + 'value' => ['type' => 'string', 'description' => 'The value of the environment variable.'], + 'is_preview' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in preview deployments.'], + 'is_build_time' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in build time.'], + 'is_literal' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is a literal, nothing espaced.'], + 'is_multiline' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is multiline.'], + 'is_shown_once' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable\'s value is shown on the UI.'], + ], + ), + ], + ], + ), + ), + ], + ), + responses: [ + new OA\Response( + response: 201, + description: 'Environment variables updated.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Environment variables updated.'], + ] + ) + ), + ]), + 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_bulk_envs(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); + if (!$service) { + return response()->json(['message' => 'Service not found.'], 404); + } + + $bulk_data = $request->get('data'); + if (!$bulk_data) { + return response()->json(['message' => 'Bulk data is required.'], 400); + } + + $updatedEnvs = collect(); + foreach ($bulk_data as $item) { + $validator = customApiValidator($item, [ + 'key' => 'string|required', + 'value' => 'string|nullable', + 'is_build_time' => 'boolean', + 'is_literal' => 'boolean', + 'is_multiline' => 'boolean', + 'is_shown_once' => 'boolean', + ]); + + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + + $env = $service->environment_variables()->updateOrCreate( + ['key' => $item['key']], + $item + ); + + $updatedEnvs->push($this->removeSensitiveData($env)); + } + + return response()->json($updatedEnvs)->setStatusCode(201); + } + + #[OA\Post( + summary: 'Create Env', + description: 'Create env by service UUID.', + path: '/services/{uuid}/envs', + operationId: 'create-env-by-service-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the service.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + ], + requestBody: new OA\RequestBody( + required: true, + description: 'Env created.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'key' => ['type' => 'string', 'description' => 'The key of the environment variable.'], + 'value' => ['type' => 'string', 'description' => 'The value of the environment variable.'], + 'is_preview' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in preview deployments.'], + 'is_build_time' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is used in build time.'], + 'is_literal' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is a literal, nothing espaced.'], + 'is_multiline' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable is multiline.'], + 'is_shown_once' => ['type' => 'boolean', 'description' => 'The flag to indicate if the environment variable\'s value is shown on the UI.'], + ], + ), + ), + ), + responses: [ + new OA\Response( + response: 201, + description: 'Environment variable created.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string', 'example' => 'nc0k04gk8g0cgsk440g0koko'], + ] + ) + ), + ]), + 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_env(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); + if (!$service) { + return response()->json(['message' => 'Service not found.'], 404); + } + + $validator = customApiValidator($request->all(), [ + 'key' => 'string|required', + 'value' => 'string|nullable', + 'is_build_time' => 'boolean', + 'is_literal' => 'boolean', + 'is_multiline' => 'boolean', + 'is_shown_once' => 'boolean', + ]); + + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + + $existingEnv = $service->environment_variables()->where('key', $request->key)->first(); + if ($existingEnv) { + return response()->json([ + 'message' => 'Environment variable already exists. Use PATCH request to update it.', + ], 409); + } + + $env = $service->environment_variables()->create($request->all()); + + return response()->json($this->removeSensitiveData($env))->setStatusCode(201); + } + + #[OA\Delete( + summary: 'Delete Env', + description: 'Delete env by UUID.', + path: '/services/{uuid}/envs/{env_uuid}', + operationId: 'delete-env-by-service-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the service.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + new OA\Parameter( + name: 'env_uuid', + in: 'path', + description: 'UUID of the environment variable.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Environment variable deleted.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Environment variable 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_env_by_uuid(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($request->uuid)->first(); + if (!$service) { + return response()->json(['message' => 'Service not found.'], 404); + } + + $env = EnvironmentVariable::where('uuid', $request->env_uuid) + ->where('service_id', $service->id) + ->first(); + + if (!$env) { + return response()->json(['message' => 'Environment variable not found.'], 404); + } + + $env->forceDelete(); + + return response()->json(['message' => 'Environment variable deleted.']); + } + #[OA\Get( summary: 'Start', description: 'Start service. `Post` request is also accepted.', From 4e1663e9dbb8d0e20948853931f0aaf9597c642b Mon Sep 17 00:00:00 2001 From: Gabriel Galer Date: Fri, 6 Sep 2024 00:10:40 +0300 Subject: [PATCH 6/7] Fix bulk rename problems in `api.php` --- routes/api.php | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/routes/api.php b/routes/api.php index 7e58e2cd8..b241b597d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -69,27 +69,27 @@ Route::group([ Route::get('/resources', [ResourcesController::class, 'resources']); - Route::get('/services', [ServicesController::class, 'services']); - Route::post('/services/public', [ServicesController::class, 'create_public_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/services/private-github-app', [ServicesController::class, 'create_private_gh_app_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/services/private-deploy-key', [ServicesController::class, 'create_private_deploy_key_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/services/dockerfile', [ServicesController::class, 'create_dockerfile_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/services/dockerimage', [ServicesController::class, 'create_dockerimage_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::post('/services/dockercompose', [ServicesController::class, 'create_dockercompose_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/applications', [ApplicationsController::class, 'applications']); + Route::post('/applications/public', [ApplicationsController::class, 'create_public_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/applications/private-github-app', [ApplicationsController::class, 'create_private_gh_app_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/applications/private-deploy-key', [ApplicationsController::class, 'create_private_deploy_key_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/applications/dockerfile', [ApplicationsController::class, 'create_dockerfile_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/applications/dockerimage', [ApplicationsController::class, 'create_dockerimage_application'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::post('/applications/dockercompose', [ApplicationsController::class, 'create_dockercompose_application'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::get('/services/{uuid}', [ServicesController::class, 'application_by_uuid']); - Route::patch('/services/{uuid}', [ServicesController::class, 'update_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::delete('/services/{uuid}', [ServicesController::class, 'delete_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/applications/{uuid}', [ApplicationsController::class, 'application_by_uuid']); + Route::patch('/applications/{uuid}', [ApplicationsController::class, 'update_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::delete('/applications/{uuid}', [ApplicationsController::class, 'delete_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::get('/services/{uuid}/envs', [ServicesController::class, 'envs']); - Route::post('/services/{uuid}/envs', [ServicesController::class, 'create_env'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::patch('/services/{uuid}/envs/bulk', [ServicesController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::patch('/services/{uuid}/envs', [ServicesController::class, 'update_env_by_uuid']); - Route::delete('/services/{uuid}/envs/{env_uuid}', [ServicesController::class, 'delete_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/applications/{uuid}/envs', [ApplicationsController::class, 'envs']); + Route::post('/applications/{uuid}/envs', [ApplicationsController::class, 'create_env'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::patch('/applications/{uuid}/envs/bulk', [ApplicationsController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::patch('/applications/{uuid}/envs', [ApplicationsController::class, 'update_env_by_uuid']); + Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::match(['get', 'post'], '/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::match(['get', 'post'], '/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/applications/{uuid}/start', [ApplicationsController::class, 'action_deploy'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/applications/{uuid}/restart', [ApplicationsController::class, 'action_restart'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/applications/{uuid}/stop', [ApplicationsController::class, 'action_stop'])->middleware([IgnoreReadOnlyApiToken::class]); Route::get('/databases', [DatabasesController::class, 'databases']); Route::post('/databases/postgresql', [DatabasesController::class, 'create_database_postgresql'])->middleware([IgnoreReadOnlyApiToken::class]); From 69794f4ca8acfc373b3e3b397e6ddaac39fa0eac Mon Sep 17 00:00:00 2001 From: Gabriel Galer Date: Fri, 6 Sep 2024 08:31:32 +0300 Subject: [PATCH 7/7] Update `api.php` routes to include middleware for updating environment variables by UUID --- routes/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes/api.php b/routes/api.php index b241b597d..a5039e9de 100644 --- a/routes/api.php +++ b/routes/api.php @@ -119,7 +119,7 @@ Route::group([ Route::get('/services/{uuid}/envs', [ServicesController::class, 'envs']); Route::post('/services/{uuid}/envs', [ServicesController::class, 'create_env'])->middleware([IgnoreReadOnlyApiToken::class]); Route::patch('/services/{uuid}/envs/bulk', [ServicesController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]); - Route::patch('/services/{uuid}/envs', [ServicesController::class, 'update_env_by_uuid']); + Route::patch('/services/{uuid}/envs', [ServicesController::class, 'update_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); Route::delete('/services/{uuid}/envs/{env_uuid}', [ServicesController::class, 'delete_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); Route::match(['get', 'post'], '/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware([IgnoreReadOnlyApiToken::class]);