2024-02-02 10:50:28 +00:00
< ? php
namespace App\Http\Controllers\Api ;
2024-07-02 11:39:44 +00:00
use App\Actions\Database\StartDatabase ;
2024-02-02 10:50:28 +00:00
use App\Actions\Service\StartService ;
use App\Http\Controllers\Controller ;
2024-03-07 11:27:23 +00:00
use App\Models\ApplicationDeploymentQueue ;
use App\Models\Server ;
2024-02-02 10:50:28 +00:00
use App\Models\Tag ;
use Illuminate\Http\Request ;
2024-07-09 08:45:10 +00:00
use OpenApi\Attributes as OA ;
2024-02-02 10:50:28 +00:00
use Visus\Cuid2\Cuid2 ;
2024-07-01 14:26:50 +00:00
class DeployController extends Controller
2024-02-02 10:50:28 +00:00
{
2024-07-02 10:15:58 +00:00
private function removeSensitiveData ( $deployment )
{
2024-12-09 10:10:35 +00:00
if ( request () -> attributes -> get ( 'can_read_sensitive' , false ) === false ) {
$deployment -> makeHidden ([
'logs' ,
]);
2024-07-02 10:15:58 +00:00
}
return serializeApiResponse ( $deployment );
}
2024-07-09 08:45:10 +00:00
#[OA\Get(
summary : 'List' ,
description : 'List currently running deployments' ,
path : '/deployments' ,
2024-09-04 08:09:10 +00:00
operationId : 'list-deployments' ,
2024-07-09 08:45:10 +00:00
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' ,
),
]
)]
2024-03-11 08:42:02 +00:00
public function deployments ( Request $request )
{
2024-07-01 14:26:50 +00:00
$teamId = getTeamIdFromToken ();
2024-03-07 11:27:23 +00:00
if ( is_null ( $teamId )) {
2024-07-01 14:26:50 +00:00
return invalidTokenResponse ();
2024-03-07 11:27:23 +00:00
}
$servers = Server :: whereTeamId ( $teamId ) -> get ();
2024-07-09 08:45:10 +00:00
$deployments_per_server = ApplicationDeploymentQueue :: whereIn ( 'status' , [ 'in_progress' , 'queued' ]) -> whereIn ( 'server_id' , $servers -> pluck ( 'id' )) -> get () -> sortBy ( 'id' );
2024-07-04 11:45:06 +00:00
$deployments_per_server = $deployments_per_server -> map ( function ( $deployment ) {
return $this -> removeSensitiveData ( $deployment );
});
2024-06-10 20:43:34 +00:00
2024-07-04 11:45:06 +00:00
return response () -> json ( $deployments_per_server );
2024-06-21 14:46:13 +00:00
}
2024-07-09 08:45:10 +00:00
#[OA\Get(
summary : 'Get' ,
description : 'Get deployment by UUID.' ,
path : '/deployments/{uuid}' ,
2024-09-04 08:09:10 +00:00
operationId : 'get-deployment-by-uuid' ,
2024-07-09 08:45:10 +00:00
security : [
[ 'bearerAuth' => []],
],
tags : [ 'Deployments' ],
parameters : [
2024-09-09 16:38:40 +00:00
new OA\Parameter ( name : 'uuid' , in : 'path' , required : true , description : 'Deployment UUID' , schema : new OA\Schema ( type : 'string' )),
2024-07-09 08:45:10 +00:00
],
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' ,
),
]
)]
2024-06-21 14:46:13 +00:00
public function deployment_by_uuid ( Request $request )
{
2024-07-01 14:26:50 +00:00
$teamId = getTeamIdFromToken ();
2024-06-21 14:46:13 +00:00
if ( is_null ( $teamId )) {
2024-07-01 14:26:50 +00:00
return invalidTokenResponse ();
2024-06-21 14:46:13 +00:00
}
$uuid = $request -> route ( 'uuid' );
if ( ! $uuid ) {
2024-07-03 11:13:38 +00:00
return response () -> json ([ 'message' => 'UUID is required.' ], 400 );
2024-06-21 14:46:13 +00:00
}
2024-07-09 08:45:10 +00:00
$deployment = ApplicationDeploymentQueue :: where ( 'deployment_uuid' , $uuid ) -> first ();
2024-06-21 14:46:13 +00:00
if ( ! $deployment ) {
2024-07-03 11:13:38 +00:00
return response () -> json ([ 'message' => 'Deployment not found.' ], 404 );
2024-06-21 14:46:13 +00:00
}
2024-07-03 11:13:38 +00:00
return response () -> json ( $this -> removeSensitiveData ( $deployment ));
2024-03-07 11:27:23 +00:00
}
2024-06-10 20:43:34 +00:00
2024-07-09 08:45:10 +00:00
#[OA\Get(
summary : 'Deploy' ,
description : 'Deploy by tag or uuid. `Post` request also accepted.' ,
path : '/deploy' ,
2024-09-04 08:09:10 +00:00
operationId : 'deploy-by-tag-or-uuid' ,
2024-07-09 08:45:10 +00:00
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' )),
2025-03-31 15:22:40 +00:00
new OA\Parameter ( name : 'pr' , in : 'query' , description : 'Pull Request Id for deploying specific PR builds. Cannot be used with tag parameter.' , schema : new OA\Schema ( type : 'integer' )),
2024-07-09 08:45:10 +00:00
],
responses : [
new OA\Response (
response : 200 ,
2024-09-09 16:38:40 +00:00
description : 'Get deployment(s) UUID\'s' ,
2024-07-09 08:45:10 +00:00
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' ,
),
]
)]
2024-02-02 10:50:28 +00:00
public function deploy ( Request $request )
{
2024-07-01 14:26:50 +00:00
$teamId = getTeamIdFromToken ();
2024-02-02 10:50:28 +00:00
$uuids = $request -> query -> get ( 'uuid' );
$tags = $request -> query -> get ( 'tag' );
$force = $request -> query -> get ( 'force' ) ? ? false ;
2025-03-27 16:50:21 +00:00
$pr = $request -> query -> get ( 'pr' ) ? ( int ) $request -> query -> get ( 'pr' ) : 0 ;
2025-03-27 16:16:29 +00:00
2024-02-02 10:50:28 +00:00
if ( $uuids && $tags ) {
2024-07-09 11:59:54 +00:00
return response () -> json ([ 'message' => 'You can only use uuid or tag, not both.' ], 400 );
2024-02-02 10:50:28 +00:00
}
if ( is_null ( $teamId )) {
2024-07-01 14:26:50 +00:00
return invalidTokenResponse ();
2024-02-02 10:50:28 +00:00
}
2025-03-27 16:16:29 +00:00
if ( $tags && $pr ) {
return response () -> json ([ 'message' => 'You can only use tag or pr, not both.' ], 400 );
}
2024-02-02 10:50:28 +00:00
if ( $tags ) {
return $this -> by_tags ( $tags , $teamId , $force );
2024-06-10 20:43:34 +00:00
} elseif ( $uuids ) {
2025-03-27 16:16:29 +00:00
return $this -> by_uuids ( $uuids , $teamId , $force , $pr );
2024-02-02 10:50:28 +00:00
}
2024-06-10 20:43:34 +00:00
2024-07-09 11:59:54 +00:00
return response () -> json ([ 'message' => 'You must provide uuid or tag.' ], 400 );
2024-02-02 10:50:28 +00:00
}
2024-06-10 20:43:34 +00:00
2025-03-27 16:16:29 +00:00
private function by_uuids ( string $uuid , int $teamId , bool $force = false , int $pr = 0 )
2024-02-02 10:50:28 +00:00
{
$uuids = explode ( ',' , $uuid );
$uuids = collect ( array_filter ( $uuids ));
if ( count ( $uuids ) === 0 ) {
2024-07-09 11:59:54 +00:00
return response () -> json ([ 'message' => 'No UUIDs provided.' ], 400 );
2024-02-02 10:50:28 +00:00
}
2024-03-07 11:22:18 +00:00
$deployments = collect ();
$payload = collect ();
2024-02-02 10:50:28 +00:00
foreach ( $uuids as $uuid ) {
$resource = getResourceByUuid ( $uuid , $teamId );
if ( $resource ) {
2025-03-27 16:16:29 +00:00
[ 'message' => $return_message , 'deployment_uuid' => $deployment_uuid ] = $this -> deploy_resource ( $resource , $force , $pr );
2024-03-07 11:22:18 +00:00
if ( $deployment_uuid ) {
2024-07-03 11:13:38 +00:00
$deployments -> push ([ 'message' => $return_message , 'resource_uuid' => $uuid , 'deployment_uuid' => $deployment_uuid -> toString ()]);
2024-03-11 08:42:02 +00:00
} else {
2024-07-03 11:13:38 +00:00
$deployments -> push ([ 'message' => $return_message , 'resource_uuid' => $uuid ]);
2024-03-07 11:22:18 +00:00
}
2024-02-02 10:50:28 +00:00
}
}
2024-03-11 08:42:02 +00:00
if ( $deployments -> count () > 0 ) {
$payload -> put ( 'deployments' , $deployments -> toArray ());
2024-06-10 20:43:34 +00:00
2024-07-03 11:13:38 +00:00
return response () -> json ( serializeApiResponse ( $payload -> toArray ()));
2024-02-02 10:50:28 +00:00
}
2024-06-10 20:43:34 +00:00
2024-07-09 11:59:54 +00:00
return response () -> json ([ 'message' => 'No resources found.' ], 404 );
2024-02-02 10:50:28 +00:00
}
2024-06-10 20:43:34 +00:00
2024-02-02 10:50:28 +00:00
public function by_tags ( string $tags , int $team_id , bool $force = false )
{
$tags = explode ( ',' , $tags );
$tags = collect ( array_filter ( $tags ));
if ( count ( $tags ) === 0 ) {
2024-07-09 11:59:54 +00:00
return response () -> json ([ 'message' => 'No TAGs provided.' ], 400 );
2024-02-02 10:50:28 +00:00
}
$message = collect ([]);
2024-03-07 11:22:18 +00:00
$deployments = collect ();
$payload = collect ();
2024-02-02 10:50:28 +00:00
foreach ( $tags as $tag ) {
$found_tag = Tag :: where ([ 'name' => $tag , 'team_id' => $team_id ]) -> first ();
2024-06-10 20:43:34 +00:00
if ( ! $found_tag ) {
2024-03-07 11:35:38 +00:00
// $message->push("Tag {$tag} not found.");
2024-02-02 10:50:28 +00:00
continue ;
}
2024-02-06 06:21:06 +00:00
$applications = $found_tag -> applications () -> get ();
$services = $found_tag -> services () -> get ();
2024-02-03 11:39:07 +00:00
if ( $applications -> count () === 0 && $services -> count () === 0 ) {
2024-02-02 10:50:28 +00:00
$message -> push ( " No resources found for tag { $tag } . " );
2024-06-10 20:43:34 +00:00
2024-02-02 10:50:28 +00:00
continue ;
}
2024-02-03 11:39:07 +00:00
foreach ( $applications as $resource ) {
2024-03-07 11:22:18 +00:00
[ '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 ()]);
}
2024-02-03 11:39:07 +00:00
$message = $message -> merge ( $return_message );
}
foreach ( $services as $resource ) {
2024-03-07 11:22:18 +00:00
[ 'message' => $return_message ] = $this -> deploy_resource ( $resource , $force );
2024-02-02 10:50:28 +00:00
$message = $message -> merge ( $return_message );
}
}
if ( $message -> count () > 0 ) {
2024-03-07 11:22:18 +00:00
$payload -> put ( 'message' , $message -> toArray ());
if ( $deployments -> count () > 0 ) {
$payload -> put ( 'details' , $deployments -> toArray ());
}
2024-06-10 20:43:34 +00:00
2024-07-03 11:13:38 +00:00
return response () -> json ( serializeApiResponse ( $payload -> toArray ()));
2024-02-02 10:50:28 +00:00
}
2024-07-09 11:59:54 +00:00
return response () -> json ([ 'message' => 'No resources found with this tag.' ], 404 );
2024-02-02 10:50:28 +00:00
}
2024-06-10 20:43:34 +00:00
2025-03-27 16:16:29 +00:00
public function deploy_resource ( $resource , bool $force = false , int $pr = 0 ) : array
2024-02-02 10:50:28 +00:00
{
2024-03-11 08:42:02 +00:00
$message = null ;
$deployment_uuid = null ;
2024-02-06 06:19:11 +00:00
if ( gettype ( $resource ) !== 'object' ) {
2024-07-03 11:13:38 +00:00
return [ 'message' => " Resource ( $resource ) not found. " , 'deployment_uuid' => $deployment_uuid ];
2024-02-06 06:19:11 +00:00
}
2024-07-02 11:39:44 +00:00
switch ( $resource ? -> getMorphClass ()) {
2024-10-28 13:56:13 +00:00
case \App\Models\Application :: class :
2024-07-25 11:31:59 +00:00
$deployment_uuid = new Cuid2 ;
2024-07-02 11:39:44 +00:00
queue_application_deployment (
application : $resource ,
deployment_uuid : $deployment_uuid ,
force_rebuild : $force ,
2025-03-27 16:16:29 +00:00
pull_request_id : $pr ,
2024-07-02 11:39:44 +00:00
);
$message = " Application { $resource -> name } deployment queued. " ;
break ;
2024-10-28 13:56:13 +00:00
case \App\Models\Service :: class :
2024-07-02 11:39:44 +00:00
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 ;
2024-02-02 10:50:28 +00:00
}
2024-06-10 20:43:34 +00:00
2024-07-03 11:13:38 +00:00
return [ 'message' => $message , 'deployment_uuid' => $deployment_uuid ];
2024-02-02 10:50:28 +00:00
}
}