Merge remote-tracking branch 'origin/next' into volume-backups-server-s3
This commit is contained in:
commit
b97a97e955
38 changed files with 379 additions and 651 deletions
|
|
@ -30,6 +30,14 @@ DB_PORT=5432
|
|||
# Enable Laravel Telescope for debugging
|
||||
TELESCOPE_ENABLED=false
|
||||
|
||||
# Enable Laravel Debugbar (disabled by default; set true when needed)
|
||||
DEBUGBAR_ENABLED=false
|
||||
|
||||
# Vite dev server. Defaults to localhost. For phone/LAN/Tailscale access, set to
|
||||
# the host machine's reachable IP (e.g. VITE_HOST=100.75.155.70), then recreate vite.
|
||||
VITE_HOST=localhost
|
||||
VITE_PORT=5173
|
||||
|
||||
# Enable Laravel Nightwatch monitoring
|
||||
NIGHTWATCH_ENABLED=false
|
||||
NIGHTWATCH_TOKEN=
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
|
|
|
|||
0
bootstrap/cache/.gitignore
vendored
Normal file → Executable file
0
bootstrap/cache/.gitignore
vendored
Normal file → Executable file
|
|
@ -96,6 +96,7 @@ services:
|
|||
container_name: coolify-vite
|
||||
working_dir: /var/www/html
|
||||
environment:
|
||||
# Set VITE_HOST in .env to a browser-reachable IP/hostname for LAN/Tailscale access
|
||||
VITE_HOST: "${VITE_HOST:-localhost}"
|
||||
VITE_PORT: "${VITE_PORT:-5173}"
|
||||
ports:
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ services:
|
|||
container_name: coolify-vite
|
||||
working_dir: /var/www/html
|
||||
environment:
|
||||
# Set VITE_HOST in .env to a browser-reachable IP/hostname for LAN/Tailscale access
|
||||
VITE_HOST: "${VITE_HOST:-localhost}"
|
||||
VITE_PORT: "${VITE_PORT:-5173}"
|
||||
ports:
|
||||
|
|
|
|||
|
|
@ -98,5 +98,8 @@ RUN mkdir -p /etc/nginx/conf.d && \
|
|||
COPY --from=minio-client /usr/bin/mc /usr/bin/mc
|
||||
RUN chmod +x /usr/bin/mc
|
||||
|
||||
# Switch to non-root user
|
||||
USER www-data
|
||||
# Stay as root for s6 init so bind-mounted workspaces work even when the host
|
||||
# tree is root-owned (CI, Jean, rootful Docker). PHP-FPM/nginx still run as
|
||||
# www-data via their service configs. Do NOT set USER www-data here — that
|
||||
# makes init-setup (composer install) fail with "vendor could not be created".
|
||||
USER root
|
||||
|
|
|
|||
22
docker/development/etc/s6-overlay/s6-rc.d/init-setup/up
Normal file → Executable file
22
docker/development/etc/s6-overlay/s6-rc.d/init-setup/up
Normal file → Executable file
|
|
@ -1,22 +1,6 @@
|
|||
#!/command/execlineb -P
|
||||
|
||||
# Use with-contenv to ensure environment variables are available
|
||||
# s6 oneshots are execline pipelines (shebang shell scripts are not run as sh).
|
||||
# Delegate to a real shell script for readable setup logic.
|
||||
with-contenv
|
||||
cd /var/www/html
|
||||
foreground {
|
||||
composer
|
||||
install
|
||||
}
|
||||
foreground {
|
||||
php
|
||||
artisan
|
||||
migrate
|
||||
--step
|
||||
}
|
||||
foreground {
|
||||
php
|
||||
artisan
|
||||
dev
|
||||
--init
|
||||
}
|
||||
|
||||
/etc/s6-overlay/scripts/init-setup.sh
|
||||
|
|
|
|||
42
docker/development/etc/s6-overlay/scripts/init-setup.sh
Executable file
42
docker/development/etc/s6-overlay/scripts/init-setup.sh
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
#!/command/with-contenv sh
|
||||
set -eu
|
||||
|
||||
cd /var/www/html
|
||||
|
||||
# When the project is bind-mounted, host ownership may be root:root while the
|
||||
# app runs as www-data (UID 1000). Fix the minimum needed so composer/laravel
|
||||
# can write, without a recursive chown of the whole tree.
|
||||
prepare_bind_mount() {
|
||||
mkdir -p \
|
||||
storage/framework/cache \
|
||||
storage/framework/sessions \
|
||||
storage/framework/views \
|
||||
storage/logs \
|
||||
bootstrap/cache
|
||||
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
# Top-level only: allows creating vendor/ when the mount is root-owned
|
||||
chown www-data:www-data /var/www/html 2>/dev/null || true
|
||||
chown -R www-data:www-data storage bootstrap/cache 2>/dev/null || true
|
||||
git config --global --add safe.directory /var/www/html 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_bind_mount
|
||||
|
||||
echo "👉 init-setup: composer install..."
|
||||
# Run as root when needed so a root-owned bind mount cannot block vendor/
|
||||
# creation, then hand writable paths to www-data for PHP-FPM.
|
||||
composer install --no-interaction
|
||||
|
||||
echo "👉 init-setup: migrate..."
|
||||
php artisan migrate --step --force
|
||||
|
||||
echo "👉 init-setup: artisan dev --init..."
|
||||
php artisan dev --init
|
||||
|
||||
if [ "$(id -u)" = "0" ]; then
|
||||
chown -R www-data:www-data vendor storage bootstrap/cache 2>/dev/null || true
|
||||
fi
|
||||
|
||||
echo "👉 init-setup: done"
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"scripts": {
|
||||
"setup": "cp $JEAN_ROOT_PATH/.env . && mkdir -p .claude && cp $JEAN_ROOT_PATH/.claude/settings.local.json .claude/settings.local.json",
|
||||
"setup": "cp $JEAN_ROOT_PATH/.env .",
|
||||
"teardown": null,
|
||||
"run": "docker rm -f coolify coolify-minio-init coolify-realtime coolify-minio coolify-testing-host coolify-redis coolify-db coolify-mail coolify-vite; spin up; spin down"
|
||||
"run": "docker rm -f coolify coolify-minio-init coolify-realtime coolify-minio coolify-testing-host coolify-redis coolify-db coolify-mail coolify-vite; docker compose -f docker-compose.yml -f docker-compose.dev.yml up; docker compose -f docker-compose.yml -f docker-compose.dev.yml down"
|
||||
},
|
||||
"ports": [
|
||||
{
|
||||
|
|
|
|||
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:
|
||||
-
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.1.2"
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.2.0"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --host",
|
||||
"build": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -39,8 +39,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']);
|
||||
});
|
||||
|
|
@ -82,7 +84,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']);
|
||||
|
|
@ -100,7 +103,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']);
|
||||
|
|
@ -163,9 +167,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']);
|
||||
|
||||
|
|
@ -219,9 +226,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']);
|
||||
|
|
@ -253,17 +263,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']);
|
||||
|
|
|
|||
0
storage/app/.gitignore
vendored
Normal file → Executable file
0
storage/app/.gitignore
vendored
Normal file → Executable file
0
storage/app/public/.gitignore
vendored
Normal file → Executable file
0
storage/app/public/.gitignore
vendored
Normal file → Executable file
0
storage/debugbar/.gitignore
vendored
Normal file → Executable file
0
storage/debugbar/.gitignore
vendored
Normal file → Executable file
0
storage/framework/.gitignore
vendored
Normal file → Executable file
0
storage/framework/.gitignore
vendored
Normal file → Executable file
0
storage/framework/cache/.gitignore
vendored
Normal file → Executable file
0
storage/framework/cache/.gitignore
vendored
Normal file → Executable file
0
storage/framework/sessions/.gitignore
vendored
Normal file → Executable file
0
storage/framework/sessions/.gitignore
vendored
Normal file → Executable file
0
storage/framework/testing/.gitignore
vendored
Normal file → Executable file
0
storage/framework/testing/.gitignore
vendored
Normal file → Executable file
0
storage/framework/views/.gitignore
vendored
Normal file → Executable file
0
storage/framework/views/.gitignore
vendored
Normal file → Executable file
0
storage/logs/.gitignore
vendored
Normal file → Executable file
0
storage/logs/.gitignore
vendored
Normal file → Executable file
0
storage/pail/.gitignore
vendored
Normal file → Executable file
0
storage/pail/.gitignore
vendored
Normal file → Executable file
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');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.1.2"
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.2.0"
|
||||
|
|
|
|||
|
|
@ -2,33 +2,30 @@ import { defineConfig, loadEnv } from "vite";
|
|||
import laravel from "laravel-vite-plugin";
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), '')
|
||||
const viteHost = env.VITE_HOST || null;
|
||||
const vitePort = Number(env.VITE_PORT || 5173);
|
||||
const env = loadEnv(mode, process.cwd(), "");
|
||||
// Prefer process.env so Docker Compose can override without writing .env.
|
||||
// Set VITE_HOST to a browser-reachable hostname/IP when accessing the app
|
||||
// from another device (LAN / Tailscale), e.g. VITE_HOST=100.75.155.70
|
||||
const viteHost = (process.env.VITE_HOST || env.VITE_HOST || "localhost").trim();
|
||||
const vitePort = Number(process.env.VITE_PORT || env.VITE_PORT || 5173);
|
||||
|
||||
return {
|
||||
server: {
|
||||
watch: {
|
||||
ignored: [
|
||||
"**/dev_*_data/**",
|
||||
"**/storage/**",
|
||||
],
|
||||
ignored: ["**/dev_*_data/**", "**/storage/**"],
|
||||
},
|
||||
// Listen on all interfaces so Docker / remote clients can reach the dev server
|
||||
host: "0.0.0.0",
|
||||
port: vitePort,
|
||||
strictPort: true,
|
||||
allowedHosts: true,
|
||||
cors: {
|
||||
origin: [
|
||||
/^https?:\/\/localhost(:\d+)?$/,
|
||||
/^https?:\/\/127\.0\.0\.1(:\d+)?$/,
|
||||
/^https?:\/\/\[::1\](:\d+)?$/,
|
||||
...(env.APP_URL ? [env.APP_URL] : []),
|
||||
...(viteHost ? [`http://${viteHost}:${vitePort}`, `https://${viteHost}:${vitePort}`] : []),
|
||||
],
|
||||
// App (:8000) and Vite (:5173) are different origins; allow any host in dev
|
||||
cors: true,
|
||||
origin: `http://${viteHost}:${vitePort}`,
|
||||
hmr: {
|
||||
host: viteHost,
|
||||
clientPort: vitePort,
|
||||
},
|
||||
origin: viteHost ? `http://${viteHost}:${vitePort}` : undefined,
|
||||
hmr: viteHost
|
||||
? { host: viteHost, clientPort: vitePort }
|
||||
: true,
|
||||
},
|
||||
plugins: [
|
||||
laravel({
|
||||
|
|
@ -36,5 +33,5 @@ export default defineConfig(({ mode }) => {
|
|||
refresh: true,
|
||||
}),
|
||||
],
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue