From 6c33bd9c727616e9287d99c95601fa4135c2dc98 Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Tue, 9 Jul 2024 13:30:13 +0200 Subject: [PATCH] openapi services --- .../Controllers/Api/ServicesController.php | 404 ++++++++++++++++++ app/Models/Service.php | 24 ++ bootstrap/helpers/services.php | 7 + openapi.yaml | 281 ++++++++++++ 4 files changed, 716 insertions(+) diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index 36bd4e562..ca230b154 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -12,6 +12,7 @@ use App\Models\Server; use App\Models\Service; use Illuminate\Http\Request; +use OpenApi\Attributes as OA; class ServicesController extends Controller { @@ -33,6 +34,38 @@ private function removeSensitiveData($service) return serializeApiResponse($service); } + #[OA\Get( + summary: 'List', + description: 'List all services.', + path: '/services', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + responses: [ + new OA\Response( + response: 200, + description: 'Get all services', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'array', + items: new OA\Items(ref: '#/components/schemas/Service') + ) + ), + ] + ), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + ] + )] public function services(Request $request) { $teamId = getTeamIdFromToken(); @@ -51,6 +84,154 @@ public function services(Request $request) return response()->json($services->flatten()); } + #[OA\Post( + summary: 'Create', + description: 'Create a one-click service', + path: '/services', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + requestBody: new OA\RequestBody( + required: true, + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + required: ['server_uuid', 'project_uuid', 'environment_name', 'type'], + properties: [ + 'type' => ['type' => 'string', + 'enum' => [ + 'activepieces', + 'appsmith', + 'appwrite', + 'authentik', + 'babybuddy', + 'budge', + 'changedetection', + 'chatwoot', + 'classicpress-with-mariadb', + 'classicpress-with-mysql', + 'classicpress-without-database', + 'cloudflared', + 'code-server', + 'dashboard', + 'directus', + 'directus-with-postgresql', + 'docker-registry', + 'docuseal', + 'docuseal-with-postgres', + 'dokuwiki', + 'duplicati', + 'emby', + 'embystat', + 'fider', + 'filebrowser', + 'firefly', + 'formbricks', + 'ghost', + 'gitea', + 'gitea-with-mariadb', + 'gitea-with-mysql', + 'gitea-with-postgresql', + 'glance', + 'glances', + 'glitchtip', + 'grafana', + 'grafana-with-postgresql', + 'grocy', + 'heimdall', + 'homepage', + 'jellyfin', + 'kuzzle', + 'listmonk', + 'logto', + 'mediawiki', + 'meilisearch', + 'metabase', + 'metube', + 'minio', + 'moodle', + 'n8n', + 'n8n-with-postgresql', + 'next-image-transformation', + 'nextcloud', + 'nocodb', + 'odoo', + 'openblocks', + 'pairdrop', + 'penpot', + 'phpmyadmin', + 'pocketbase', + 'posthog', + 'reactive-resume', + 'rocketchat', + 'shlink', + 'slash', + 'snapdrop', + 'statusnook', + 'stirling-pdf', + 'supabase', + 'syncthing', + 'tolgee', + 'trigger', + 'trigger-with-external-database', + 'twenty', + 'umami', + 'unleash-with-postgresql', + 'unleash-without-database', + 'uptime-kuma', + 'vaultwarden', + 'vikunja', + 'weblate', + 'whoogle', + 'wordpress-with-mariadb', + 'wordpress-with-mysql', + 'wordpress-without-database', + ], + ], + 'server_uuid' => ['type' => 'string'], + 'project_uuid' => ['type' => 'string'], + 'environment_name' => ['type' => 'string'], + 'destination_uuid' => ['type' => 'string'], + 'name' => ['type' => 'string'], + 'description' => ['type' => 'string'], + 'project_uuid' => ['type' => 'string'], + 'environment_name' => ['type' => 'string'], + 'server_uuid' => ['type' => 'string'], + 'destination_uuid' => ['type' => 'string'], + 'instant_deploy' => ['type' => 'boolean'], + ], + ), + ), + ), + responses: [ + new OA\Response( + response: 201, + description: 'Create a service.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string'], + 'domains' => ['type' => 'array', 'items' => ['type' => 'string']], + ] + ) + ), + ] + ), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + ] + )] public function create_service(Request $request) { $allowedFields = ['type', 'name', 'description', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy']; @@ -182,6 +363,44 @@ public function create_service(Request $request) return response()->json(['message' => 'Invalid service type.'], 400); } + #[OA\Get( + summary: 'Get', + description: 'Get service by UUID.', + path: '/services/{uuid}', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + parameters: [ + new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Service UUID', schema: new OA\Schema(type: 'string')), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Get a service by Uuid.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + ref: '#/components/schemas/Service' + ) + ), + ] + ), + 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 service_by_uuid(Request $request) { $teamId = getTeamIdFromToken(); @@ -199,6 +418,47 @@ public function service_by_uuid(Request $request) return response()->json($this->removeSensitiveData($service)); } + #[OA\Delete( + summary: 'Delete', + description: 'Delete service by UUID.', + path: '/services/{uuid}', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + parameters: [ + new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Service UUID', schema: new OA\Schema(type: 'string')), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Delete a service by Uuid', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Service deletion request queued.'], + ], + ) + ), + ] + ), + 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_by_uuid(Request $request) { $teamId = getTeamIdFromToken(); @@ -219,6 +479,54 @@ public function delete_by_uuid(Request $request) ]); } + #[OA\Get( + summary: 'Start', + description: 'Start service. `Post` request is also accepted.', + path: '/services/{uuid}/start', + 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: 'Start service.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Service starting request queued.'], + ]) + ), + ]), + 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 action_deploy(Request $request) { $teamId = getTeamIdFromToken(); @@ -246,6 +554,54 @@ public function action_deploy(Request $request) ); } + #[OA\Get( + summary: 'Stop', + description: 'Stop service. `Post` request is also accepted.', + path: '/services/{uuid}/stop', + 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: 'Stop service.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Service stopping request queued.'], + ]) + ), + ]), + 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 action_stop(Request $request) { $teamId = getTeamIdFromToken(); @@ -273,6 +629,54 @@ public function action_stop(Request $request) ); } + #[OA\Get( + summary: 'Restart', + description: 'Restart service. `Post` request is also accepted.', + path: '/services/{uuid}/restart', + 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: 'Restart service.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'message' => ['type' => 'string', 'example' => 'Service restaring request queued.'], + ]) + ), + ]), + 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 action_restart(Request $request) { $teamId = getTeamIdFromToken(); diff --git a/app/Models/Service.php b/app/Models/Service.php index cc6a20749..92aca5141 100644 --- a/app/Models/Service.php +++ b/app/Models/Service.php @@ -6,8 +6,32 @@ use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Collection; +use OpenApi\Attributes as OA; use Symfony\Component\Yaml\Yaml; +#[OA\Schema( + description: 'Service model', + type: 'object', + properties: [ + 'id' => ['type' => 'integer'], + 'uuid' => ['type' => 'string'], + 'name' => ['type' => 'string'], + 'environment_id' => ['type' => 'integer'], + 'created_at' => ['type' => 'string'], + 'updated_at' => ['type' => 'string'], + 'server_id' => ['type' => 'integer'], + 'description' => ['type' => 'string'], + 'docker_compose_raw' => ['type' => 'string'], + 'docker_compose' => ['type' => 'string'], + 'destination_type' => ['type' => 'string'], + 'destination_id' => ['type' => 'integer'], + 'deleted_at' => ['type' => 'string'], + 'connect_to_docker_network' => ['type' => 'boolean'], + 'config_hash' => ['type' => 'string'], + 'service_type' => ['type' => 'string'], + 'is_container_label_escape_enabled' => ['type' => 'boolean'], + ], +)] class Service extends BaseModel { use HasFactory, SoftDeletes; diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php index ad2268a8e..9140ca8c8 100644 --- a/bootstrap/helpers/services.php +++ b/bootstrap/helpers/services.php @@ -199,3 +199,10 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource) return handleError($e); } } +function serviceKeys() +{ + $services = get_service_templates(); + $serviceKeys = $services->keys(); + + return $serviceKeys; +} diff --git a/openapi.yaml b/openapi.yaml index 1131b171e..13d72596c 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -2784,6 +2784,246 @@ paths: security: - bearerAuth: [] + /services: + get: + tags: + - Services + summary: List + description: 'List all services.' + operationId: 5d014ac25d33391b8f4c2316060ba452 + responses: + '200': + description: 'Get all services' + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Service' + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + security: + - + bearerAuth: [] + post: + tags: + - Services + summary: Create + description: 'Create a one-click service' + operationId: 3d6cbfb54d919b53ba3984a113e837d7 + requestBody: + required: true + content: + application/json: + schema: + required: + - server_uuid + - project_uuid + - environment_name + - type + properties: + type: + type: string + enum: [activepieces, appsmith, appwrite, authentik, babybuddy, budge, changedetection, chatwoot, classicpress-with-mariadb, classicpress-with-mysql, classicpress-without-database, cloudflared, code-server, dashboard, directus, directus-with-postgresql, docker-registry, docuseal, docuseal-with-postgres, dokuwiki, duplicati, emby, embystat, fider, filebrowser, firefly, formbricks, ghost, gitea, gitea-with-mariadb, gitea-with-mysql, gitea-with-postgresql, glance, glances, glitchtip, grafana, grafana-with-postgresql, grocy, heimdall, homepage, jellyfin, kuzzle, listmonk, logto, mediawiki, meilisearch, metabase, metube, minio, moodle, n8n, n8n-with-postgresql, next-image-transformation, nextcloud, nocodb, odoo, openblocks, pairdrop, penpot, phpmyadmin, pocketbase, posthog, reactive-resume, rocketchat, shlink, slash, snapdrop, statusnook, stirling-pdf, supabase, syncthing, tolgee, trigger, trigger-with-external-database, twenty, umami, unleash-with-postgresql, unleash-without-database, uptime-kuma, vaultwarden, vikunja, weblate, whoogle, wordpress-with-mariadb, wordpress-with-mysql, wordpress-without-database] + server_uuid: + type: string + project_uuid: + type: string + environment_name: + type: string + destination_uuid: + type: string + name: + type: string + description: + type: string + instant_deploy: + type: boolean + type: object + responses: + '201': + description: 'Create a service.' + content: + application/json: + schema: + properties: + uuid: { type: string } + domains: { type: array, items: { type: string } } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + security: + - + bearerAuth: [] + '/services/{uuid}': + get: + tags: + - Services + summary: Get + description: 'Get service by UUID.' + operationId: 895d39ee2cb3994285de57256c2d428d + parameters: + - + name: uuid + in: path + description: 'Service UUID' + required: true + schema: + type: string + responses: + '200': + description: 'Get a service by Uuid.' + content: + application/json: + schema: + $ref: '#/components/schemas/Service' + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + delete: + tags: + - Services + summary: Delete + description: 'Delete service by UUID.' + operationId: 6e1a61e4fddaa9d95bb9fc66dfaf0442 + parameters: + - + name: uuid + in: path + description: 'Service UUID' + required: true + schema: + type: string + responses: + '200': + description: 'Delete a service by Uuid' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Service deletion request queued.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + '/services/{uuid}/start': + get: + tags: + - Services + summary: Start + description: 'Start service. `Post` request is also accepted.' + operationId: d2ddd9c028d123fbdec830dc4b25b4cb + parameters: + - + name: uuid + in: path + description: 'UUID of the service.' + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'Start service.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Service starting request queued.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + '/services/{uuid}/stop': + get: + tags: + - Services + summary: Stop + description: 'Stop service. `Post` request is also accepted.' + operationId: 87399d34758ce16830740c68626614db + parameters: + - + name: uuid + in: path + description: 'UUID of the service.' + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'Stop service.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Service stopping request queued.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] + '/services/{uuid}/restart': + get: + tags: + - Services + summary: Restart + description: 'Restart service. `Post` request is also accepted.' + operationId: 836645faa615b75052759dae78639469 + parameters: + - + name: uuid + in: path + description: 'UUID of the service.' + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'Restart service.' + content: + application/json: + schema: + properties: + message: { type: string, example: 'Service restaring request queued.' } + type: object + '401': + $ref: '#/components/responses/401' + '400': + $ref: '#/components/responses/400' + '404': + $ref: '#/components/responses/404' + security: + - + bearerAuth: [] /teams: get: tags: @@ -3503,6 +3743,44 @@ components: updated_at: type: string type: object + Service: + description: 'Service model' + properties: + id: + type: integer + uuid: + type: string + name: + type: string + environment_id: + type: integer + created_at: + type: string + updated_at: + type: string + server_id: + type: integer + description: + type: string + docker_compose_raw: + type: string + docker_compose: + type: string + destination_type: + type: string + destination_id: + type: integer + deleted_at: + type: string + connect_to_docker_network: + type: boolean + config_hash: + type: string + service_type: + type: string + is_container_label_escape_enabled: + type: boolean + type: object Team: description: 'Team model' properties: @@ -3730,6 +4008,9 @@ tags: - name: Servers description: Servers + - + name: Services + description: Services - name: Teams description: Teams