diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 4b0cfc6ab..a6609eb47 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -3919,4 +3919,191 @@ private function validateDataApplications(Request $request, Server $server) } } } + + #[OA\Get( + summary: 'List Storages', + description: 'List all persistent storages and file storages by application UUID.', + path: '/applications/{uuid}/storages', + operationId: 'list-storages-by-application-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Applications'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the application.', + required: true, + schema: new OA\Schema( + type: 'string', + ) + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'All storages by application UUID.', + ), + 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 storages(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'message' => 'Application not found', + ], 404); + } + + $this->authorize('view', $application); + + $persistentStorages = $application->persistentStorages->sortBy('id')->values(); + $fileStorages = $application->fileStorages->sortBy('id')->values(); + + return response()->json([ + 'persistent_storages' => $persistentStorages, + 'file_storages' => $fileStorages, + ]); + } + + #[OA\Patch( + summary: 'Update Storage', + description: 'Update a persistent storage or file storage by application UUID.', + path: '/applications/{uuid}/storages', + operationId: 'update-storage-by-application-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Applications'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the application.', + required: true, + schema: new OA\Schema( + type: 'string', + ) + ), + ], + requestBody: new OA\RequestBody( + description: 'Storage updated.', + required: true, + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + required: ['id', 'type'], + properties: [ + 'id' => ['type' => 'integer', 'description' => 'The ID of the storage.'], + 'type' => ['type' => 'string', 'enum' => ['persistent', 'file'], 'description' => 'The type of storage: persistent or file.'], + 'is_preview_suffix_enabled' => ['type' => 'boolean', 'description' => 'Whether to add -pr-N suffix for preview deployments.'], + ], + ), + ), + ], + ), + responses: [ + new OA\Response( + response: 200, + description: 'Storage 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_storage(Request $request) + { + $allowedFields = ['id', 'type', 'is_preview_suffix_enabled']; + $teamId = getTeamIdFromToken(); + + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + $return = validateIncomingRequest($request); + if ($return instanceof \Illuminate\Http\JsonResponse) { + return $return; + } + + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + + if (! $application) { + return response()->json([ + 'message' => 'Application not found', + ], 404); + } + + $this->authorize('update', $application); + + $validator = customApiValidator($request->all(), [ + 'id' => 'required|integer', + 'type' => 'required|string|in:persistent,file', + 'is_preview_suffix_enabled' => 'boolean', + ]); + + $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->type === 'persistent') { + $storage = $application->persistentStorages->where('id', $request->id)->first(); + } else { + $storage = $application->fileStorages->where('id', $request->id)->first(); + } + + if (! $storage) { + return response()->json([ + 'message' => 'Storage not found.', + ], 404); + } + + if ($request->has('is_preview_suffix_enabled')) { + $storage->is_preview_suffix_enabled = $request->is_preview_suffix_enabled; + } + + $storage->save(); + + return response()->json($storage); + } } diff --git a/openapi.json b/openapi.json index f5d9813b3..13f745e11 100644 --- a/openapi.json +++ b/openapi.json @@ -3442,6 +3442,117 @@ ] } }, + "\/applications\/{uuid}\/storages": { + "get": { + "tags": [ + "Applications" + ], + "summary": "List Storages", + "description": "List all persistent storages and file storages by application UUID.", + "operationId": "list-storages-by-application-uuid", + "parameters": [ + { + "name": "uuid", + "in": "path", + "description": "UUID of the application.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "All storages by application UUID." + }, + "401": { + "$ref": "#\/components\/responses\/401" + }, + "400": { + "$ref": "#\/components\/responses\/400" + }, + "404": { + "$ref": "#\/components\/responses\/404" + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + }, + "patch": { + "tags": [ + "Applications" + ], + "summary": "Update Storage", + "description": "Update a persistent storage or file storage by application UUID.", + "operationId": "update-storage-by-application-uuid", + "parameters": [ + { + "name": "uuid", + "in": "path", + "description": "UUID of the application.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Storage updated.", + "required": true, + "content": { + "application\/json": { + "schema": { + "required": [ + "id", + "type" + ], + "properties": { + "id": { + "type": "integer", + "description": "The ID of the storage." + }, + "type": { + "type": "string", + "enum": [ + "persistent", + "file" + ], + "description": "The type of storage: persistent or file." + }, + "is_preview_suffix_enabled": { + "type": "boolean", + "description": "Whether to add -pr-N suffix for preview deployments." + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "Storage updated." + }, + "401": { + "$ref": "#\/components\/responses\/401" + }, + "400": { + "$ref": "#\/components\/responses\/400" + }, + "404": { + "$ref": "#\/components\/responses\/404" + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, "\/cloud-tokens": { "get": { "tags": [ diff --git a/openapi.yaml b/openapi.yaml index 81753544f..7ee406c99 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2170,6 +2170,80 @@ paths: security: - bearerAuth: [] + '/applications/{uuid}/storages': + get: + tags: + - Applications + summary: 'List Storages' + description: 'List all persistent storages and file storages by application UUID.' + operationId: list-storages-by-application-uuid + parameters: + - + name: uuid + in: path + description: 'UUID of the application.' + required: true + schema: + type: string + responses: + '200': + description: 'All storages by application UUID.' + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + patch: + tags: + - Applications + summary: 'Update Storage' + description: 'Update a persistent storage or file storage by application UUID.' + operationId: update-storage-by-application-uuid + parameters: + - + name: uuid + in: path + description: 'UUID of the application.' + required: true + schema: + type: string + requestBody: + description: 'Storage updated.' + required: true + content: + application/json: + schema: + required: + - id + - type + properties: + id: + type: integer + description: 'The ID of the storage.' + type: + type: string + enum: [persistent, file] + description: 'The type of storage: persistent or file.' + is_preview_suffix_enabled: + type: boolean + description: 'Whether to add -pr-N suffix for preview deployments.' + type: object + responses: + '200': + description: 'Storage updated.' + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] /cloud-tokens: get: tags: diff --git a/routes/api.php b/routes/api.php index 8b28177f3..b02682a5b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -120,6 +120,8 @@ Route::patch('/applications/{uuid}/envs', [ApplicationsController::class, 'update_env_by_uuid'])->middleware(['api.ability:write']); Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid'])->middleware(['api.ability:write']); Route::get('/applications/{uuid}/logs', [ApplicationsController::class, 'logs_by_uuid'])->middleware(['api.ability:read']); + Route::get('/applications/{uuid}/storages', [ApplicationsController::class, 'storages'])->middleware(['api.ability:read']); + Route::patch('/applications/{uuid}/storages', [ApplicationsController::class, 'update_storage'])->middleware(['api.ability:write']); Route::match(['get', 'post'], '/applications/{uuid}/start', [ApplicationsController::class, 'action_deploy'])->middleware(['api.ability:deploy']); Route::match(['get', 'post'], '/applications/{uuid}/restart', [ApplicationsController::class, 'action_restart'])->middleware(['api.ability:deploy']);