755 lines
26 KiB
PHP
755 lines
26 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Http\Controllers\Api;
|
||
|
|
|
||
|
|
use App\Actions\Service\DeployServiceApplication;
|
||
|
|
use App\Actions\Service\RestartServiceApplication;
|
||
|
|
use App\Actions\Service\StopServiceApplication;
|
||
|
|
use App\Actions\Service\UpdateServiceApplicationFromApi;
|
||
|
|
use App\Http\Controllers\Controller;
|
||
|
|
use App\Models\Service;
|
||
|
|
use App\Models\ServiceApplication;
|
||
|
|
use Illuminate\Http\JsonResponse;
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use Illuminate\Support\Facades\Validator;
|
||
|
|
use OpenApi\Attributes as OA;
|
||
|
|
|
||
|
|
class ServiceApplicationsController extends Controller
|
||
|
|
{
|
||
|
|
private function removeSensitiveData(ServiceApplication $serviceApplication): array
|
||
|
|
{
|
||
|
|
$serviceApplication->makeHidden([
|
||
|
|
'id',
|
||
|
|
'resourceable',
|
||
|
|
'resourceable_id',
|
||
|
|
'resourceable_type',
|
||
|
|
]);
|
||
|
|
|
||
|
|
return serializeApiResponse($serviceApplication);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function resolveService(Request $request, int $teamId): ?Service
|
||
|
|
{
|
||
|
|
$uuid = $request->route('uuid');
|
||
|
|
if (! $uuid) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return Service::whereRelation('environment.project.team', 'id', $teamId)
|
||
|
|
->whereUuid($uuid)
|
||
|
|
->first();
|
||
|
|
}
|
||
|
|
|
||
|
|
private function resolveServiceApplicationForService(Request $request, Service $service): ?ServiceApplication
|
||
|
|
{
|
||
|
|
$appUuid = $request->route('app_uuid');
|
||
|
|
if (! $appUuid) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $service->applications()
|
||
|
|
->where('uuid', $appUuid)
|
||
|
|
->with(['service.destination.server'])
|
||
|
|
->first();
|
||
|
|
}
|
||
|
|
|
||
|
|
private function swarmNotSupportedResponse(): JsonResponse
|
||
|
|
{
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'This operation is not supported for Swarm servers yet.',
|
||
|
|
], 501);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[OA\Get(
|
||
|
|
summary: 'List service applications',
|
||
|
|
description: 'List compose service applications (containers) for a single service.',
|
||
|
|
path: '/services/{uuid}/applications',
|
||
|
|
operationId: 'list-service-applications-by-service-uuid',
|
||
|
|
security: [
|
||
|
|
['bearerAuth' => []],
|
||
|
|
],
|
||
|
|
tags: ['Service applications'],
|
||
|
|
parameters: [
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
],
|
||
|
|
responses: [
|
||
|
|
new OA\Response(
|
||
|
|
response: 200,
|
||
|
|
description: 'Service applications for this service.',
|
||
|
|
content: [
|
||
|
|
new OA\MediaType(
|
||
|
|
mediaType: 'application/json',
|
||
|
|
schema: new OA\Schema(
|
||
|
|
type: 'array',
|
||
|
|
items: new OA\Items(type: 'object')
|
||
|
|
)
|
||
|
|
),
|
||
|
|
]
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 401,
|
||
|
|
ref: '#/components/responses/401',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 404,
|
||
|
|
ref: '#/components/responses/404',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
)]
|
||
|
|
public function index(Request $request): JsonResponse
|
||
|
|
{
|
||
|
|
$teamId = getTeamIdFromToken();
|
||
|
|
if (is_null($teamId)) {
|
||
|
|
return invalidTokenResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
$service = $this->resolveService($request, $teamId);
|
||
|
|
if (! $service) {
|
||
|
|
return response()->json(['message' => 'Service not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->authorize('view', $service);
|
||
|
|
|
||
|
|
$items = $service->applications()
|
||
|
|
->get()
|
||
|
|
->map(fn (ServiceApplication $sa) => $this->removeSensitiveData($sa));
|
||
|
|
|
||
|
|
return response()->json($items);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[OA\Get(
|
||
|
|
summary: 'Get service application',
|
||
|
|
description: 'Get a single compose service application by service UUID and application UUID.',
|
||
|
|
path: '/services/{uuid}/applications/{app_uuid}',
|
||
|
|
operationId: 'get-service-application-by-service-and-app-uuid',
|
||
|
|
security: [
|
||
|
|
['bearerAuth' => []],
|
||
|
|
],
|
||
|
|
tags: ['Service applications'],
|
||
|
|
parameters: [
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'app_uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service application UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
],
|
||
|
|
responses: [
|
||
|
|
new OA\Response(
|
||
|
|
response: 200,
|
||
|
|
description: 'Service application.',
|
||
|
|
content: [
|
||
|
|
new OA\MediaType(
|
||
|
|
mediaType: 'application/json',
|
||
|
|
schema: new OA\Schema(type: 'object')
|
||
|
|
),
|
||
|
|
]
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 401,
|
||
|
|
ref: '#/components/responses/401',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 404,
|
||
|
|
ref: '#/components/responses/404',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
)]
|
||
|
|
public function show(Request $request): JsonResponse
|
||
|
|
{
|
||
|
|
$teamId = getTeamIdFromToken();
|
||
|
|
if (is_null($teamId)) {
|
||
|
|
return invalidTokenResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
$service = $this->resolveService($request, $teamId);
|
||
|
|
if (! $service) {
|
||
|
|
return response()->json(['message' => 'Service not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$serviceApplication = $this->resolveServiceApplicationForService($request, $service);
|
||
|
|
if (! $serviceApplication) {
|
||
|
|
return response()->json(['message' => 'Service application not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->authorize('view', $serviceApplication);
|
||
|
|
|
||
|
|
return response()->json($this->removeSensitiveData($serviceApplication));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[OA\Patch(
|
||
|
|
summary: 'Update service application',
|
||
|
|
description: 'Update fields for a compose service application. Use `url` for comma-separated public URLs (same rules as `urls[].url` on PATCH /services/{uuid}).',
|
||
|
|
path: '/services/{uuid}/applications/{app_uuid}',
|
||
|
|
operationId: 'patch-service-application-by-service-and-app-uuid',
|
||
|
|
security: [
|
||
|
|
['bearerAuth' => []],
|
||
|
|
],
|
||
|
|
tags: ['Service applications'],
|
||
|
|
parameters: [
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'app_uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service application UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'force_domain_override',
|
||
|
|
in: 'query',
|
||
|
|
description: 'When true, allow duplicate URLs in the request and proceed despite domain conflicts (same as service PATCH).',
|
||
|
|
required: false,
|
||
|
|
schema: new OA\Schema(type: 'boolean', default: false)
|
||
|
|
),
|
||
|
|
],
|
||
|
|
requestBody: new OA\RequestBody(
|
||
|
|
content: new OA\MediaType(
|
||
|
|
mediaType: 'application/json',
|
||
|
|
schema: new OA\Schema(
|
||
|
|
type: 'object',
|
||
|
|
properties: [
|
||
|
|
'url' => new OA\Property(
|
||
|
|
property: 'url',
|
||
|
|
type: 'string',
|
||
|
|
nullable: true,
|
||
|
|
description: 'Comma-separated list of URLs (e.g. "http://app.example.com:8080,https://app2.example.com"). Stored as fqdn.'
|
||
|
|
),
|
||
|
|
'human_name' => new OA\Property(property: 'human_name', type: 'string', nullable: true),
|
||
|
|
'description' => new OA\Property(property: 'description', type: 'string', nullable: true),
|
||
|
|
'image' => new OA\Property(property: 'image', type: 'string', nullable: true),
|
||
|
|
'exclude_from_status' => new OA\Property(property: 'exclude_from_status', type: 'boolean', nullable: true),
|
||
|
|
'is_log_drain_enabled' => new OA\Property(property: 'is_log_drain_enabled', type: 'boolean', nullable: true),
|
||
|
|
'is_gzip_enabled' => new OA\Property(property: 'is_gzip_enabled', type: 'boolean', nullable: true),
|
||
|
|
'is_stripprefix_enabled' => new OA\Property(property: 'is_stripprefix_enabled', type: 'boolean', nullable: true),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
)
|
||
|
|
),
|
||
|
|
responses: [
|
||
|
|
new OA\Response(
|
||
|
|
response: 200,
|
||
|
|
description: 'Updated service application.',
|
||
|
|
content: [
|
||
|
|
new OA\MediaType(
|
||
|
|
mediaType: 'application/json',
|
||
|
|
schema: new OA\Schema(type: 'object')
|
||
|
|
),
|
||
|
|
]
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 401,
|
||
|
|
ref: '#/components/responses/401',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 404,
|
||
|
|
ref: '#/components/responses/404',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 409,
|
||
|
|
description: 'Domain conflicts (unless force_domain_override).',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 422,
|
||
|
|
ref: '#/components/responses/422',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
)]
|
||
|
|
public function update(Request $request, UpdateServiceApplicationFromApi $updateServiceApplicationFromApi): JsonResponse
|
||
|
|
{
|
||
|
|
$teamId = getTeamIdFromToken();
|
||
|
|
if (is_null($teamId)) {
|
||
|
|
return invalidTokenResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
$return = validateIncomingRequest($request);
|
||
|
|
if ($return instanceof JsonResponse) {
|
||
|
|
return $return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$service = $this->resolveService($request, $teamId);
|
||
|
|
if (! $service) {
|
||
|
|
return response()->json(['message' => 'Service not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$serviceApplication = $this->resolveServiceApplicationForService($request, $service);
|
||
|
|
if (! $serviceApplication) {
|
||
|
|
return response()->json(['message' => 'Service application not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->authorize('update', $serviceApplication);
|
||
|
|
|
||
|
|
$allowedFields = [
|
||
|
|
'url',
|
||
|
|
'human_name',
|
||
|
|
'description',
|
||
|
|
'image',
|
||
|
|
'exclude_from_status',
|
||
|
|
'is_log_drain_enabled',
|
||
|
|
'is_gzip_enabled',
|
||
|
|
'is_stripprefix_enabled',
|
||
|
|
];
|
||
|
|
|
||
|
|
$validationRules = [
|
||
|
|
'url' => 'nullable|string',
|
||
|
|
'human_name' => 'nullable|string|max:255',
|
||
|
|
'description' => 'nullable|string',
|
||
|
|
'image' => 'nullable|string',
|
||
|
|
'exclude_from_status' => 'sometimes|boolean',
|
||
|
|
'is_log_drain_enabled' => 'sometimes|boolean',
|
||
|
|
'is_gzip_enabled' => 'sometimes|boolean',
|
||
|
|
'is_stripprefix_enabled' => 'sometimes|boolean',
|
||
|
|
];
|
||
|
|
|
||
|
|
$validator = Validator::make($request->all(), $validationRules);
|
||
|
|
|
||
|
|
$extraFields = array_diff(array_keys($request->all()), $allowedFields);
|
||
|
|
if ($validator->fails() || ! empty($extraFields)) {
|
||
|
|
$errors = $validator->errors();
|
||
|
|
foreach ($extraFields as $field) {
|
||
|
|
$errors->add($field, 'This field is not allowed.');
|
||
|
|
}
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Validation failed.',
|
||
|
|
'errors' => $errors,
|
||
|
|
], 422);
|
||
|
|
}
|
||
|
|
|
||
|
|
$response = $updateServiceApplicationFromApi->execute($serviceApplication, $request, $teamId);
|
||
|
|
if ($response instanceof JsonResponse) {
|
||
|
|
return $response;
|
||
|
|
}
|
||
|
|
|
||
|
|
$serviceApplication->refresh();
|
||
|
|
|
||
|
|
return response()->json($this->removeSensitiveData($serviceApplication));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[OA\Get(
|
||
|
|
summary: 'Get service application logs',
|
||
|
|
description: 'Get Docker logs for a single compose service container.',
|
||
|
|
path: '/services/{uuid}/applications/{app_uuid}/logs',
|
||
|
|
operationId: 'get-service-application-logs-by-service-and-app-uuid',
|
||
|
|
security: [
|
||
|
|
['bearerAuth' => []],
|
||
|
|
],
|
||
|
|
tags: ['Service applications'],
|
||
|
|
parameters: [
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'app_uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service application UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'lines',
|
||
|
|
in: 'query',
|
||
|
|
description: 'Number of lines to show from the end of the logs.',
|
||
|
|
required: false,
|
||
|
|
schema: new OA\Schema(type: 'integer', format: 'int32', default: 100)
|
||
|
|
),
|
||
|
|
],
|
||
|
|
responses: [
|
||
|
|
new OA\Response(
|
||
|
|
response: 200,
|
||
|
|
description: 'Logs.',
|
||
|
|
content: [
|
||
|
|
new OA\MediaType(
|
||
|
|
mediaType: 'application/json',
|
||
|
|
schema: new OA\Schema(
|
||
|
|
type: 'object',
|
||
|
|
properties: [
|
||
|
|
'logs' => new OA\Property(property: 'logs', type: 'string'),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
),
|
||
|
|
]
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 400,
|
||
|
|
ref: '#/components/responses/400',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 401,
|
||
|
|
ref: '#/components/responses/401',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 404,
|
||
|
|
ref: '#/components/responses/404',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 501,
|
||
|
|
description: 'Swarm not supported.',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
)]
|
||
|
|
public function logs_by_uuid(Request $request): JsonResponse
|
||
|
|
{
|
||
|
|
$teamId = getTeamIdFromToken();
|
||
|
|
if (is_null($teamId)) {
|
||
|
|
return invalidTokenResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
$service = $this->resolveService($request, $teamId);
|
||
|
|
if (! $service) {
|
||
|
|
return response()->json(['message' => 'Service not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$serviceApplication = $this->resolveServiceApplicationForService($request, $service);
|
||
|
|
if (! $serviceApplication) {
|
||
|
|
return response()->json(['message' => 'Service application not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->authorize('view', $serviceApplication);
|
||
|
|
|
||
|
|
$server = $serviceApplication->service->destination->server;
|
||
|
|
if ($server->isSwarm()) {
|
||
|
|
return $this->swarmNotSupportedResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (! $server->isFunctional()) {
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Server is not functional.',
|
||
|
|
], 400);
|
||
|
|
}
|
||
|
|
|
||
|
|
$containerName = $serviceApplication->name.'-'.$serviceApplication->service->uuid;
|
||
|
|
|
||
|
|
$status = getContainerStatus($server, $containerName);
|
||
|
|
if ($status !== 'running') {
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Service application container is not running.',
|
||
|
|
], 400);
|
||
|
|
}
|
||
|
|
|
||
|
|
$lines = (int) ($request->query('lines', 100) ?: 100);
|
||
|
|
$logs = getContainerLogs($server, $containerName, $lines);
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'logs' => $logs,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[OA\Get(
|
||
|
|
summary: 'Start or redeploy service application container',
|
||
|
|
description: 'Runs docker compose up for a single compose service (no-deps), optionally pulling the image and rebuilding.',
|
||
|
|
path: '/services/{uuid}/applications/{app_uuid}/start',
|
||
|
|
operationId: 'start-service-application-by-service-and-app-uuid',
|
||
|
|
security: [
|
||
|
|
['bearerAuth' => []],
|
||
|
|
],
|
||
|
|
tags: ['Service applications'],
|
||
|
|
parameters: [
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'app_uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service application UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'force',
|
||
|
|
in: 'query',
|
||
|
|
description: 'When true, passes --build to docker compose up.',
|
||
|
|
required: false,
|
||
|
|
schema: new OA\Schema(type: 'boolean', default: false)
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'latest',
|
||
|
|
in: 'query',
|
||
|
|
description: 'When true, pulls the image for this compose service before up.',
|
||
|
|
required: false,
|
||
|
|
schema: new OA\Schema(type: 'boolean', default: false)
|
||
|
|
),
|
||
|
|
],
|
||
|
|
responses: [
|
||
|
|
new OA\Response(
|
||
|
|
response: 200,
|
||
|
|
description: 'Deploy request queued.',
|
||
|
|
content: [
|
||
|
|
new OA\MediaType(
|
||
|
|
mediaType: 'application/json',
|
||
|
|
schema: new OA\Schema(
|
||
|
|
type: 'object',
|
||
|
|
properties: [
|
||
|
|
'message' => new OA\Property(property: 'message', type: 'string'),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
),
|
||
|
|
]
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 401,
|
||
|
|
ref: '#/components/responses/401',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 404,
|
||
|
|
ref: '#/components/responses/404',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 501,
|
||
|
|
description: 'Swarm not supported.',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
)]
|
||
|
|
public function action_start(Request $request): JsonResponse
|
||
|
|
{
|
||
|
|
$teamId = getTeamIdFromToken();
|
||
|
|
if (is_null($teamId)) {
|
||
|
|
return invalidTokenResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
$service = $this->resolveService($request, $teamId);
|
||
|
|
if (! $service) {
|
||
|
|
return response()->json(['message' => 'Service not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$serviceApplication = $this->resolveServiceApplicationForService($request, $service);
|
||
|
|
if (! $serviceApplication) {
|
||
|
|
return response()->json(['message' => 'Service application not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->authorize('deploy', $serviceApplication);
|
||
|
|
|
||
|
|
$server = $serviceApplication->service->destination->server;
|
||
|
|
if ($server->isSwarm()) {
|
||
|
|
return $this->swarmNotSupportedResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (! $server->isFunctional()) {
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Server is not functional.',
|
||
|
|
], 400);
|
||
|
|
}
|
||
|
|
|
||
|
|
$pullLatest = $request->boolean('latest', false);
|
||
|
|
$forceRebuild = $request->boolean('force', false);
|
||
|
|
|
||
|
|
DeployServiceApplication::dispatch($serviceApplication, $pullLatest, $forceRebuild);
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Service application deploy request queued.',
|
||
|
|
], 200);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[OA\Get(
|
||
|
|
summary: 'Restart service application container',
|
||
|
|
description: 'Restarts a single compose service container (docker restart).',
|
||
|
|
path: '/services/{uuid}/applications/{app_uuid}/restart',
|
||
|
|
operationId: 'restart-service-application-by-service-and-app-uuid',
|
||
|
|
security: [
|
||
|
|
['bearerAuth' => []],
|
||
|
|
],
|
||
|
|
tags: ['Service applications'],
|
||
|
|
parameters: [
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'app_uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service application UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
],
|
||
|
|
responses: [
|
||
|
|
new OA\Response(
|
||
|
|
response: 200,
|
||
|
|
description: 'Restart queued.',
|
||
|
|
content: [
|
||
|
|
new OA\MediaType(
|
||
|
|
mediaType: 'application/json',
|
||
|
|
schema: new OA\Schema(
|
||
|
|
type: 'object',
|
||
|
|
properties: [
|
||
|
|
'message' => new OA\Property(property: 'message', type: 'string'),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
),
|
||
|
|
]
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 401,
|
||
|
|
ref: '#/components/responses/401',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 404,
|
||
|
|
ref: '#/components/responses/404',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 501,
|
||
|
|
description: 'Swarm not supported.',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
)]
|
||
|
|
public function action_restart(Request $request): JsonResponse
|
||
|
|
{
|
||
|
|
$teamId = getTeamIdFromToken();
|
||
|
|
if (is_null($teamId)) {
|
||
|
|
return invalidTokenResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
$service = $this->resolveService($request, $teamId);
|
||
|
|
if (! $service) {
|
||
|
|
return response()->json(['message' => 'Service not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$serviceApplication = $this->resolveServiceApplicationForService($request, $service);
|
||
|
|
if (! $serviceApplication) {
|
||
|
|
return response()->json(['message' => 'Service application not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->authorize('deploy', $serviceApplication);
|
||
|
|
|
||
|
|
$server = $serviceApplication->service->destination->server;
|
||
|
|
if ($server->isSwarm()) {
|
||
|
|
return $this->swarmNotSupportedResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (! $server->isFunctional()) {
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Server is not functional.',
|
||
|
|
], 400);
|
||
|
|
}
|
||
|
|
|
||
|
|
RestartServiceApplication::dispatch($serviceApplication);
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Service application restart request queued.',
|
||
|
|
], 200);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[OA\Get(
|
||
|
|
summary: 'Stop service application container',
|
||
|
|
description: 'Stops a single compose service container (docker stop).',
|
||
|
|
path: '/services/{uuid}/applications/{app_uuid}/stop',
|
||
|
|
operationId: 'stop-service-application-by-service-and-app-uuid',
|
||
|
|
security: [
|
||
|
|
['bearerAuth' => []],
|
||
|
|
],
|
||
|
|
tags: ['Service applications'],
|
||
|
|
parameters: [
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
new OA\Parameter(
|
||
|
|
name: 'app_uuid',
|
||
|
|
in: 'path',
|
||
|
|
description: 'Service application UUID.',
|
||
|
|
required: true,
|
||
|
|
schema: new OA\Schema(type: 'string')
|
||
|
|
),
|
||
|
|
],
|
||
|
|
responses: [
|
||
|
|
new OA\Response(
|
||
|
|
response: 200,
|
||
|
|
description: 'Stop queued.',
|
||
|
|
content: [
|
||
|
|
new OA\MediaType(
|
||
|
|
mediaType: 'application/json',
|
||
|
|
schema: new OA\Schema(
|
||
|
|
type: 'object',
|
||
|
|
properties: [
|
||
|
|
'message' => new OA\Property(property: 'message', type: 'string'),
|
||
|
|
]
|
||
|
|
)
|
||
|
|
),
|
||
|
|
]
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 401,
|
||
|
|
ref: '#/components/responses/401',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 404,
|
||
|
|
ref: '#/components/responses/404',
|
||
|
|
),
|
||
|
|
new OA\Response(
|
||
|
|
response: 501,
|
||
|
|
description: 'Swarm not supported.',
|
||
|
|
),
|
||
|
|
]
|
||
|
|
)]
|
||
|
|
public function action_stop(Request $request): JsonResponse
|
||
|
|
{
|
||
|
|
$teamId = getTeamIdFromToken();
|
||
|
|
if (is_null($teamId)) {
|
||
|
|
return invalidTokenResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
$service = $this->resolveService($request, $teamId);
|
||
|
|
if (! $service) {
|
||
|
|
return response()->json(['message' => 'Service not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$serviceApplication = $this->resolveServiceApplicationForService($request, $service);
|
||
|
|
if (! $serviceApplication) {
|
||
|
|
return response()->json(['message' => 'Service application not found.'], 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->authorize('deploy', $serviceApplication);
|
||
|
|
|
||
|
|
$server = $serviceApplication->service->destination->server;
|
||
|
|
if ($server->isSwarm()) {
|
||
|
|
return $this->swarmNotSupportedResponse();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (! $server->isFunctional()) {
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Server is not functional.',
|
||
|
|
], 400);
|
||
|
|
}
|
||
|
|
|
||
|
|
StopServiceApplication::dispatch($serviceApplication);
|
||
|
|
|
||
|
|
return response()->json([
|
||
|
|
'message' => 'Service application stop request queued.',
|
||
|
|
], 200);
|
||
|
|
}
|
||
|
|
}
|