user()->currentAccessToken(); if ($token->can('view:sensitive')) { return serializeApiResponse($deployment); } $deployment->makeHidden([ 'logs', ]); return serializeApiResponse($deployment); } #[OA\Get( summary: 'List', description: 'List currently running deployments', path: '/deployments', security: [ ['bearerAuth' => []], ], tags: ['Deployments'], responses: [ new OA\Response( response: 200, description: 'Get all currently running deployments.', content: [ new OA\MediaType( mediaType: 'application/json', schema: new OA\Schema( type: 'array', items: new OA\Items(ref: '#/components/schemas/ApplicationDeploymentQueue'), ) ), ]), new OA\Response( response: 401, ref: '#/components/responses/401', ), new OA\Response( response: 400, ref: '#/components/responses/400', ), ] )] public function deployments(Request $request) { $teamId = getTeamIdFromToken(); if (is_null($teamId)) { return invalidTokenResponse(); } $servers = Server::whereTeamId($teamId)->get(); $deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get()->sortBy('id'); $deployments_per_server = $deployments_per_server->map(function ($deployment) { return $this->removeSensitiveData($deployment); }); return response()->json($deployments_per_server); } #[OA\Get( summary: 'Get', description: 'Get deployment by UUID.', path: '/deployments/{uuid}', security: [ ['bearerAuth' => []], ], tags: ['Deployments'], parameters: [ new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment Uuid', schema: new OA\Schema(type: 'integer')), ], responses: [ new OA\Response( response: 200, description: 'Get deployment by UUID.', content: [ new OA\MediaType( mediaType: 'application/json', schema: new OA\Schema( ref: '#/components/schemas/ApplicationDeploymentQueue', ) ), ]), 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 deployment_by_uuid(Request $request) { $teamId = getTeamIdFromToken(); if (is_null($teamId)) { return invalidTokenResponse(); } $uuid = $request->route('uuid'); if (! $uuid) { return response()->json(['message' => 'UUID is required.'], 400); } $deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first(); if (! $deployment) { return response()->json(['message' => 'Deployment not found.'], 404); } return response()->json($this->removeSensitiveData($deployment)); } #[OA\Get( summary: 'Deploy', description: 'Deploy by tag or uuid. `Post` request also accepted.', path: '/deploy', security: [ ['bearerAuth' => []], ], tags: ['Deployments'], parameters: [ new OA\Parameter(name: 'tag', in: 'query', description: 'Tag name(s). Comma separated list is also accepted.', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'uuid', in: 'query', description: 'Resource UUID(s). Comma separated list is also accepted.', schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'force', in: 'query', description: 'Force rebuild (without cache)', schema: new OA\Schema(type: 'boolean')), ], responses: [ new OA\Response( response: 200, description: 'Get deployment(s) Uuid\'s', content: [ new OA\MediaType( mediaType: 'application/json', schema: new OA\Schema( type: 'object', properties: [ 'deployments' => new OA\Property( property: 'deployments', type: 'array', items: new OA\Items( type: 'object', properties: [ 'message' => ['type' => 'string'], 'resource_uuid' => ['type' => 'string'], 'deployment_uuid' => ['type' => 'string'], ] ), ), ], ) ), ]), new OA\Response( response: 401, ref: '#/components/responses/401', ), new OA\Response( response: 400, ref: '#/components/responses/400', ), ] )] public function deploy(Request $request) { $teamId = getTeamIdFromToken(); $uuids = $request->query->get('uuid'); $tags = $request->query->get('tag'); $force = $request->query->get('force') ?? false; if ($uuids && $tags) { return response()->json(['message' => 'You can only use uuid or tag, not both.'], 400); } if (is_null($teamId)) { return invalidTokenResponse(); } if ($tags) { return $this->by_tags($tags, $teamId, $force); } elseif ($uuids) { return $this->by_uuids($uuids, $teamId, $force); } return response()->json(['message' => 'You must provide uuid or tag.'], 400); } private function by_uuids(string $uuid, int $teamId, bool $force = false) { $uuids = explode(',', $uuid); $uuids = collect(array_filter($uuids)); if (count($uuids) === 0) { return response()->json(['message' => 'No UUIDs provided.'], 400); } $deployments = collect(); $payload = collect(); foreach ($uuids as $uuid) { $resource = getResourceByUuid($uuid, $teamId); if ($resource) { ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force); if ($deployment_uuid) { $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]); } else { $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]); } } } if ($deployments->count() > 0) { $payload->put('deployments', $deployments->toArray()); return response()->json(serializeApiResponse($payload->toArray())); } return response()->json(['message' => 'No resources found.'], 404); } public function by_tags(string $tags, int $team_id, bool $force = false) { $tags = explode(',', $tags); $tags = collect(array_filter($tags)); if (count($tags) === 0) { return response()->json(['message' => 'No TAGs provided.'], 400); } $message = collect([]); $deployments = collect(); $payload = collect(); foreach ($tags as $tag) { $found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first(); if (! $found_tag) { // $message->push("Tag {$tag} not found."); continue; } $applications = $found_tag->applications()->get(); $services = $found_tag->services()->get(); if ($applications->count() === 0 && $services->count() === 0) { $message->push("No resources found for tag {$tag}."); continue; } foreach ($applications as $resource) { ['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force); if ($deployment_uuid) { $deployments->push(['resource_uuid' => $resource->uuid, 'deployment_uuid' => $deployment_uuid->toString()]); } $message = $message->merge($return_message); } foreach ($services as $resource) { ['message' => $return_message] = $this->deploy_resource($resource, $force); $message = $message->merge($return_message); } } if ($message->count() > 0) { $payload->put('message', $message->toArray()); if ($deployments->count() > 0) { $payload->put('details', $deployments->toArray()); } return response()->json(serializeApiResponse($payload->toArray())); } return response()->json(['message' => 'No resources found with this tag.'], 404); } public function deploy_resource($resource, bool $force = false): array { $message = null; $deployment_uuid = null; if (gettype($resource) !== 'object') { return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid]; } switch ($resource?->getMorphClass()) { case 'App\Models\Application': $deployment_uuid = new Cuid2(7); queue_application_deployment( application: $resource, deployment_uuid: $deployment_uuid, force_rebuild: $force, ); $message = "Application {$resource->name} deployment queued."; break; case 'App\Models\Service': StartService::run($resource); $message = "Service {$resource->name} started. It could take a while, be patient."; break; default: // Database resource StartDatabase::dispatch($resource); $resource->update([ 'started_at' => now(), ]); $message = "Database {$resource->name} started."; break; } return ['message' => $message, 'deployment_uuid' => $deployment_uuid]; } }