feat(api): require POST for state-changing endpoints
Make start/stop/restart, deploy, enable/disable, and server validate POST-only, with GET returning 405. Server validate accepts optional install and uses ValidateAndInstallServerJob. Update OpenAPI and tests.
This commit is contained in:
parent
e2c2180f4f
commit
0633b543ee
16 changed files with 297 additions and 605 deletions
|
|
@ -3849,9 +3849,9 @@ public function delete_env_by_uuid(Request $request)
|
|||
]);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Start',
|
||||
description: 'Start application. `Post` request is also accepted.',
|
||||
description: 'Start application.',
|
||||
path: '/applications/{uuid}/start',
|
||||
operationId: 'start-application-by-uuid',
|
||||
security: [
|
||||
|
|
@ -3973,9 +3973,9 @@ public function action_deploy(Request $request)
|
|||
);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Stop',
|
||||
description: 'Stop application. `Post` request is also accepted.',
|
||||
description: 'Stop application.',
|
||||
path: '/applications/{uuid}/stop',
|
||||
operationId: 'stop-application-by-uuid',
|
||||
security: [
|
||||
|
|
@ -4066,9 +4066,9 @@ public function action_stop(Request $request)
|
|||
);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Restart',
|
||||
description: 'Restart application. `Post` request is also accepted.',
|
||||
description: 'Restart application.',
|
||||
path: '/applications/{uuid}/restart',
|
||||
operationId: 'restart-application-by-uuid',
|
||||
security: [
|
||||
|
|
|
|||
|
|
@ -3050,9 +3050,9 @@ public function move_by_uuid(Request $request): JsonResponse
|
|||
return moveResourceToEnvironment($request, $database, 'Database', $teamId);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Start',
|
||||
description: 'Start database. `Post` request is also accepted.',
|
||||
description: 'Start database.',
|
||||
path: '/databases/{uuid}/start',
|
||||
operationId: 'start-database-by-uuid',
|
||||
security: [
|
||||
|
|
@ -3137,9 +3137,9 @@ public function action_deploy(Request $request)
|
|||
);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Stop',
|
||||
description: 'Stop database. `Post` request is also accepted.',
|
||||
description: 'Stop database.',
|
||||
path: '/databases/{uuid}/stop',
|
||||
operationId: 'stop-database-by-uuid',
|
||||
security: [
|
||||
|
|
@ -3236,9 +3236,9 @@ public function action_stop(Request $request)
|
|||
);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Restart',
|
||||
description: 'Restart database. `Post` request is also accepted.',
|
||||
description: 'Restart database.',
|
||||
path: '/databases/{uuid}/restart',
|
||||
operationId: 'restart-database-by-uuid',
|
||||
security: [
|
||||
|
|
|
|||
|
|
@ -304,9 +304,9 @@ public function cancel_deployment(Request $request)
|
|||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Deploy',
|
||||
description: 'Deploy by tag or uuid. `Post` request also accepted with `uuid` and `tag` json body.',
|
||||
description: 'Deploy by tag or UUID using query parameters or a JSON body.',
|
||||
path: '/deploy',
|
||||
operationId: 'deploy-by-tag-or-uuid',
|
||||
security: [
|
||||
|
|
|
|||
|
|
@ -3,12 +3,20 @@
|
|||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use OpenApi\Attributes as OA;
|
||||
|
||||
class OtherController extends Controller
|
||||
{
|
||||
public function post_required(): JsonResponse
|
||||
{
|
||||
return response()
|
||||
->json(['message' => 'This endpoint has changed to a POST request.'], 405)
|
||||
->header('Allow', 'POST');
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
summary: 'Version',
|
||||
description: 'Get Coolify version.',
|
||||
|
|
@ -41,7 +49,7 @@ public function version(Request $request)
|
|||
return response(config('constants.coolify.version'));
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Enable API',
|
||||
description: 'Enable API (only with root permissions).',
|
||||
path: '/enable',
|
||||
|
|
@ -97,7 +105,7 @@ public function enable_api(Request $request)
|
|||
return response()->json(['message' => 'API enabled.'], 200);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Disable API',
|
||||
description: 'Disable API (only with root permissions).',
|
||||
path: '/disable',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
use App\Enums\ProxyTypes;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\DeleteResourceJob;
|
||||
use App\Jobs\ValidateAndInstallServerJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Project;
|
||||
|
|
@ -887,7 +888,7 @@ public function delete_server(Request $request)
|
|||
return response()->json(['message' => 'Server deleted.']);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Validate',
|
||||
description: 'Validate server by UUID.',
|
||||
path: '/servers/{uuid}/validate',
|
||||
|
|
@ -899,6 +900,19 @@ public function delete_server(Request $request)
|
|||
parameters: [
|
||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server UUID', schema: new OA\Schema(type: 'string')),
|
||||
],
|
||||
requestBody: new OA\RequestBody(
|
||||
required: false,
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(
|
||||
property: 'install',
|
||||
description: 'Install missing prerequisites and Docker. This can restart the Docker daemon.',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 201,
|
||||
|
|
@ -948,14 +962,33 @@ public function validate_server(Request $request)
|
|||
return response()->json(['message' => 'Server not found.'], 404);
|
||||
}
|
||||
$this->authorize('update', $server);
|
||||
ValidateServer::dispatch($server);
|
||||
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'install' => 'boolean',
|
||||
]);
|
||||
if ($validator->fails()) {
|
||||
return response()->json([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $validator->errors(),
|
||||
], 422);
|
||||
}
|
||||
|
||||
$install = $request->boolean('install', false);
|
||||
if ($install) {
|
||||
ValidateAndInstallServerJob::dispatch($server);
|
||||
} else {
|
||||
ValidateServer::dispatch($server);
|
||||
}
|
||||
|
||||
auditLog('api.server.validated', [
|
||||
'team_id' => $teamId,
|
||||
'server_uuid' => $server->uuid,
|
||||
'server_name' => $server->name,
|
||||
'install' => $install,
|
||||
]);
|
||||
|
||||
return response()->json(['message' => 'Validation started.'], 201);
|
||||
$message = $install ? 'Validation and installation started.' : 'Validation started.';
|
||||
|
||||
return response()->json(['message' => $message], 201);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -498,75 +498,6 @@ public function logs_by_uuid(Request $request): JsonResponse
|
|||
]);
|
||||
}
|
||||
|
||||
#[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.',
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[OA\Post(
|
||||
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.',
|
||||
|
|
@ -635,61 +566,6 @@ public function action_start(Request $request): JsonResponse
|
|||
], 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.',
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[OA\Post(
|
||||
summary: 'Restart service application container',
|
||||
description: 'Restarts a single compose service container.',
|
||||
|
|
@ -753,61 +629,6 @@ public function action_restart(Request $request): JsonResponse
|
|||
], 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.',
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[OA\Post(
|
||||
summary: 'Stop service application container',
|
||||
description: 'Stops a single compose service container.',
|
||||
|
|
|
|||
|
|
@ -1970,9 +1970,9 @@ public function move_by_uuid(Request $request): JsonResponse
|
|||
return moveResourceToEnvironment($request, $service, 'Service', $teamId);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Start',
|
||||
description: 'Start service. `Post` request is also accepted.',
|
||||
description: 'Start service.',
|
||||
path: '/services/{uuid}/start',
|
||||
operationId: 'start-service-by-uuid',
|
||||
security: [
|
||||
|
|
@ -2056,9 +2056,9 @@ public function action_deploy(Request $request)
|
|||
);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Stop',
|
||||
description: 'Stop service. `Post` request is also accepted.',
|
||||
description: 'Stop service.',
|
||||
path: '/services/{uuid}/stop',
|
||||
operationId: 'stop-service-by-uuid',
|
||||
security: [
|
||||
|
|
@ -2154,9 +2154,9 @@ public function action_stop(Request $request)
|
|||
);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
#[OA\Post(
|
||||
summary: 'Restart',
|
||||
description: 'Restart service. `Post` request is also accepted.',
|
||||
description: 'Restart service.',
|
||||
path: '/services/{uuid}/restart',
|
||||
operationId: 'restart-service-by-uuid',
|
||||
security: [
|
||||
|
|
|
|||
260
openapi.json
260
openapi.json
|
|
@ -3535,12 +3535,12 @@
|
|||
}
|
||||
},
|
||||
"\/applications\/{uuid}\/start": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Applications"
|
||||
],
|
||||
"summary": "Start",
|
||||
"description": "Start application. `Post` request is also accepted.",
|
||||
"description": "Start application.",
|
||||
"operationId": "start-application-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -3612,12 +3612,12 @@
|
|||
}
|
||||
},
|
||||
"\/applications\/{uuid}\/stop": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Applications"
|
||||
],
|
||||
"summary": "Stop",
|
||||
"description": "Stop application. `Post` request is also accepted.",
|
||||
"description": "Stop application.",
|
||||
"operationId": "stop-application-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -3674,12 +3674,12 @@
|
|||
}
|
||||
},
|
||||
"\/applications\/{uuid}\/restart": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Applications"
|
||||
],
|
||||
"summary": "Restart",
|
||||
"description": "Restart application. `Post` request is also accepted.",
|
||||
"description": "Restart application.",
|
||||
"operationId": "restart-application-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -7034,12 +7034,12 @@
|
|||
}
|
||||
},
|
||||
"\/databases\/{uuid}\/start": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Databases"
|
||||
],
|
||||
"summary": "Start",
|
||||
"description": "Start database. `Post` request is also accepted.",
|
||||
"description": "Start database.",
|
||||
"operationId": "start-database-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -7087,12 +7087,12 @@
|
|||
}
|
||||
},
|
||||
"\/databases\/{uuid}\/stop": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Databases"
|
||||
],
|
||||
"summary": "Stop",
|
||||
"description": "Stop database. `Post` request is also accepted.",
|
||||
"description": "Stop database.",
|
||||
"operationId": "stop-database-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -7149,12 +7149,12 @@
|
|||
}
|
||||
},
|
||||
"\/databases\/{uuid}\/restart": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Databases"
|
||||
],
|
||||
"summary": "Restart",
|
||||
"description": "Restart database. `Post` request is also accepted.",
|
||||
"description": "Restart database.",
|
||||
"operationId": "restart-database-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -8256,12 +8256,12 @@
|
|||
}
|
||||
},
|
||||
"\/deploy": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Deployments"
|
||||
],
|
||||
"summary": "Deploy",
|
||||
"description": "Deploy by tag or uuid. `Post` request also accepted with `uuid` and `tag` json body.",
|
||||
"description": "Deploy by tag or UUID using query parameters or a JSON body.",
|
||||
"operationId": "deploy-by-tag-or-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -10103,7 +10103,7 @@
|
|||
}
|
||||
},
|
||||
"\/enable": {
|
||||
"get": {
|
||||
"post": {
|
||||
"summary": "Enable API",
|
||||
"description": "Enable API (only with root permissions).",
|
||||
"operationId": "enable-api",
|
||||
|
|
@ -10155,7 +10155,7 @@
|
|||
}
|
||||
},
|
||||
"\/disable": {
|
||||
"get": {
|
||||
"post": {
|
||||
"summary": "Disable API",
|
||||
"description": "Disable API (only with root permissions).",
|
||||
"operationId": "disable-api",
|
||||
|
|
@ -12376,7 +12376,7 @@
|
|||
}
|
||||
},
|
||||
"\/servers\/{uuid}\/validate": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Servers"
|
||||
],
|
||||
|
|
@ -12394,6 +12394,23 @@
|
|||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": false,
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"install": {
|
||||
"description": "Install missing prerequisites and Docker. This can restart the Docker daemon.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Server validation started.",
|
||||
|
|
@ -12804,85 +12821,6 @@
|
|||
}
|
||||
},
|
||||
"\/services\/{uuid}\/applications\/{app_uuid}\/start": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Service applications"
|
||||
],
|
||||
"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.",
|
||||
"operationId": "start-service-application-by-service-and-app-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "uuid",
|
||||
"in": "path",
|
||||
"description": "Service UUID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "app_uuid",
|
||||
"in": "path",
|
||||
"description": "Service application UUID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "force",
|
||||
"in": "query",
|
||||
"description": "When true, passes --build to docker compose up.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "latest",
|
||||
"in": "query",
|
||||
"description": "When true, pulls the image for this compose service before up.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Deploy request queued.",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#\/components\/responses\/401"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#\/components\/responses\/404"
|
||||
},
|
||||
"501": {
|
||||
"description": "Swarm not supported."
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"Service applications"
|
||||
|
|
@ -12963,65 +12901,6 @@
|
|||
}
|
||||
},
|
||||
"\/services\/{uuid}\/applications\/{app_uuid}\/restart": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Service applications"
|
||||
],
|
||||
"summary": "Restart service application container",
|
||||
"description": "Restarts a single compose service container (docker restart).",
|
||||
"operationId": "restart-service-application-by-service-and-app-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "uuid",
|
||||
"in": "path",
|
||||
"description": "Service UUID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "app_uuid",
|
||||
"in": "path",
|
||||
"description": "Service application UUID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Restart queued.",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#\/components\/responses\/401"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#\/components\/responses\/404"
|
||||
},
|
||||
"501": {
|
||||
"description": "Swarm not supported."
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"Service applications"
|
||||
|
|
@ -13084,65 +12963,6 @@
|
|||
}
|
||||
},
|
||||
"\/services\/{uuid}\/applications\/{app_uuid}\/stop": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Service applications"
|
||||
],
|
||||
"summary": "Stop service application container",
|
||||
"description": "Stops a single compose service container (docker stop).",
|
||||
"operationId": "stop-service-application-by-service-and-app-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "uuid",
|
||||
"in": "path",
|
||||
"description": "Service UUID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "app_uuid",
|
||||
"in": "path",
|
||||
"description": "Service application UUID.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Stop queued.",
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#\/components\/responses\/401"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#\/components\/responses\/404"
|
||||
},
|
||||
"501": {
|
||||
"description": "Swarm not supported."
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"Service applications"
|
||||
|
|
@ -14797,12 +14617,12 @@
|
|||
}
|
||||
},
|
||||
"\/services\/{uuid}\/start": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Services"
|
||||
],
|
||||
"summary": "Start",
|
||||
"description": "Start service. `Post` request is also accepted.",
|
||||
"description": "Start service.",
|
||||
"operationId": "start-service-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -14850,12 +14670,12 @@
|
|||
}
|
||||
},
|
||||
"\/services\/{uuid}\/stop": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Services"
|
||||
],
|
||||
"summary": "Stop",
|
||||
"description": "Stop service. `Post` request is also accepted.",
|
||||
"description": "Stop service.",
|
||||
"operationId": "stop-service-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
@ -14912,12 +14732,12 @@
|
|||
}
|
||||
},
|
||||
"\/services\/{uuid}\/restart": {
|
||||
"get": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Services"
|
||||
],
|
||||
"summary": "Restart",
|
||||
"description": "Restart service. `Post` request is also accepted.",
|
||||
"description": "Restart service.",
|
||||
"operationId": "restart-service-by-uuid",
|
||||
"parameters": [
|
||||
{
|
||||
|
|
|
|||
190
openapi.yaml
190
openapi.yaml
|
|
@ -2304,11 +2304,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/applications/{uuid}/start':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Applications
|
||||
summary: Start
|
||||
description: 'Start application. `Post` request is also accepted.'
|
||||
description: 'Start application.'
|
||||
operationId: start-application-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -2352,11 +2352,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/applications/{uuid}/stop':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Applications
|
||||
summary: Stop
|
||||
description: 'Stop application. `Post` request is also accepted.'
|
||||
description: 'Stop application.'
|
||||
operationId: stop-application-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -2392,11 +2392,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/applications/{uuid}/restart':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Applications
|
||||
summary: Restart
|
||||
description: 'Restart application. `Post` request is also accepted.'
|
||||
description: 'Restart application.'
|
||||
operationId: restart-application-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -4635,11 +4635,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/databases/{uuid}/start':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Databases
|
||||
summary: Start
|
||||
description: 'Start database. `Post` request is also accepted.'
|
||||
description: 'Start database.'
|
||||
operationId: start-database-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -4668,11 +4668,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/databases/{uuid}/stop':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Databases
|
||||
summary: Stop
|
||||
description: 'Stop database. `Post` request is also accepted.'
|
||||
description: 'Stop database.'
|
||||
operationId: stop-database-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -4708,11 +4708,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/databases/{uuid}/restart':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Databases
|
||||
summary: Restart
|
||||
description: 'Restart database. `Post` request is also accepted.'
|
||||
description: 'Restart database.'
|
||||
operationId: restart-database-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -5408,11 +5408,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
/deploy:
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Deployments
|
||||
summary: Deploy
|
||||
description: 'Deploy by tag or uuid. `Post` request also accepted with `uuid` and `tag` json body.'
|
||||
description: 'Deploy by tag or UUID using query parameters or a JSON body.'
|
||||
operationId: deploy-by-tag-or-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -6499,7 +6499,7 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
/enable:
|
||||
get:
|
||||
post:
|
||||
summary: 'Enable API'
|
||||
description: 'Enable API (only with root permissions).'
|
||||
operationId: enable-api
|
||||
|
|
@ -6528,7 +6528,7 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
/disable:
|
||||
get:
|
||||
post:
|
||||
summary: 'Disable API'
|
||||
description: 'Disable API (only with root permissions).'
|
||||
operationId: disable-api
|
||||
|
|
@ -7951,7 +7951,7 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/servers/{uuid}/validate':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Servers
|
||||
summary: Validate
|
||||
|
|
@ -7965,6 +7965,17 @@ paths:
|
|||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
install:
|
||||
description: 'Install missing prerequisites and Docker. This can restart the Docker daemon.'
|
||||
type: boolean
|
||||
default: false
|
||||
type: object
|
||||
responses:
|
||||
'201':
|
||||
description: 'Server validation started.'
|
||||
|
|
@ -8221,61 +8232,6 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/services/{uuid}/applications/{app_uuid}/start':
|
||||
get:
|
||||
tags:
|
||||
- 'Service applications'
|
||||
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.'
|
||||
operationId: start-service-application-by-service-and-app-uuid
|
||||
parameters:
|
||||
-
|
||||
name: uuid
|
||||
in: path
|
||||
description: 'Service UUID.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
-
|
||||
name: app_uuid
|
||||
in: path
|
||||
description: 'Service application UUID.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
-
|
||||
name: force
|
||||
in: query
|
||||
description: 'When true, passes --build to docker compose up.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
-
|
||||
name: latest
|
||||
in: query
|
||||
description: 'When true, pulls the image for this compose service before up.'
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
responses:
|
||||
'200':
|
||||
description: 'Deploy request queued.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
message: { type: string }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
'501':
|
||||
description: 'Swarm not supported.'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
post:
|
||||
tags:
|
||||
- 'Service applications'
|
||||
|
|
@ -8330,45 +8286,6 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/services/{uuid}/applications/{app_uuid}/restart':
|
||||
get:
|
||||
tags:
|
||||
- 'Service applications'
|
||||
summary: 'Restart service application container'
|
||||
description: 'Restarts a single compose service container (docker restart).'
|
||||
operationId: restart-service-application-by-service-and-app-uuid
|
||||
parameters:
|
||||
-
|
||||
name: uuid
|
||||
in: path
|
||||
description: 'Service UUID.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
-
|
||||
name: app_uuid
|
||||
in: path
|
||||
description: 'Service application UUID.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: 'Restart queued.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
message: { type: string }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
'501':
|
||||
description: 'Swarm not supported.'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
post:
|
||||
tags:
|
||||
- 'Service applications'
|
||||
|
|
@ -8409,45 +8326,6 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/services/{uuid}/applications/{app_uuid}/stop':
|
||||
get:
|
||||
tags:
|
||||
- 'Service applications'
|
||||
summary: 'Stop service application container'
|
||||
description: 'Stops a single compose service container (docker stop).'
|
||||
operationId: stop-service-application-by-service-and-app-uuid
|
||||
parameters:
|
||||
-
|
||||
name: uuid
|
||||
in: path
|
||||
description: 'Service UUID.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
-
|
||||
name: app_uuid
|
||||
in: path
|
||||
description: 'Service application UUID.'
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: 'Stop queued.'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
message: { type: string }
|
||||
type: object
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'404':
|
||||
$ref: '#/components/responses/404'
|
||||
'501':
|
||||
description: 'Swarm not supported.'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
post:
|
||||
tags:
|
||||
- 'Service applications'
|
||||
|
|
@ -9449,11 +9327,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/services/{uuid}/start':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Services
|
||||
summary: Start
|
||||
description: 'Start service. `Post` request is also accepted.'
|
||||
description: 'Start service.'
|
||||
operationId: start-service-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -9482,11 +9360,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/services/{uuid}/stop':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Services
|
||||
summary: Stop
|
||||
description: 'Stop service. `Post` request is also accepted.'
|
||||
description: 'Stop service.'
|
||||
operationId: stop-service-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
@ -9522,11 +9400,11 @@ paths:
|
|||
-
|
||||
bearerAuth: []
|
||||
'/services/{uuid}/restart':
|
||||
get:
|
||||
post:
|
||||
tags:
|
||||
- Services
|
||||
summary: Restart
|
||||
description: 'Restart service. `Post` request is also accepted.'
|
||||
description: 'Restart service.'
|
||||
operationId: restart-service-by-uuid
|
||||
parameters:
|
||||
-
|
||||
|
|
|
|||
|
|
@ -38,8 +38,10 @@
|
|||
'middleware' => ['auth:sanctum', 'api.token.team', 'api.ability:write'],
|
||||
'prefix' => 'v1',
|
||||
], function () {
|
||||
Route::get('/enable', [OtherController::class, 'enable_api']);
|
||||
Route::get('/disable', [OtherController::class, 'disable_api']);
|
||||
Route::get('/enable', [OtherController::class, 'post_required']);
|
||||
Route::get('/disable', [OtherController::class, 'post_required']);
|
||||
Route::post('/enable', [OtherController::class, 'enable_api']);
|
||||
Route::post('/disable', [OtherController::class, 'disable_api']);
|
||||
Route::post('/mcp/enable', [OtherController::class, 'enable_mcp']);
|
||||
Route::post('/mcp/disable', [OtherController::class, 'disable_mcp']);
|
||||
});
|
||||
|
|
@ -81,7 +83,8 @@
|
|||
Route::delete('/cloud-tokens/{uuid}', [CloudProviderTokensController::class, 'destroy'])->middleware(['api.ability:write']);
|
||||
Route::post('/cloud-tokens/{uuid}/validate', [CloudProviderTokensController::class, 'validateToken'])->middleware(['api.ability:write']);
|
||||
|
||||
Route::match(['get', 'post'], '/deploy', [DeployController::class, 'deploy'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/deploy', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/deploy', [DeployController::class, 'deploy'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/deployments', [DeployController::class, 'deployments'])->middleware(['api.ability:read']);
|
||||
Route::get('/deployments/{uuid}', [DeployController::class, 'deployment_by_uuid'])->middleware(['api.ability:read']);
|
||||
Route::post('/deployments/{uuid}/cancel', [DeployController::class, 'cancel_deployment'])->middleware(['api.ability:deploy']);
|
||||
|
|
@ -99,7 +102,8 @@
|
|||
Route::get('/servers/{server_uuid}/destinations', [DestinationsController::class, 'index_by_server'])->middleware(['api.ability:read']);
|
||||
Route::post('/servers/{server_uuid}/destinations', [DestinationsController::class, 'create'])->middleware(['api.ability:write']);
|
||||
|
||||
Route::get('/servers/{uuid}/validate', [ServersController::class, 'validate_server'])->middleware(['api.ability:write']);
|
||||
Route::get('/servers/{uuid}/validate', [OtherController::class, 'post_required'])->middleware(['api.ability:write']);
|
||||
Route::post('/servers/{uuid}/validate', [ServersController::class, 'validate_server'])->middleware(['api.ability:write']);
|
||||
|
||||
Route::post('/servers', [ServersController::class, 'create_server'])->middleware(['api.ability:write']);
|
||||
Route::patch('/servers/{uuid}', [ServersController::class, 'update_server'])->middleware(['api.ability:write']);
|
||||
|
|
@ -156,9 +160,12 @@
|
|||
Route::delete('/applications/{uuid}/tags/{tag_uuid}', [ApplicationsController::class, 'delete_tag'])->middleware(['api.ability:write']);
|
||||
|
||||
Route::post('/applications/{uuid}/move', [ApplicationsController::class, 'move_by_uuid'])->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']);
|
||||
Route::match(['get', 'post'], '/applications/{uuid}/stop', [ApplicationsController::class, 'action_stop'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/applications/{uuid}/start', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/applications/{uuid}/restart', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/applications/{uuid}/stop', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/applications/{uuid}/start', [ApplicationsController::class, 'action_deploy'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/applications/{uuid}/restart', [ApplicationsController::class, 'action_restart'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/applications/{uuid}/stop', [ApplicationsController::class, 'action_stop'])->middleware(['api.ability:deploy']);
|
||||
|
||||
Route::delete('/applications/{uuid}/previews/{pull_request_id}', [ApplicationsController::class, 'delete_preview_by_pull_request_id'])->middleware(['api.ability:write']);
|
||||
|
||||
|
|
@ -206,9 +213,12 @@
|
|||
Route::delete('/databases/{uuid}/tags/{tag_uuid}', [DatabasesController::class, 'delete_tag'])->middleware(['api.ability:write']);
|
||||
|
||||
Route::post('/databases/{uuid}/move', [DatabasesController::class, 'move_by_uuid'])->middleware(['api.ability:write']);
|
||||
Route::match(['get', 'post'], '/databases/{uuid}/start', [DatabasesController::class, 'action_deploy'])->middleware(['api.ability:deploy']);
|
||||
Route::match(['get', 'post'], '/databases/{uuid}/restart', [DatabasesController::class, 'action_restart'])->middleware(['api.ability:deploy']);
|
||||
Route::match(['get', 'post'], '/databases/{uuid}/stop', [DatabasesController::class, 'action_stop'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/databases/{uuid}/start', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/databases/{uuid}/restart', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/databases/{uuid}/stop', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/databases/{uuid}/start', [DatabasesController::class, 'action_deploy'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/databases/{uuid}/restart', [DatabasesController::class, 'action_restart'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/databases/{uuid}/stop', [DatabasesController::class, 'action_stop'])->middleware(['api.ability:deploy']);
|
||||
|
||||
Route::get('/services', [ServicesController::class, 'services'])->middleware(['api.ability:read']);
|
||||
Route::post('/services', [ServicesController::class, 'create_service'])->middleware(['api.ability:write']);
|
||||
|
|
@ -234,17 +244,23 @@
|
|||
Route::delete('/services/{uuid}/tags/{tag_uuid}', [ServicesController::class, 'delete_tag'])->middleware(['api.ability:write']);
|
||||
|
||||
Route::post('/services/{uuid}/move', [ServicesController::class, 'move_by_uuid'])->middleware(['api.ability:write']);
|
||||
Route::match(['get', 'post'], '/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware(['api.ability:deploy']);
|
||||
Route::match(['get', 'post'], '/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware(['api.ability:deploy']);
|
||||
Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/services/{uuid}/start', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/services/{uuid}/restart', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/services/{uuid}/stop', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware(['api.ability:deploy']);
|
||||
|
||||
Route::get('/services/{uuid}/applications', [ServiceApplicationsController::class, 'index'])->middleware(['api.ability:read']);
|
||||
Route::get('/services/{uuid}/applications/{app_uuid}', [ServiceApplicationsController::class, 'show'])->middleware(['api.ability:read']);
|
||||
Route::patch('/services/{uuid}/applications/{app_uuid}', [ServiceApplicationsController::class, 'update'])->middleware(['api.ability:write']);
|
||||
Route::match(['get', 'post'], '/services/{uuid}/applications/{app_uuid}/logs', [ServiceApplicationsController::class, 'logs_by_uuid'])->middleware(['api.ability:read']);
|
||||
Route::match(['get', 'post'], '/services/{uuid}/applications/{app_uuid}/start', [ServiceApplicationsController::class, 'action_start'])->middleware(['api.ability:deploy']);
|
||||
Route::match(['get', 'post'], '/services/{uuid}/applications/{app_uuid}/restart', [ServiceApplicationsController::class, 'action_restart'])->middleware(['api.ability:deploy']);
|
||||
Route::match(['get', 'post'], '/services/{uuid}/applications/{app_uuid}/stop', [ServiceApplicationsController::class, 'action_stop'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/services/{uuid}/applications/{app_uuid}/start', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/services/{uuid}/applications/{app_uuid}/restart', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::get('/services/{uuid}/applications/{app_uuid}/stop', [OtherController::class, 'post_required'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/services/{uuid}/applications/{app_uuid}/start', [ServiceApplicationsController::class, 'action_start'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/services/{uuid}/applications/{app_uuid}/restart', [ServiceApplicationsController::class, 'action_restart'])->middleware(['api.ability:deploy']);
|
||||
Route::post('/services/{uuid}/applications/{app_uuid}/stop', [ServiceApplicationsController::class, 'action_stop'])->middleware(['api.ability:deploy']);
|
||||
|
||||
Route::get('/services/{uuid}/databases', [ServiceDatabasesController::class, 'index'])->middleware(['api.ability:read']);
|
||||
Route::get('/services/{uuid}/databases/{database_uuid}', [ServiceDatabasesController::class, 'show'])->middleware(['api.ability:read']);
|
||||
|
|
|
|||
56
tests/Feature/Api/ServerValidationApiTest.php
Normal file
56
tests/Feature/Api/ServerValidationApiTest.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
use App\Actions\Server\ValidateServer;
|
||||
use App\Jobs\ValidateAndInstallServerJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::updateOrCreate(['id' => 0], ['is_api_enabled' => true]);
|
||||
|
||||
$this->team = Team::factory()->create();
|
||||
$this->user = User::factory()->create();
|
||||
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
|
||||
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->token = $this->user->createToken('server-validation', ['write'])->plainTextToken;
|
||||
|
||||
Queue::fake();
|
||||
});
|
||||
|
||||
function serverValidationHeaders(): array
|
||||
{
|
||||
return ['Authorization' => 'Bearer '.test()->token];
|
||||
}
|
||||
|
||||
it('validates without installing by default', function () {
|
||||
$response = $this->withHeaders(serverValidationHeaders())
|
||||
->postJson("/api/v1/servers/{$this->server->uuid}/validate");
|
||||
|
||||
$response->assertCreated()->assertJson(['message' => 'Validation started.']);
|
||||
Queue::assertNotPushed(ValidateAndInstallServerJob::class);
|
||||
});
|
||||
|
||||
it('validates and installs when explicitly requested', function () {
|
||||
$response = $this->withHeaders(serverValidationHeaders())
|
||||
->postJson("/api/v1/servers/{$this->server->uuid}/validate", ['install' => true]);
|
||||
|
||||
$response->assertCreated()->assertJson(['message' => 'Validation and installation started.']);
|
||||
Queue::assertPushed(
|
||||
ValidateAndInstallServerJob::class,
|
||||
fn (ValidateAndInstallServerJob $job): bool => $job->server->is($this->server)
|
||||
);
|
||||
Queue::assertNotPushed(ValidateServer::class);
|
||||
});
|
||||
|
||||
it('rejects an invalid install option', function () {
|
||||
$response = $this->withHeaders(serverValidationHeaders())
|
||||
->postJson("/api/v1/servers/{$this->server->uuid}/validate", ['install' => 'yes']);
|
||||
|
||||
$response->assertUnprocessable()->assertJsonValidationErrors('install');
|
||||
});
|
||||
43
tests/Feature/Api/StateChangingRoutesUsePostTest.php
Normal file
43
tests/Feature/Api/StateChangingRoutesUsePostTest.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\OtherController;
|
||||
use Illuminate\Routing\Route;
|
||||
|
||||
it('uses POST for state changes and keeps GET as a non-mutating compatibility response', function (string $uri) {
|
||||
$routes = collect(app('router')->getRoutes()->getRoutes())
|
||||
->filter(fn (Route $route): bool => $route->uri() === $uri);
|
||||
|
||||
$postRoute = $routes->first(fn (Route $route): bool => $route->methods() === ['POST']);
|
||||
$getRoute = $routes->first(fn (Route $route): bool => $route->methods() === ['GET', 'HEAD']);
|
||||
|
||||
expect($postRoute)->not->toBeNull()
|
||||
->and($getRoute)->not->toBeNull()
|
||||
->and($getRoute->getActionName())->toBe(OtherController::class.'@post_required');
|
||||
})->with([
|
||||
'enable API' => 'api/v1/enable',
|
||||
'disable API' => 'api/v1/disable',
|
||||
'deploy' => 'api/v1/deploy',
|
||||
'validate server' => 'api/v1/servers/{uuid}/validate',
|
||||
'start application' => 'api/v1/applications/{uuid}/start',
|
||||
'restart application' => 'api/v1/applications/{uuid}/restart',
|
||||
'stop application' => 'api/v1/applications/{uuid}/stop',
|
||||
'start database' => 'api/v1/databases/{uuid}/start',
|
||||
'restart database' => 'api/v1/databases/{uuid}/restart',
|
||||
'stop database' => 'api/v1/databases/{uuid}/stop',
|
||||
'start service' => 'api/v1/services/{uuid}/start',
|
||||
'restart service' => 'api/v1/services/{uuid}/restart',
|
||||
'stop service' => 'api/v1/services/{uuid}/stop',
|
||||
'start service application' => 'api/v1/services/{uuid}/applications/{app_uuid}/start',
|
||||
'restart service application' => 'api/v1/services/{uuid}/applications/{app_uuid}/restart',
|
||||
'stop service application' => 'api/v1/services/{uuid}/applications/{app_uuid}/stop',
|
||||
]);
|
||||
|
||||
it('tells GET callers to use POST without changing state', function () {
|
||||
$response = app(OtherController::class)->post_required();
|
||||
|
||||
expect($response->getStatusCode())->toBe(405)
|
||||
->and($response->headers->get('Allow'))->toBe('POST')
|
||||
->and($response->getData(true))->toBe([
|
||||
'message' => 'This endpoint has changed to a POST request.',
|
||||
]);
|
||||
});
|
||||
|
|
@ -77,13 +77,13 @@
|
|||
});
|
||||
});
|
||||
|
||||
describe('GET /api/v1/servers/{uuid}/validate', function () {
|
||||
describe('POST /api/v1/servers/{uuid}/validate', function () {
|
||||
test('read-only token cannot trigger server validation', function () {
|
||||
$token = $this->user->createToken('read-only', ['read']);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$token->plainTextToken,
|
||||
])->getJson('/api/v1/servers/fake-uuid/validate');
|
||||
])->postJson('/api/v1/servers/fake-uuid/validate');
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -397,7 +397,7 @@ function makeAuditApplication(string $repo = 'test-org/test-repo'): Application
|
|||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$token,
|
||||
])->getJson('/api/v1/enable');
|
||||
])->postJson('/api/v1/enable');
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
|
|
|
|||
19
tests/Unit/ServerValidationOpenApiTest.php
Normal file
19
tests/Unit/ServerValidationOpenApiTest.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
it('documents optional installation during server validation', function () {
|
||||
$openApi = json_decode(
|
||||
file_get_contents(__DIR__.'/../../openapi.json'),
|
||||
true,
|
||||
flags: JSON_THROW_ON_ERROR,
|
||||
);
|
||||
|
||||
$operation = $openApi['paths']['/servers/{uuid}/validate']['post'];
|
||||
$install = $operation['requestBody']['content']['application/json']['schema']['properties']['install'];
|
||||
|
||||
expect($install)
|
||||
->toMatchArray([
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
])
|
||||
->and($install['description'])->toContain('restart the Docker daemon');
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
it('documents both supported methods for service application actions', function () {
|
||||
it('documents POST for service application actions', function () {
|
||||
$openApi = json_decode(
|
||||
file_get_contents(__DIR__.'/../../openapi.json'),
|
||||
true,
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
);
|
||||
|
||||
$actionPaths = [
|
||||
'/services/{uuid}/applications/{app_uuid}/logs',
|
||||
'/services/{uuid}/applications/{app_uuid}/start',
|
||||
'/services/{uuid}/applications/{app_uuid}/restart',
|
||||
'/services/{uuid}/applications/{app_uuid}/stop',
|
||||
|
|
@ -16,17 +15,16 @@
|
|||
|
||||
foreach ($actionPaths as $path) {
|
||||
expect($openApi['paths'][$path])
|
||||
->toHaveKeys(['get', 'post'])
|
||||
->toHaveKey('post')
|
||||
->not->toHaveKey('get')
|
||||
->and($openApi['paths'][$path]['post']['responses'])
|
||||
->toHaveKeys(['200', '400', '401', '404', '501']);
|
||||
}
|
||||
|
||||
expect($openApi['paths'][$actionPaths[0]]['post']['responses']['200']['content']['application/json']['schema']['properties'])
|
||||
->toHaveKey('logs')
|
||||
->toHaveKey('message')
|
||||
->and($openApi['paths'][$actionPaths[1]]['post']['responses']['200']['content']['application/json']['schema']['properties'])
|
||||
->toHaveKey('message')
|
||||
->and($openApi['paths'][$actionPaths[2]]['post']['responses']['200']['content']['application/json']['schema']['properties'])
|
||||
->toHaveKey('message')
|
||||
->and($openApi['paths'][$actionPaths[3]]['post']['responses']['200']['content']['application/json']['schema']['properties'])
|
||||
->toHaveKey('message');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue