Expose the running Coolify version on /api/health and only treat an upgrade as complete once that version meets the target. Parse upgrade status in a shared service, start the update after the Livewire response, and keep polling when the instance is still on the old version.
328 lines
11 KiB
PHP
328 lines
11 KiB
PHP
<?php
|
|
|
|
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.',
|
|
path: '/version',
|
|
operationId: 'version',
|
|
security: [
|
|
['bearerAuth' => []],
|
|
],
|
|
responses: [
|
|
new OA\Response(
|
|
response: 200,
|
|
description: 'Returns the version of the application',
|
|
content: new OA\MediaType(
|
|
mediaType: 'text/html',
|
|
schema: new OA\Schema(type: 'string'),
|
|
example: 'v4.0.0',
|
|
)),
|
|
new OA\Response(
|
|
response: 401,
|
|
ref: '#/components/responses/401',
|
|
),
|
|
new OA\Response(
|
|
response: 400,
|
|
ref: '#/components/responses/400',
|
|
),
|
|
]
|
|
)]
|
|
public function version(Request $request)
|
|
{
|
|
return response(config('constants.coolify.version'));
|
|
}
|
|
|
|
#[OA\Post(
|
|
summary: 'Enable API',
|
|
description: 'Enable API (only with root permissions).',
|
|
path: '/enable',
|
|
operationId: 'enable-api',
|
|
security: [
|
|
['bearerAuth' => []],
|
|
],
|
|
responses: [
|
|
new OA\Response(
|
|
response: 200,
|
|
description: 'Enable API.',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
properties: [
|
|
new OA\Property(property: 'message', type: 'string', example: 'API enabled.'),
|
|
]
|
|
)),
|
|
new OA\Response(
|
|
response: 403,
|
|
description: 'You are not allowed to enable the API.',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
properties: [
|
|
new OA\Property(property: 'message', type: 'string', example: 'You are not allowed to enable the API.'),
|
|
]
|
|
)),
|
|
new OA\Response(
|
|
response: 401,
|
|
ref: '#/components/responses/401',
|
|
),
|
|
new OA\Response(
|
|
response: 400,
|
|
ref: '#/components/responses/400',
|
|
),
|
|
]
|
|
)]
|
|
public function enable_api(Request $request)
|
|
{
|
|
$teamId = getTeamIdFromToken();
|
|
if (is_null($teamId)) {
|
|
return invalidTokenResponse();
|
|
}
|
|
if ($teamId !== '0') {
|
|
auditLog('api.instance.enable_denied', ['team_id' => $teamId], 'warning');
|
|
|
|
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
|
|
}
|
|
$settings = instanceSettings();
|
|
$settings->update(['is_api_enabled' => true]);
|
|
|
|
auditLog('api.instance.enabled', ['team_id' => $teamId]);
|
|
|
|
return response()->json(['message' => 'API enabled.'], 200);
|
|
}
|
|
|
|
#[OA\Post(
|
|
summary: 'Disable API',
|
|
description: 'Disable API (only with root permissions).',
|
|
path: '/disable',
|
|
operationId: 'disable-api',
|
|
security: [
|
|
['bearerAuth' => []],
|
|
],
|
|
responses: [
|
|
new OA\Response(
|
|
response: 200,
|
|
description: 'Disable API.',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
properties: [
|
|
new OA\Property(property: 'message', type: 'string', example: 'API disabled.'),
|
|
]
|
|
)),
|
|
new OA\Response(
|
|
response: 403,
|
|
description: 'You are not allowed to disable the API.',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
properties: [
|
|
new OA\Property(property: 'message', type: 'string', example: 'You are not allowed to disable the API.'),
|
|
]
|
|
)),
|
|
new OA\Response(
|
|
response: 401,
|
|
ref: '#/components/responses/401',
|
|
),
|
|
new OA\Response(
|
|
response: 400,
|
|
ref: '#/components/responses/400',
|
|
),
|
|
]
|
|
)]
|
|
public function disable_api(Request $request)
|
|
{
|
|
$teamId = getTeamIdFromToken();
|
|
if (is_null($teamId)) {
|
|
return invalidTokenResponse();
|
|
}
|
|
if ($teamId !== '0') {
|
|
auditLog('api.instance.disable_denied', ['team_id' => $teamId], 'warning');
|
|
|
|
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
|
|
}
|
|
$settings = instanceSettings();
|
|
$settings->update(['is_api_enabled' => false]);
|
|
|
|
auditLog('api.instance.disabled', ['team_id' => $teamId]);
|
|
|
|
return response()->json(['message' => 'API disabled.'], 200);
|
|
}
|
|
|
|
#[OA\Post(
|
|
summary: 'Enable MCP Server',
|
|
description: 'Enable the MCP server endpoint at /mcp (only with root permissions).',
|
|
path: '/mcp/enable',
|
|
operationId: 'enable-mcp',
|
|
security: [
|
|
['bearerAuth' => []],
|
|
],
|
|
responses: [
|
|
new OA\Response(
|
|
response: 200,
|
|
description: 'MCP server enabled.',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
properties: [
|
|
new OA\Property(property: 'message', type: 'string', example: 'MCP server enabled.'),
|
|
]
|
|
)),
|
|
new OA\Response(
|
|
response: 403,
|
|
description: 'You are not allowed to enable the MCP server.',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
properties: [
|
|
new OA\Property(property: 'message', type: 'string', example: 'You are not allowed to enable the MCP server.'),
|
|
]
|
|
)),
|
|
new OA\Response(
|
|
response: 401,
|
|
ref: '#/components/responses/401',
|
|
),
|
|
new OA\Response(
|
|
response: 400,
|
|
ref: '#/components/responses/400',
|
|
),
|
|
]
|
|
)]
|
|
public function enable_mcp(Request $request)
|
|
{
|
|
$teamId = getTeamIdFromToken();
|
|
if (is_null($teamId)) {
|
|
return invalidTokenResponse();
|
|
}
|
|
if ($teamId !== '0') {
|
|
auditLog('api.mcp.enable_denied', ['team_id' => $teamId], 'warning');
|
|
|
|
return response()->json(['message' => 'You are not allowed to enable the MCP server.'], 403);
|
|
}
|
|
$settings = instanceSettings();
|
|
$settings->update(['is_mcp_server_enabled' => true]);
|
|
|
|
auditLog('api.mcp.enabled', ['team_id' => $teamId]);
|
|
|
|
return response()->json(['message' => 'MCP server enabled.'], 200);
|
|
}
|
|
|
|
#[OA\Post(
|
|
summary: 'Disable MCP Server',
|
|
description: 'Disable the MCP server endpoint at /mcp (only with root permissions).',
|
|
path: '/mcp/disable',
|
|
operationId: 'disable-mcp',
|
|
security: [
|
|
['bearerAuth' => []],
|
|
],
|
|
responses: [
|
|
new OA\Response(
|
|
response: 200,
|
|
description: 'MCP server disabled.',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
properties: [
|
|
new OA\Property(property: 'message', type: 'string', example: 'MCP server disabled.'),
|
|
]
|
|
)),
|
|
new OA\Response(
|
|
response: 403,
|
|
description: 'You are not allowed to disable the MCP server.',
|
|
content: new OA\JsonContent(
|
|
type: 'object',
|
|
properties: [
|
|
new OA\Property(property: 'message', type: 'string', example: 'You are not allowed to disable the MCP server.'),
|
|
]
|
|
)),
|
|
new OA\Response(
|
|
response: 401,
|
|
ref: '#/components/responses/401',
|
|
),
|
|
new OA\Response(
|
|
response: 400,
|
|
ref: '#/components/responses/400',
|
|
),
|
|
]
|
|
)]
|
|
public function disable_mcp(Request $request)
|
|
{
|
|
$teamId = getTeamIdFromToken();
|
|
if (is_null($teamId)) {
|
|
return invalidTokenResponse();
|
|
}
|
|
if ($teamId !== '0') {
|
|
auditLog('api.mcp.disable_denied', ['team_id' => $teamId], 'warning');
|
|
|
|
return response()->json(['message' => 'You are not allowed to disable the MCP server.'], 403);
|
|
}
|
|
$settings = instanceSettings();
|
|
$settings->update(['is_mcp_server_enabled' => false]);
|
|
|
|
auditLog('api.mcp.disabled', ['team_id' => $teamId]);
|
|
|
|
return response()->json(['message' => 'MCP server disabled.'], 200);
|
|
}
|
|
|
|
public function feedback(Request $request)
|
|
{
|
|
$data = $request->validate([
|
|
'content' => ['required', 'string', 'min:10', 'max:2000'],
|
|
]);
|
|
|
|
$webhook_url = config('constants.webhooks.feedback_discord_webhook');
|
|
if ($webhook_url) {
|
|
Http::timeout(5)->post($webhook_url, [
|
|
'content' => $data['content'],
|
|
'allowed_mentions' => ['parse' => []],
|
|
]);
|
|
}
|
|
|
|
return response()->json(['message' => 'Feedback sent.'], 200);
|
|
}
|
|
|
|
#[OA\Get(
|
|
summary: 'Healthcheck',
|
|
description: 'Healthcheck endpoint. Includes the running Coolify version in the X-Coolify-Version header.',
|
|
path: '/health',
|
|
operationId: 'healthcheck',
|
|
responses: [
|
|
new OA\Response(
|
|
response: 200,
|
|
description: 'Healthcheck endpoint.',
|
|
headers: [
|
|
new OA\Header(
|
|
header: 'X-Coolify-Version',
|
|
description: 'Currently running Coolify version.',
|
|
schema: new OA\Schema(type: 'string', example: '4.3.1'),
|
|
),
|
|
],
|
|
content: new OA\MediaType(
|
|
mediaType: 'text/html',
|
|
schema: new OA\Schema(type: 'string'),
|
|
example: 'OK',
|
|
)),
|
|
new OA\Response(
|
|
response: 401,
|
|
ref: '#/components/responses/401',
|
|
),
|
|
new OA\Response(
|
|
response: 400,
|
|
ref: '#/components/responses/400',
|
|
),
|
|
]
|
|
)]
|
|
public function healthcheck(Request $request)
|
|
{
|
|
return response('OK')->header('X-Coolify-Version', (string) config('constants.coolify.version'));
|
|
}
|
|
}
|