diff --git a/app/Enums/NewDatabaseTypes.php b/app/Enums/NewDatabaseTypes.php
new file mode 100644
index 000000000..3563146ff
--- /dev/null
+++ b/app/Enums/NewDatabaseTypes.php
@@ -0,0 +1,15 @@
+user()->id ?? null;
}
if (is_null($userId)) {
- throw new \Exception('User id is null');
+ throw new \RuntimeException('User id is null');
}
$this->userId = $userId;
}
diff --git a/app/Http/Controllers/Api/Applications.php b/app/Http/Controllers/Api/ApplicationsController.php
similarity index 72%
rename from app/Http/Controllers/Api/Applications.php
rename to app/Http/Controllers/Api/ApplicationsController.php
index c73b894ba..e37de0378 100644
--- a/app/Http/Controllers/Api/Applications.php
+++ b/app/Http/Controllers/Api/ApplicationsController.php
@@ -13,47 +13,45 @@
use App\Models\PrivateKey;
use App\Models\Project;
use App\Models\Server;
+use App\Models\Service;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
+use Symfony\Component\Yaml\Yaml;
use Visus\Cuid2\Cuid2;
-class Applications extends Controller
+class ApplicationsController extends Controller
{
public function applications(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$projects = Project::where('team_id', $teamId)->get();
$applications = collect();
$applications->push($projects->pluck('applications')->flatten());
$applications = $applications->flatten();
+ $applications = $applications->map(function ($application) {
+ return serializeApiResponse($application);
+ });
- return response()->json(serialize_api_response($applications));
+ return response()->json([
+ 'success' => true,
+ 'data' => $applications,
+ ]);
}
public function create_application(Request $request)
{
-
- ray()->clearAll();
$allowedFields = ['project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'type', 'name', 'description', 'is_static', 'domains', 'git_repository', 'git_branch', 'git_commit_sha', 'docker_registry_image_name', 'docker_registry_image_tag', 'build_pack', 'install_command', 'build_command', 'start_command', 'ports_exposes', 'ports_mappings', 'base_directory', 'publish_directory', 'health_check_enabled', 'health_check_path', 'health_check_port', 'health_check_host', 'health_check_method', 'health_check_return_code', 'health_check_scheme', 'health_check_response_text', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'custom_labels', 'custom_docker_run_options', 'post_deployment_command', 'post_deployment_command_container', 'pre_deployment_command', 'pre_deployment_command_container', 'manual_webhook_secret_github', 'manual_webhook_secret_gitlab', 'manual_webhook_secret_bitbucket', 'manual_webhook_secret_gitea', 'redirect', 'github_app_uuid', 'instant_deploy', 'dockerfile'];
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
- if (! $request->isJson()) {
- return response()->json([
- 'message' => 'Invalid request.',
- 'error' => 'Content-Type must be application/json.',
- ], 400);
- }
- // check if request is valid json
- if (! json_decode($request->getContent())) {
- return response()->json([
- 'message' => 'Invalid request.',
- 'error' => 'Invalid JSON.',
- ], 400);
+
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
}
$validator = customApiValidator($request->all(), [
'name' => 'string|max:255',
@@ -75,6 +73,7 @@ public function create_application(Request $request)
}
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $errors,
], 422);
@@ -88,22 +87,22 @@ public function create_application(Request $request)
$project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
if (! $project) {
- return response()->json(['error' => 'Project not found.'], 404);
+ return response()->json(['succes' => false, 'message' => 'Project not found.'], 404);
}
$environment = $project->environments()->where('name', $request->environment_name)->first();
if (! $environment) {
- return response()->json(['error' => 'Environment not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Environment not found.'], 404);
}
$server = Server::whereTeamId($teamId)->whereUuid($serverUuid)->first();
if (! $server) {
- return response()->json(['error' => 'Server not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Server not found.'], 404);
}
$destinations = $server->destinations();
if ($destinations->count() == 0) {
- return response()->json(['error' => 'Server has no destinations.'], 400);
+ return response()->json(['success' => false, 'message' => 'Server has no destinations.'], 400);
}
if ($destinations->count() > 1 && ! $request->has('destination_uuid')) {
- return response()->json(['error' => 'Server has multiple destinations and you do not set destination_uuid.'], 400);
+ return response()->json(['success' => false, 'message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400);
}
$destination = $destinations->first();
if ($type === 'public') {
@@ -114,11 +113,12 @@ public function create_application(Request $request)
sharedDataApplications(),
'git_repository' => 'string|required',
'git_branch' => 'string|required',
- 'build_pack' => ['required', Rule::enum(BuildPackTypes::class)],
+ 'build_pack' => [Rule::enum(BuildPackTypes::class)],
'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required',
]);
if ($validator->fails()) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
@@ -128,7 +128,7 @@ public function create_application(Request $request)
return $return;
}
$application = new Application();
- $this->removeUnnecessaryFieldsFromRequest($request);
+ removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all());
@@ -149,7 +149,10 @@ public function create_application(Request $request)
);
}
- return response()->json(serialize_api_response($application));
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($application),
+ ]);
} elseif ($type === 'private-gh-app') {
if (! $request->has('name')) {
$request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch));
@@ -164,6 +167,7 @@ public function create_application(Request $request)
]);
if ($validator->fails()) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
@@ -174,14 +178,14 @@ public function create_application(Request $request)
}
$githubApp = GithubApp::whereTeamId($teamId)->where('uuid', $githubAppUuid)->first();
if (! $githubApp) {
- return response()->json(['error' => 'Github App not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Github App not found.'], 404);
}
$gitRepository = $request->git_repository;
if (str($gitRepository)->startsWith('http') || str($gitRepository)->contains('github.com')) {
$gitRepository = str($gitRepository)->replace('https://', '')->replace('http://', '')->replace('github.com/', '');
}
$application = new Application();
- $this->removeUnnecessaryFieldsFromRequest($request);
+ removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all());
@@ -205,7 +209,10 @@ public function create_application(Request $request)
);
}
- return response()->json(serialize_api_response($application));
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($application),
+ ]);
} elseif ($type === 'private-deploy-key') {
if (! $request->has('name')) {
$request->offsetSet('name', generate_application_name($request->git_repository, $request->git_branch));
@@ -230,11 +237,11 @@ public function create_application(Request $request)
}
$privateKey = PrivateKey::whereTeamId($teamId)->where('uuid', $request->private_key_uuid)->first();
if (! $privateKey) {
- return response()->json(['error' => 'Private Key not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Private Key not found.'], 404);
}
$application = new Application();
- $this->removeUnnecessaryFieldsFromRequest($request);
+ removeUnnecessaryFieldsFromRequest($request);
$application->fill($request->all());
$application->fqdn = $fqdn;
@@ -255,7 +262,10 @@ public function create_application(Request $request)
);
}
- return response()->json(serialize_api_response($application));
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($application),
+ ]);
} elseif ($type === 'dockerfile') {
if (! $request->has('name')) {
$request->offsetSet('name', 'dockerfile-'.new Cuid2(7));
@@ -266,6 +276,7 @@ public function create_application(Request $request)
]);
if ($validator->fails()) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
@@ -276,6 +287,7 @@ public function create_application(Request $request)
}
if (! isBase64Encoded($request->dockerfile)) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => [
'dockerfile' => 'The dockerfile should be base64 encoded.',
@@ -285,6 +297,7 @@ public function create_application(Request $request)
$dockerFile = base64_decode($request->dockerfile);
if (mb_detect_encoding($dockerFile, 'ASCII', true) === false) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => [
'dockerfile' => 'The dockerfile should be base64 encoded.',
@@ -292,7 +305,7 @@ public function create_application(Request $request)
], 422);
}
$dockerFile = base64_decode($request->dockerfile);
- $this->removeUnnecessaryFieldsFromRequest($request);
+ removeUnnecessaryFieldsFromRequest($request);
$port = get_port_from_dockerfile($request->dockerfile);
if (! $port) {
@@ -324,42 +337,179 @@ public function create_application(Request $request)
);
}
- return response()->json(serialize_api_response($application));
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($application),
+ ]);
+ } elseif ($type === 'docker-image') {
+ if (! $request->has('name')) {
+ $request->offsetSet('name', 'docker-image-'.new Cuid2(7));
+ }
+ $validator = customApiValidator($request->all(), [
+ sharedDataApplications(),
+ 'docker_registry_image_name' => 'string|required',
+ 'docker_registry_image_tag' => 'string',
+ 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required',
+ ]);
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validation failed.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+ $return = $this->validateDataApplications($request, $server);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+ if (! $request->docker_registry_image_tag) {
+ $request->offsetSet('docker_registry_image_tag', 'latest');
+ }
+ $application = new Application();
+ removeUnnecessaryFieldsFromRequest($request);
+
+ $application->fill($request->all());
+ $application->fqdn = $fqdn;
+ $application->build_pack = 'dockerimage';
+ $application->destination_id = $destination->id;
+ $application->destination_type = $destination->getMorphClass();
+ $application->environment_id = $environment->id;
+
+ $application->git_repository = 'coollabsio/coolify';
+ $application->git_branch = 'main';
+ $application->save();
+
+ if ($instantDeploy) {
+ $deployment_uuid = new Cuid2(7);
+
+ queue_application_deployment(
+ application: $application,
+ deployment_uuid: $deployment_uuid,
+ no_questions_asked: true,
+ is_api: true,
+ );
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($application),
+ ]);
+ } elseif ($type === 'docker-compose-empty') {
+ if (! $request->has('name')) {
+ $request->offsetSet('name', 'service'.new Cuid2(7));
+ }
+ $validator = customApiValidator($request->all(), [
+ sharedDataApplications(),
+ 'docker_compose' => 'string|required',
+ 'ports_exposes' => 'string|regex:/^(\d+)(,\d+)*$/|required',
+ ]);
+ if ($validator->fails()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validation failed.',
+ 'errors' => $validator->errors(),
+ ], 422);
+ }
+ $return = $this->validateDataApplications($request, $server);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+ if (! isBase64Encoded($request->docker_compose)) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validation failed.',
+ 'errors' => [
+ 'docker_compose' => 'The docker_compose should be base64 encoded.',
+ ],
+ ], 422);
+ }
+ $dockerCompose = base64_decode($request->docker_compose);
+ if (mb_detect_encoding($dockerCompose, 'ASCII', true) === false) {
+ return response()->json([
+ 'message' => 'Validation failed.',
+ 'errors' => [
+ 'docker_compose' => 'The docker_compose should be base64 encoded.',
+ ],
+ ], 422);
+ }
+ $dockerCompose = base64_decode($request->docker_compose);
+ $dockerComposeRaw = Yaml::dump(Yaml::parse($dockerCompose), 10, 2, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK);
+
+ // $isValid = validateComposeFile($dockerComposeRaw, $server_id);
+ // if ($isValid !== 'OK') {
+ // return $this->dispatch('error', "Invalid docker-compose file.\n$isValid");
+ // }
+
+ $service = new Service();
+ removeUnnecessaryFieldsFromRequest($request);
+ $service->name = $request->name;
+ $service->description = $request->description;
+ $service->docker_compose_raw = $dockerComposeRaw;
+ $service->environment_id = $environment->id;
+ $service->server_id = $server->id;
+ $service->destination_id = $destination->id;
+ $service->destination_type = $destination->getMorphClass();
+ $service->save();
+
+ $service->name = "service-$service->uuid";
+ $service->parse(isNew: true);
+ // if ($instantDeploy) {
+ // $deployment_uuid = new Cuid2(7);
+
+ // queue_application_deployment(
+ // application: $application,
+ // deployment_uuid: $deployment_uuid,
+ // no_questions_asked: true,
+ // is_api: true,
+ // );
+ // }
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($service),
+ ]);
}
- return response()->json(['error' => 'Invalid type.'], 400);
+ return response()->json(['success' => false, 'message' => 'Invalid type.'], 400);
}
public function application_by_uuid(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$uuid = $request->route('uuid');
if (! $uuid) {
- return response()->json(['error' => 'UUID is required.'], 400);
+ return response()->json(['success' => false, 'message' => 'UUID is required.'], 400);
+ }
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
if (! $application) {
- return response()->json(['error' => 'Application not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Application not found.'], 404);
}
- return response()->json(serialize_api_response($application));
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($application),
+ ]);
}
public function delete_by_uuid(Request $request)
{
- ray()->clearAll();
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
$cleanup = $request->query->get('cleanup') ?? false;
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
if ($request->collect()->count() == 0) {
return response()->json([
+ 'success' => false,
'message' => 'Invalid request.',
], 400);
}
@@ -381,17 +531,21 @@ public function delete_by_uuid(Request $request)
public function update_by_uuid(Request $request)
{
- ray()->clearAll();
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
if ($request->collect()->count() == 0) {
return response()->json([
+ 'success' => false,
'message' => 'Invalid request.',
], 400);
}
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
if (! $application) {
@@ -423,6 +577,7 @@ public function update_by_uuid(Request $request)
foreach ($ports as $port) {
if (! is_numeric($port)) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => [
'ports_exposes' => 'The ports_exposes should be a comma separated list of numbers.',
@@ -445,6 +600,7 @@ public function update_by_uuid(Request $request)
}
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $errors,
], 422);
@@ -467,15 +623,21 @@ public function update_by_uuid(Request $request)
$application->fill($data);
$application->save();
- return response()->json(serialize_api_response($application));
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($application),
+ ]);
}
public function envs_by_uuid(Request $request)
{
- ray()->clearAll();
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
+ }
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
@@ -487,17 +649,24 @@ public function envs_by_uuid(Request $request)
}
$envs = $application->environment_variables->sortBy('id')->merge($application->environment_variables_preview->sortBy('id'));
- return response()->json(serialize_api_response($envs));
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($envs),
+ ]);
}
public function update_env_by_uuid(Request $request)
{
- ray()->clearAll();
$allowedFields = ['key', 'value', 'is_preview', 'is_build_time', 'is_literal'];
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
+ }
+
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
@@ -525,6 +694,7 @@ public function update_env_by_uuid(Request $request)
}
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $errors,
], 422);
@@ -547,9 +717,10 @@ public function update_env_by_uuid(Request $request)
}
$env->save();
- return response()->json(serialize_api_response($env));
+ return response()->json(serializeApiResponse($env));
} else {
return response()->json([
+ 'success' => false,
'message' => 'Environment variable not found.',
], 404);
}
@@ -568,10 +739,14 @@ public function update_env_by_uuid(Request $request)
}
$env->save();
- return response()->json(serialize_api_response($env));
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($env),
+ ]);
} else {
return response()->json([
+ 'success' => false,
'message' => 'Environment variable not found.',
], 404);
@@ -579,6 +754,7 @@ public function update_env_by_uuid(Request $request)
}
return response()->json([
+ 'success' => false,
'message' => 'Something went wrong.',
], 500);
@@ -586,11 +762,15 @@ public function update_env_by_uuid(Request $request)
public function create_bulk_envs(Request $request)
{
- ray()->clearAll();
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
+ }
+
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
@@ -604,6 +784,7 @@ public function create_bulk_envs(Request $request)
$bulk_data = $request->get('data');
if (! $bulk_data) {
return response()->json([
+ 'success' => false,
'message' => 'Bulk data is required.',
], 400);
}
@@ -620,6 +801,7 @@ public function create_bulk_envs(Request $request)
]);
if ($validator->fails()) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
@@ -671,18 +853,18 @@ public function create_bulk_envs(Request $request)
}
return response()->json([
- 'message' => 'Environments updated.',
+ 'success' => true,
+ 'data' => serializeApiResponse($env),
]);
}
public function create_env(Request $request)
{
- ray()->clearAll();
$allowedFields = ['key', 'value', 'is_preview', 'is_build_time', 'is_literal'];
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
@@ -710,6 +892,7 @@ public function create_env(Request $request)
}
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $errors,
], 422);
@@ -719,6 +902,7 @@ public function create_env(Request $request)
$env = $application->environment_variables_preview->where('key', $request->key)->first();
if ($env) {
return response()->json([
+ 'success' => false,
'message' => 'Environment variable already exists. Use PATCH request to update it.',
], 409);
} else {
@@ -730,7 +914,10 @@ public function create_env(Request $request)
'is_literal' => $request->is_literal ?? false,
]);
- return response()->json(serialize_api_response($env))->setStatusCode(201);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($env),
+ ])->setStatusCode(201);
}
} else {
$env = $application->environment_variables->where('key', $request->key)->first();
@@ -747,12 +934,16 @@ public function create_env(Request $request)
'is_literal' => $request->is_literal ?? false,
]);
- return response()->json(serialize_api_response($env))->setStatusCode(201);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($env),
+ ])->setStatusCode(201);
}
}
return response()->json([
+ 'success' => false,
'message' => 'Something went wrong.',
], 500);
@@ -760,10 +951,9 @@ public function create_env(Request $request)
public function delete_env_by_uuid(Request $request)
{
- ray()->clearAll();
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
@@ -790,19 +980,19 @@ public function delete_env_by_uuid(Request $request)
public function action_deploy(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$force = $request->query->get('force') ?? false;
$instant_deploy = $request->query->get('instant_deploy') ?? false;
$uuid = $request->route('uuid');
if (! $uuid) {
- return response()->json(['error' => 'UUID is required.'], 400);
+ return response()->json(['success' => false, 'message' => 'UUID is required.'], 400);
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
if (! $application) {
- return response()->json(['error' => 'Application not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Application not found.'], 404);
}
$deployment_uuid = new Cuid2(7);
@@ -817,9 +1007,12 @@ public function action_deploy(Request $request)
return response()->json(
[
+ 'success' => true,
'message' => 'Deployment request queued.',
- 'deployment_uuid' => $deployment_uuid->toString(),
- 'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
+ 'data' => [
+ 'deployment_uuid' => $deployment_uuid->toString(),
+ 'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
+ ],
],
200
);
@@ -827,43 +1020,53 @@ public function action_deploy(Request $request)
public function action_stop(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$uuid = $request->route('uuid');
$sync = $request->query->get('sync') ?? false;
if (! $uuid) {
- return response()->json(['error' => 'UUID is required.'], 400);
+ return response()->json(['success' => false, 'message' => 'UUID is required.'], 400);
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
if (! $application) {
- return response()->json(['error' => 'Application not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Application not found.'], 404);
}
if ($sync) {
StopApplication::run($application);
- return response()->json(['message' => 'Stopped the application.'], 200);
+ return response()->json(
+ [
+ 'success' => true,
+ 'message' => 'Stopped the application.',
+ ],
+ );
} else {
StopApplication::dispatch($application);
- return response()->json(['message' => 'Stopping request queued.'], 200);
+ return response()->json(
+ [
+ 'success' => true,
+ 'message' => 'Stopping request queued.',
+ ],
+ );
}
}
public function action_restart(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$uuid = $request->route('uuid');
if (! $uuid) {
- return response()->json(['error' => 'UUID is required.'], 400);
+ return response()->json(['success' => false, 'message' => 'UUID is required.'], 400);
}
$application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first();
if (! $application) {
- return response()->json(['error' => 'Application not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Application not found.'], 404);
}
$deployment_uuid = new Cuid2(7);
@@ -877,31 +1080,25 @@ public function action_restart(Request $request)
return response()->json(
[
+ 'success' => true,
'message' => 'Restart request queued.',
- 'deployment_uuid' => $deployment_uuid->toString(),
- 'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
+ 'data' => [
+ 'deployment_uuid' => $deployment_uuid->toString(),
+ 'deployment_api_url' => base_url().'/api/v1/deployment/'.$deployment_uuid->toString(),
+ ],
],
- 200
);
}
- private function removeUnnecessaryFieldsFromRequest(Request $request)
- {
- $request->offsetUnset('project_uuid');
- $request->offsetUnset('environment_name');
- $request->offsetUnset('destination_uuid');
- $request->offsetUnset('server_uuid');
- $request->offsetUnset('type');
- $request->offsetUnset('domains');
- $request->offsetUnset('instant_deploy');
- $request->offsetUnset('github_app_uuid');
- $request->offsetUnset('private_key_uuid');
- }
-
private function validateDataApplications(Request $request, Server $server)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
+
+ // Default build pack is nixpacks
+ if (! $request->has('build_pack')) {
+ $request->offsetSet('build_pack', 'nixpacks');
+ }
// Validate ports_mappings
if ($request->has('ports_mappings')) {
@@ -910,6 +1107,7 @@ private function validateDataApplications(Request $request, Server $server)
$port = explode(':', $portMapping);
if (in_array($port[0], $ports)) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => [
'ports_mappings' => 'The first number before : should be unique between mappings.',
@@ -923,6 +1121,7 @@ private function validateDataApplications(Request $request, Server $server)
if ($request->has('custom_labels')) {
if (! isBase64Encoded($request->custom_labels)) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => [
'custom_labels' => 'The custom_labels should be base64 encoded.',
@@ -932,6 +1131,7 @@ private function validateDataApplications(Request $request, Server $server)
$customLabels = base64_decode($request->custom_labels);
if (mb_detect_encoding($customLabels, 'ASCII', true) === false) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => [
'custom_labels' => 'The custom_labels should be base64 encoded.',
@@ -954,12 +1154,14 @@ private function validateDataApplications(Request $request, Server $server)
});
if (count($errors) > 0) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => $errors,
], 422);
}
if (checkIfDomainIsAlreadyUsed($fqdn, $teamId)) {
return response()->json([
+ 'success' => false,
'message' => 'Validation failed.',
'errors' => [
'domains' => 'One of the domain is already used.',
diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php
new file mode 100644
index 000000000..36a5fffaf
--- /dev/null
+++ b/app/Http/Controllers/Api/DatabasesController.php
@@ -0,0 +1,259 @@
+get();
+ $databases = collect();
+ foreach ($projects as $project) {
+ $databases = $databases->merge($project->databases());
+ }
+ $databases = $databases->map(function ($database) {
+ return serializeApiResponse($database);
+ });
+
+ return response()->json([
+ 'success' => true,
+ 'data' => $databases,
+ ]);
+ }
+
+ public function database_by_uuid(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ if (! $request->uuid) {
+ return response()->json(['success' => false, 'message' => 'UUID is required.'], 404);
+ }
+ $database = queryDatabaseByUuidWithinTeam($request->uuid, $teamId);
+ if (! $database) {
+ return response()->json(['success' => false, 'message' => 'Database not found.'], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($database),
+ ]);
+ }
+
+ public function create_database(Request $request)
+ {
+ $allowedFields = ['type', 'name', 'description', 'image', 'project_uuid', 'environment_name', 'server_uuid', 'destination_uuid', 'instant_deploy', 'postgres_user', 'postgres_password', 'postgres_db', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares'];
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+ $validator = customApiValidator($request->all(), [
+ 'type' => ['required', Rule::enum(NewDatabaseTypes::class)],
+ 'name' => 'string|max:255',
+ 'description' => 'string|nullable',
+ 'image' => 'string',
+ 'project_uuid' => 'string|required',
+ 'environment_name' => 'string|required',
+ 'server_uuid' => 'string|required',
+ 'destination_uuid' => 'string',
+ 'postgres_user' => 'string',
+ 'postgres_password' => 'string',
+ 'postgres_db' => 'string',
+ 'limits_memory' => 'string',
+ 'limits_memory_swap' => 'string',
+ 'limits_memory_swappiness' => 'numeric',
+ 'limits_memory_reservation' => 'string',
+ 'limits_cpus' => 'string',
+ 'limits_cpuset' => 'string|nullable',
+ 'limits_cpu_shares' => 'numeric',
+ 'instant_deploy' => 'boolean',
+ ]);
+
+ $extraFields = array_diff(array_keys($request->all()), $allowedFields);
+ if ($validator->fails() || ! empty($extraFields)) {
+ $errors = $validator->errors();
+ if (! empty($extraFields)) {
+ foreach ($extraFields as $field) {
+ $errors->add($field, 'This field is not allowed.');
+ }
+ }
+
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validation failed.',
+ 'errors' => $errors,
+ ], 422);
+ }
+ $serverUuid = $request->server_uuid;
+ $instantDeploy = $request->instant_deploy ?? false;
+
+ $project = Project::whereTeamId($teamId)->whereUuid($request->project_uuid)->first();
+ if (! $project) {
+ return response()->json(['succes' => false, 'message' => 'Project not found.'], 404);
+ }
+ $environment = $project->environments()->where('name', $request->environment_name)->first();
+ if (! $environment) {
+ return response()->json(['success' => false, 'message' => 'Environment not found.'], 404);
+ }
+ $server = Server::whereTeamId($teamId)->whereUuid($serverUuid)->first();
+ if (! $server) {
+ return response()->json(['success' => false, 'message' => 'Server not found.'], 404);
+ }
+ $destinations = $server->destinations();
+ if ($destinations->count() == 0) {
+ return response()->json(['success' => false, 'message' => 'Server has no destinations.'], 400);
+ }
+ if ($destinations->count() > 1 && ! $request->has('destination_uuid')) {
+ return response()->json(['success' => false, 'message' => 'Server has multiple destinations and you do not set destination_uuid.'], 400);
+ }
+ $destination = $destinations->first();
+
+ if ($request->type === NewDatabaseTypes::POSTGRESQL->value) {
+ removeUnnecessaryFieldsFromRequest($request);
+ $database = create_standalone_postgresql($environment->id, $destination->uuid, $request->all());
+ if ($instantDeploy) {
+ StartPostgresql::dispatch($database);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database starting queued.',
+ 'data' => serializeApiResponse($database),
+ ]);
+ } elseif ($request->type === NewDatabaseTypes::MARIADB->value) {
+ removeUnnecessaryFieldsFromRequest($request);
+ $database = create_standalone_mariadb($environment->id, $destination->uuid, $request->all());
+ if ($instantDeploy) {
+ StartMariadb::dispatch($database);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database starting queued.',
+ 'data' => serializeApiResponse($database),
+ ]);
+ } elseif ($request->type === NewDatabaseTypes::MYSQL->value) {
+ removeUnnecessaryFieldsFromRequest($request);
+ $database = create_standalone_mysql($environment->id, $destination->uuid, $request->all());
+ if ($instantDeploy) {
+ StartMysql::dispatch($database);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database starting queued.',
+ 'data' => serializeApiResponse($database),
+ ]);
+ } elseif ($request->type === NewDatabaseTypes::REDIS->value) {
+ removeUnnecessaryFieldsFromRequest($request);
+ $database = create_standalone_redis($environment->id, $destination->uuid, $request->all());
+ if ($instantDeploy) {
+ StartRedis::dispatch($database);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database starting queued.',
+ 'data' => serializeApiResponse($database),
+ ]);
+ } elseif ($request->type === NewDatabaseTypes::DRAGONFLY->value) {
+ removeUnnecessaryFieldsFromRequest($request);
+ $database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->all());
+ if ($instantDeploy) {
+ StartDragonfly::dispatch($database);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database starting queued.',
+ 'data' => serializeApiResponse($database),
+ ]);
+ } elseif ($request->type === NewDatabaseTypes::KEYDB->value) {
+ removeUnnecessaryFieldsFromRequest($request);
+ $database = create_standalone_keydb($environment->id, $destination->uuid, $request->all());
+ if ($instantDeploy) {
+ StartKeydb::dispatch($database);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database starting queued.',
+ 'data' => serializeApiResponse($database),
+ ]);
+ } elseif ($request->type === NewDatabaseTypes::CLICKHOUSE->value) {
+ removeUnnecessaryFieldsFromRequest($request);
+ $database = create_standalone_clickhouse($environment->id, $destination->uuid, $request->all());
+ if ($instantDeploy) {
+ StartClickhouse::dispatch($database);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database starting queued.',
+ 'data' => serializeApiResponse($database),
+ ]);
+ } elseif ($request->type === NewDatabaseTypes::MONGODB->value) {
+ removeUnnecessaryFieldsFromRequest($request);
+ $database = create_standalone_mongodb($environment->id, $destination->uuid, $request->all());
+ if ($instantDeploy) {
+ StartMongodb::dispatch($database);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database starting queued.',
+ 'data' => serializeApiResponse($database),
+ ]);
+ }
+
+ return response()->json(['success' => false, 'message' => 'Invalid database type requested.'], 400);
+ }
+
+ public function delete_by_uuid(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ if (! $request->uuid) {
+ return response()->json(['success' => false, 'message' => 'UUID is required.'], 404);
+ }
+ $database = queryDatabaseByUuidWithinTeam($request->uuid, $teamId);
+ if (! $database) {
+ return response()->json(['success' => false, 'message' => 'Database not found.'], 404);
+ }
+ StopDatabase::dispatch($database);
+ $database->delete();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Database deletion request queued.',
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Api/Deploy.php b/app/Http/Controllers/Api/DeployController.php
similarity index 73%
rename from app/Http/Controllers/Api/Deploy.php
rename to app/Http/Controllers/Api/DeployController.php
index d510970dd..76e67548c 100644
--- a/app/Http/Controllers/Api/Deploy.php
+++ b/app/Http/Controllers/Api/DeployController.php
@@ -18,13 +18,13 @@
use Illuminate\Http\Request;
use Visus\Cuid2\Cuid2;
-class Deploy extends Controller
+class DeployController extends Controller
{
public function deployments(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$servers = Server::whereTeamId($teamId)->get();
$deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get([
@@ -38,39 +38,45 @@ public function deployments(Request $request)
'status',
])->sortBy('id')->toArray();
- return response()->json(serialize_api_response($deployments_per_server), 200);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($deployments_per_server),
+ ]);
}
public function deployment_by_uuid(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$uuid = $request->route('uuid');
if (! $uuid) {
- return response()->json(['message' => 'UUID is required.'], 400);
+ return response()->json(['success' => false, 'message' => 'UUID is required.'], 400);
}
$deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first();
if (! $deployment) {
- return response()->json(['message' => 'Deployment not found.'], 404);
+ return response()->json(['success' => false, 'message' => 'Deployment not found.'], 404);
}
- return response()->json(serialize_api_response($deployment->makeHidden('logs')), 200);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($deployment->makeHidden('logs')),
+ ]);
}
public function deploy(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
$uuids = $request->query->get('uuid');
$tags = $request->query->get('tag');
$force = $request->query->get('force') ?? false;
if ($uuids && $tags) {
- return response()->json(['message' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
+ return response()->json(['success' => false, 'message' => 'You can only use uuid or tag, not both.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
}
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
if ($tags) {
return $this->by_tags($tags, $teamId, $force);
@@ -78,7 +84,7 @@ public function deploy(Request $request)
return $this->by_uuids($uuids, $teamId, $force);
}
- return response()->json(['message' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
+ return response()->json(['success' => false, 'message' => 'You must provide uuid or tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
}
private function by_uuids(string $uuid, int $teamId, bool $force = false)
@@ -87,7 +93,7 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false)
$uuids = collect(array_filter($uuids));
if (count($uuids) === 0) {
- return response()->json(['message' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
+ return response()->json(['success' => false, 'message' => 'No UUIDs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
}
$deployments = collect();
$payload = collect();
@@ -96,19 +102,22 @@ private function by_uuids(string $uuid, int $teamId, bool $force = false)
if ($resource) {
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
if ($deployment_uuid) {
- $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
+ $deployments->push(['success' => true, 'message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
} else {
- $deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]);
+ $deployments->push(['success' => true, 'message' => $return_message, 'resource_uuid' => $uuid]);
}
}
}
if ($deployments->count() > 0) {
$payload->put('deployments', $deployments->toArray());
- return response()->json($payload->toArray(), 200);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($payload->toArray()),
+ ]);
}
- return response()->json(['message' => 'No resources found.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
+ return response()->json(['success' => false, 'message' => 'No resources found.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
}
public function by_tags(string $tags, int $team_id, bool $force = false)
@@ -117,7 +126,7 @@ public function by_tags(string $tags, int $team_id, bool $force = false)
$tags = collect(array_filter($tags));
if (count($tags) === 0) {
- return response()->json(['message' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
+ return response()->json(['success' => false, 'message' => 'No TAGs provided.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 400);
}
$message = collect([]);
$deployments = collect();
@@ -153,10 +162,13 @@ public function by_tags(string $tags, int $team_id, bool $force = false)
$payload->put('details', $deployments->toArray());
}
- return response()->json($payload->toArray(), 200);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($payload->toArray()),
+ ]);
}
- return response()->json(['message' => 'No resources found with this tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
+ return response()->json(['success' => false, 'message' => 'No resources found with this tag.', 'docs' => 'https://coolify.io/docs/api-reference/deploy-webhook'], 404);
}
public function deploy_resource($resource, bool $force = false): array
@@ -164,7 +176,7 @@ public function deploy_resource($resource, bool $force = false): array
$message = null;
$deployment_uuid = null;
if (gettype($resource) !== 'object') {
- return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
+ return ['success' => false, 'message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
}
$type = $resource?->getMorphClass();
if ($type === 'App\Models\Application') {
@@ -228,6 +240,6 @@ public function deploy_resource($resource, bool $force = false): array
$message = "Service {$resource->name} started. It could take a while, be patient.";
}
- return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
+ return ['success' => true, 'message' => $message, 'deployment_uuid' => $deployment_uuid];
}
}
diff --git a/app/Http/Controllers/Api/EnvironmentVariables.php b/app/Http/Controllers/Api/EnvironmentVariablesController.php
similarity index 86%
rename from app/Http/Controllers/Api/EnvironmentVariables.php
rename to app/Http/Controllers/Api/EnvironmentVariablesController.php
index d788bdb0c..c54656dc6 100644
--- a/app/Http/Controllers/Api/EnvironmentVariables.php
+++ b/app/Http/Controllers/Api/EnvironmentVariablesController.php
@@ -6,14 +6,13 @@
use App\Models\EnvironmentVariable;
use Illuminate\Http\Request;
-class EnvironmentVariables extends Controller
+class EnvironmentVariablesController extends Controller
{
public function delete_env_by_uuid(Request $request)
{
- ray()->clearAll();
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$env = EnvironmentVariable::where('uuid', $request->env_uuid)->first();
if (! $env) {
diff --git a/app/Http/Controllers/Api/Project.php b/app/Http/Controllers/Api/Project.php
deleted file mode 100644
index baaf1eacb..000000000
--- a/app/Http/Controllers/Api/Project.php
+++ /dev/null
@@ -1,44 +0,0 @@
-select('id', 'name', 'uuid')->get();
-
- return response()->json($projects);
- }
-
- public function project_by_uuid(Request $request)
- {
- $teamId = get_team_id_from_token();
- if (is_null($teamId)) {
- return invalid_token();
- }
- $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']);
-
- return response()->json($project);
- }
-
- public function environment_details(Request $request)
- {
- $teamId = get_team_id_from_token();
- if (is_null($teamId)) {
- return invalid_token();
- }
- $project = ModelsProject::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
- $environment = $project->environments()->whereName(request()->environment_name)->first()->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
-
- return response()->json($environment);
- }
-}
diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php
new file mode 100644
index 000000000..4721b48e1
--- /dev/null
+++ b/app/Http/Controllers/Api/ProjectController.php
@@ -0,0 +1,60 @@
+select('id', 'name', 'uuid')->get();
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($projects),
+ ]);
+ }
+
+ public function project_by_uuid(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first()->load(['environments']);
+ if (! $project) {
+ return response()->json(['success' => false, 'message' => 'Project not found.'], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($project),
+ ]);
+ }
+
+ public function environment_details(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $project = Project::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
+ $environment = $project->environments()->whereName(request()->environment_name)->first();
+ if (! $environment) {
+ return response()->json(['success' => false, 'message' => 'Environment not found.'], 404);
+ }
+ $environment = $environment->load(['applications', 'postgresqls', 'redis', 'mongodbs', 'mysqls', 'mariadbs', 'services']);
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($environment),
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Api/Resources.php b/app/Http/Controllers/Api/ResourcesController.php
similarity index 80%
rename from app/Http/Controllers/Api/Resources.php
rename to app/Http/Controllers/Api/ResourcesController.php
index 0d538b62e..47dfc6733 100644
--- a/app/Http/Controllers/Api/Resources.php
+++ b/app/Http/Controllers/Api/ResourcesController.php
@@ -6,13 +6,13 @@
use App\Models\Project;
use Illuminate\Http\Request;
-class Resources extends Controller
+class ResourcesController extends Controller
{
public function resources(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$projects = Project::where('team_id', $teamId)->get();
$resources = collect();
@@ -34,6 +34,9 @@ public function resources(Request $request)
return $payload;
});
- return response()->json($resources);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($resources),
+ ]);
}
}
diff --git a/app/Http/Controllers/Api/SecurityController.php b/app/Http/Controllers/Api/SecurityController.php
new file mode 100644
index 000000000..51c6fee26
--- /dev/null
+++ b/app/Http/Controllers/Api/SecurityController.php
@@ -0,0 +1,160 @@
+get();
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($keys),
+ ]);
+ }
+
+ public function key_by_uuid(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+
+ $key = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first();
+
+ if (is_null($key)) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Key not found.',
+ ], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($key),
+ ]);
+ }
+
+ public function create_key(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+ $validator = customApiValidator($request->all(), [
+ 'name' => 'string|max:255',
+ 'description' => 'string|max:255',
+ 'private_key' => 'required|string',
+ ]);
+
+ if ($validator->fails()) {
+ $errors = $validator->errors();
+
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validation failed.',
+ 'errors' => $errors,
+ ], 422);
+ }
+ if (! $request->name) {
+ $request->offsetSet('name', generate_random_name());
+ }
+ if (! $request->description) {
+ $request->offsetSet('description', 'Created by Coolify via API');
+ }
+ $key = PrivateKey::create([
+ 'team_id' => $teamId,
+ 'name' => $request->name,
+ 'description' => $request->description,
+ 'private_key' => $request->private_key,
+ ]);
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($key),
+ ]);
+ }
+
+ public function update_key(Request $request)
+ {
+ $allowedFields = ['name', 'description', 'private_key'];
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $return = validateIncomingRequest($request);
+ if ($return instanceof \Illuminate\Http\JsonResponse) {
+ return $return;
+ }
+
+ $validator = customApiValidator($request->all(), [
+ 'name' => 'string|max:255',
+ 'description' => 'string|max:255',
+ 'private_key' => 'required|string',
+ ]);
+
+ $extraFields = array_diff(array_keys($request->all()), $allowedFields);
+ if ($validator->fails() || ! empty($extraFields)) {
+ $errors = $validator->errors();
+ if (! empty($extraFields)) {
+ foreach ($extraFields as $field) {
+ $errors->add($field, 'This field is not allowed.');
+ }
+ }
+
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Validation failed.',
+ 'errors' => $errors,
+ ], 422);
+ }
+ $foundKey = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first();
+ if (is_null($foundKey)) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Key not found.',
+ ], 404);
+ }
+ $foundKey->update($request->all());
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($foundKey),
+ ])->setStatusCode(201);
+ }
+
+ public function delete_key(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ if (! $request->uuid) {
+ return response()->json(['success' => false, 'message' => 'UUID is required.'], 422);
+ }
+
+ $key = PrivateKey::where('team_id', $teamId)->where('uuid', $request->uuid)->first();
+ if (is_null($key)) {
+ return response()->json(['success' => false, 'message' => 'Key not found.'], 404);
+ }
+ $key->delete();
+
+ return response()->json([
+ 'success' => true,
+ 'message' => 'Key deleted.',
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Api/Servers.php b/app/Http/Controllers/Api/ServersController.php
similarity index 79%
rename from app/Http/Controllers/Api/Servers.php
rename to app/Http/Controllers/Api/ServersController.php
index 387c4bd48..4d9479b7c 100644
--- a/app/Http/Controllers/Api/Servers.php
+++ b/app/Http/Controllers/Api/ServersController.php
@@ -8,14 +8,15 @@
use App\Models\Project;
use App\Models\Server as ModelsServer;
use Illuminate\Http\Request;
+use Stringable;
-class Servers extends Controller
+class ServersController extends Controller
{
public function servers(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$servers = ModelsServer::whereTeamId($teamId)->select('id', 'name', 'uuid', 'ip', 'user', 'port')->get()->load(['settings'])->map(function ($server) {
$server['is_reachable'] = $server->settings->is_reachable;
@@ -23,16 +24,22 @@ public function servers(Request $request)
return $server;
});
+ $servers = $servers->map(function ($server) {
+ return serializeApiResponse($server);
+ });
- return response()->json($servers);
+ return response()->json([
+ 'success' => true,
+ 'data' => $servers,
+ ]);
}
public function server_by_uuid(Request $request)
{
$with_resources = $request->query('resources');
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
$server = ModelsServer::whereTeamId($teamId)->whereUuid(request()->uuid)->first();
if (is_null($server)) {
@@ -60,22 +67,25 @@ public function server_by_uuid(Request $request)
$server->load(['settings']);
}
- return response()->json($server);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($server),
+ ]);
}
public function get_domains_by_server(Request $request)
{
- $teamId = get_team_id_from_token();
+ $teamId = getTeamIdFromToken();
if (is_null($teamId)) {
- return invalid_token();
+ return invalidTokenResponse();
}
- $uuid = $request->query->get('uuid');
+ $uuid = $request->get('uuid');
if ($uuid) {
$domains = Application::getDomainsByUuid($uuid);
return response()->json([
- 'uuid' => $uuid,
- 'domains' => $domains,
+ 'success' => true,
+ 'data' => serializeApiResponse($domains),
]);
}
$projects = Project::where('team_id', $teamId)->get();
@@ -86,8 +96,13 @@ public function get_domains_by_server(Request $request)
foreach ($applications as $application) {
$ip = $application->destination->server->ip;
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
- return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
+ $f = str($fqdn)->replace('http://', '')->replace('https://', '')->explode('/');
+
+ return str(str($f[0])->explode(':')[0]);
+ })->filter(function (Stringable $fqdn) {
+ return $fqdn->isNotEmpty();
});
+
if ($ip === 'host.docker.internal') {
if ($settings->public_ipv4) {
$domains->push([
@@ -122,7 +137,11 @@ public function get_domains_by_server(Request $request)
if ($service_applications->count() > 0) {
foreach ($service_applications as $application) {
$fqdn = str($application->fqdn)->explode(',')->map(function ($fqdn) {
- return str($fqdn)->replace('http://', '')->replace('https://', '')->replace('/', '');
+ $f = str($fqdn)->replace('http://', '')->replace('https://', '')->explode('/');
+
+ return str(str($f[0])->explode(':')[0]);
+ })->filter(function (Stringable $fqdn) {
+ return $fqdn->isNotEmpty();
});
if ($ip === 'host.docker.internal') {
if ($settings->public_ipv4) {
@@ -162,6 +181,9 @@ public function get_domains_by_server(Request $request)
];
})->values();
- return response()->json($domains);
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($domains),
+ ]);
}
}
diff --git a/app/Http/Controllers/Api/Team.php b/app/Http/Controllers/Api/Team.php
deleted file mode 100644
index c895f2c1b..000000000
--- a/app/Http/Controllers/Api/Team.php
+++ /dev/null
@@ -1,74 +0,0 @@
-user()->teams;
-
- return response()->json($teams);
- }
-
- public function team_by_id(Request $request)
- {
- $id = $request->id;
- $teamId = get_team_id_from_token();
- if (is_null($teamId)) {
- return invalid_token();
- }
- $teams = auth()->user()->teams;
- $team = $teams->where('id', $id)->first();
- if (is_null($team)) {
- return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid'], 404);
- }
-
- return response()->json($team);
- }
-
- public function members_by_id(Request $request)
- {
- $id = $request->id;
- $teamId = get_team_id_from_token();
- if (is_null($teamId)) {
- return invalid_token();
- }
- $teams = auth()->user()->teams;
- $team = $teams->where('id', $id)->first();
- if (is_null($team)) {
- return response()->json(['error' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid-members'], 404);
- }
-
- return response()->json($team->members);
- }
-
- public function current_team(Request $request)
- {
- $teamId = get_team_id_from_token();
- if (is_null($teamId)) {
- return invalid_token();
- }
- $team = auth()->user()->currentTeam();
-
- return response()->json($team);
- }
-
- public function current_team_members(Request $request)
- {
- $teamId = get_team_id_from_token();
- if (is_null($teamId)) {
- return invalid_token();
- }
- $team = auth()->user()->currentTeam();
-
- return response()->json($team->members);
- }
-}
diff --git a/app/Http/Controllers/Api/TeamController.php b/app/Http/Controllers/Api/TeamController.php
new file mode 100644
index 000000000..a256e9caf
--- /dev/null
+++ b/app/Http/Controllers/Api/TeamController.php
@@ -0,0 +1,89 @@
+user()->teams;
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($teams),
+ ]);
+ }
+
+ public function team_by_id(Request $request)
+ {
+ $id = $request->id;
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $teams = auth()->user()->teams;
+ $team = $teams->where('id', $id)->first();
+ if (is_null($team)) {
+ return response()->json(['success' => false, 'message' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid'], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($team),
+ ]);
+ }
+
+ public function members_by_id(Request $request)
+ {
+ $id = $request->id;
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $teams = auth()->user()->teams;
+ $team = $teams->where('id', $id)->first();
+ if (is_null($team)) {
+ return response()->json(['success' => false, 'message' => 'Team not found.', 'docs' => 'https://coolify.io/docs/api-reference/get-team-by-teamid-members'], 404);
+ }
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($team->members),
+ ]);
+ }
+
+ public function current_team(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $team = auth()->user()->currentTeam();
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($team),
+ ]);
+ }
+
+ public function current_team_members(Request $request)
+ {
+ $teamId = getTeamIdFromToken();
+ if (is_null($teamId)) {
+ return invalidTokenResponse();
+ }
+ $team = auth()->user()->currentTeam();
+
+ return response()->json([
+ 'success' => true,
+ 'data' => serializeApiResponse($team->members),
+ ]);
+ }
+}
diff --git a/app/Http/Middleware/ApiAllowed.php b/app/Http/Middleware/ApiAllowed.php
new file mode 100644
index 000000000..dc0a433e2
--- /dev/null
+++ b/app/Http/Middleware/ApiAllowed.php
@@ -0,0 +1,34 @@
+clearAll();
+ if (isCloud()) {
+ return $next($request);
+ }
+ $settings = InstanceSettings::get();
+ if ($settings->is_api_enabled === false) {
+ return response()->json(['success' => true, 'message' => 'API is disabled.'], 403);
+ }
+
+ if (! isDev()) {
+ if ($settings->allowed_ips) {
+ $allowedIps = explode(',', $settings->allowed_ips);
+ if (! in_array($request->ip(), $allowedIps)) {
+ return response()->json(['success' => true, 'message' => 'You are not allowed to access the API.'], 403);
+ }
+ }
+ }
+
+ return $next($request);
+ }
+}
diff --git a/app/Livewire/Settings/Configuration.php b/app/Livewire/Settings/Configuration.php
index 3b6d7cd72..7439e112f 100644
--- a/app/Livewire/Settings/Configuration.php
+++ b/app/Livewire/Settings/Configuration.php
@@ -18,7 +18,8 @@ class Configuration extends Component
public bool $is_dns_validation_enabled;
- // public bool $next_channel;
+ public bool $is_api_enabled;
+
protected string $dynamic_config_path = '/data/coolify/proxy/dynamic';
protected Server $server;
@@ -30,6 +31,7 @@ class Configuration extends Component
'settings.public_port_max' => 'required',
'settings.custom_dns_servers' => 'nullable',
'settings.instance_name' => 'nullable',
+ 'settings.allowed_ips' => 'nullable',
];
protected $validationAttributes = [
@@ -38,6 +40,7 @@ class Configuration extends Component
'settings.public_port_min' => 'Public port min',
'settings.public_port_max' => 'Public port max',
'settings.custom_dns_servers' => 'Custom DNS servers',
+ 'settings.allowed_ips' => 'Allowed IPs',
];
public function mount()
@@ -45,8 +48,8 @@ public function mount()
$this->do_not_track = $this->settings->do_not_track;
$this->is_auto_update_enabled = $this->settings->is_auto_update_enabled;
$this->is_registration_enabled = $this->settings->is_registration_enabled;
- // $this->next_channel = $this->settings->next_channel;
$this->is_dns_validation_enabled = $this->settings->is_dns_validation_enabled;
+ $this->is_api_enabled = $this->settings->is_api_enabled;
}
public function instantSave()
@@ -55,12 +58,7 @@ public function instantSave()
$this->settings->is_auto_update_enabled = $this->is_auto_update_enabled;
$this->settings->is_registration_enabled = $this->is_registration_enabled;
$this->settings->is_dns_validation_enabled = $this->is_dns_validation_enabled;
- // if ($this->next_channel) {
- // $this->settings->next_channel = false;
- // $this->next_channel = false;
- // } else {
- // $this->settings->next_channel = $this->next_channel;
- // }
+ $this->settings->is_api_enabled = $this->is_api_enabled;
$this->settings->save();
$this->dispatch('success', 'Settings updated!');
}
@@ -94,6 +92,13 @@ public function submit()
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->unique();
$this->settings->custom_dns_servers = $this->settings->custom_dns_servers->implode(',');
+ $this->settings->allowed_ips = str($this->settings->allowed_ips)->replaceEnd(',', '')->trim();
+ $this->settings->allowed_ips = str($this->settings->allowed_ips)->trim()->explode(',')->map(function ($ip) {
+ return str($ip)->trim();
+ });
+ $this->settings->allowed_ips = $this->settings->allowed_ips->unique();
+ $this->settings->allowed_ips = $this->settings->allowed_ips->implode(',');
+
$this->settings->save();
$this->server->setupDynamicProxyConfiguration();
if (! $error_show) {
diff --git a/app/Models/InstanceSettings.php b/app/Models/InstanceSettings.php
index 38f79ce75..bd3c41a1f 100644
--- a/app/Models/InstanceSettings.php
+++ b/app/Models/InstanceSettings.php
@@ -17,6 +17,7 @@ class InstanceSettings extends Model implements SendsEmail
protected $casts = [
'resale_license' => 'encrypted',
'smtp_password' => 'encrypted',
+ 'allowed_ip_ranges' => 'array',
];
public function fqdn(): Attribute
diff --git a/app/Models/StandaloneClickhouse.php b/app/Models/StandaloneClickhouse.php
index e968db18d..673224650 100644
--- a/app/Models/StandaloneClickhouse.php
+++ b/app/Models/StandaloneClickhouse.php
@@ -13,6 +13,8 @@ class StandaloneClickhouse extends BaseModel
protected $guarded = [];
+ protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
+
protected $casts = [
'clickhouse_password' => 'encrypted',
];
@@ -178,17 +180,44 @@ public function team()
return data_get($this, 'environment.project.team');
}
+ public function databaseType(): Attribute
+ {
+ return new Attribute(
+ get: fn () => $this->type(),
+ );
+ }
+
public function type(): string
{
return 'standalone-clickhouse';
}
- public function get_db_url(bool $useInternal = false): string
+ protected function internalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: fn () => "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->uuid}:9000/{$this->clickhouse_db}",
+ );
+ }
+
+ protected function externalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: function () {
+ if ($this->is_public && $this->public_port) {
+ return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->clickhouse_db}";
+ }
+
+ return null;
+ }
+ );
+ }
+
+ public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && ! $useInternal) {
- return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->clickhouse_db}";
+ return $this->externalDbUrl;
} else {
- return "clickhouse://{$this->clickhouse_user}:{$this->clickhouse_password}@{$this->uuid}:9000/{$this->clickhouse_db}";
+ return $this->internalDbUrl;
}
}
diff --git a/app/Models/StandaloneDragonfly.php b/app/Models/StandaloneDragonfly.php
index c6718acfe..d78d656c1 100644
--- a/app/Models/StandaloneDragonfly.php
+++ b/app/Models/StandaloneDragonfly.php
@@ -13,6 +13,8 @@ class StandaloneDragonfly extends BaseModel
protected $guarded = [];
+ protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
+
protected $casts = [
'dragonfly_password' => 'encrypted',
];
@@ -178,17 +180,44 @@ public function portsMappingsArray(): Attribute
);
}
+ public function databaseType(): Attribute
+ {
+ return new Attribute(
+ get: fn () => $this->type(),
+ );
+ }
+
public function type(): string
{
return 'standalone-dragonfly';
}
- public function get_db_url(bool $useInternal = false): string
+ protected function internalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: fn () => "redis://:{$this->dragonfly_password}@{$this->uuid}:6379/0",
+ );
+ }
+
+ protected function externalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: function () {
+ if ($this->is_public && $this->public_port) {
+ return "redis://:{$this->dragonfly_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
+ }
+
+ return null;
+ }
+ );
+ }
+
+ public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && ! $useInternal) {
- return "redis://:{$this->dragonfly_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
+ return $this->externalDbUrl;
} else {
- return "redis://:{$this->dragonfly_password}@{$this->uuid}:6379/0";
+ return $this->internalDbUrl;
}
}
diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php
index 142f960aa..7b71bd55f 100644
--- a/app/Models/StandaloneKeydb.php
+++ b/app/Models/StandaloneKeydb.php
@@ -13,6 +13,8 @@ class StandaloneKeydb extends BaseModel
protected $guarded = [];
+ protected $appends = ['internal_db_url', 'external_db_url'];
+
protected $casts = [
'keydb_password' => 'encrypted',
];
@@ -178,17 +180,44 @@ public function portsMappingsArray(): Attribute
);
}
+ public function databaseType(): Attribute
+ {
+ return new Attribute(
+ get: fn () => $this->type(),
+ );
+ }
+
public function type(): string
{
return 'standalone-keydb';
}
- public function get_db_url(bool $useInternal = false): string
+ protected function internalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: fn () => "redis://{$this->keydb_password}@{$this->uuid}:6379/0",
+ );
+ }
+
+ protected function externalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: function () {
+ if ($this->is_public && $this->public_port) {
+ return "redis://{$this->keydb_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
+ }
+
+ return null;
+ }
+ );
+ }
+
+ public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && ! $useInternal) {
- return "redis://{$this->keydb_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
+ return $this->externalDbUrl;
} else {
- return "redis://{$this->keydb_password}@{$this->uuid}:6379/0";
+ return $this->internalDbUrl;
}
}
diff --git a/app/Models/StandaloneMariadb.php b/app/Models/StandaloneMariadb.php
index 7e6d2e0d1..00df4fe71 100644
--- a/app/Models/StandaloneMariadb.php
+++ b/app/Models/StandaloneMariadb.php
@@ -13,6 +13,8 @@ class StandaloneMariadb extends BaseModel
protected $guarded = [];
+ protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
+
protected $casts = [
'mariadb_password' => 'encrypted',
];
@@ -161,6 +163,13 @@ public function isLogDrainEnabled()
return data_get($this, 'is_log_drain_enabled', false);
}
+ public function databaseType(): Attribute
+ {
+ return new Attribute(
+ get: fn () => $this->type(),
+ );
+ }
+
public function type(): string
{
return 'standalone-mariadb';
@@ -183,12 +192,32 @@ public function portsMappingsArray(): Attribute
);
}
- public function get_db_url(bool $useInternal = false): string
+ protected function internalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: fn () => "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}",
+ );
+ }
+
+ protected function externalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: function () {
+ if ($this->is_public && $this->public_port) {
+ return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";
+ }
+
+ return null;
+ }
+ );
+ }
+
+ public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && ! $useInternal) {
- return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";
+ return $this->externalDbUrl;
} else {
- return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}";
+ return $this->internalDbUrl;
}
}
diff --git a/app/Models/StandaloneMongodb.php b/app/Models/StandaloneMongodb.php
index df895bb34..0863522a8 100644
--- a/app/Models/StandaloneMongodb.php
+++ b/app/Models/StandaloneMongodb.php
@@ -13,6 +13,8 @@ class StandaloneMongodb extends BaseModel
protected $guarded = [];
+ protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
+
protected static function booted()
{
static::created(function ($database) {
@@ -198,17 +200,44 @@ public function portsMappingsArray(): Attribute
);
}
+ public function databaseType(): Attribute
+ {
+ return new Attribute(
+ get: fn () => $this->type(),
+ );
+ }
+
public function type(): string
{
return 'standalone-mongodb';
}
+ protected function internalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: fn () => "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->uuid}:27017/?directConnection=true",
+ );
+ }
+
+ protected function externalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: function () {
+ if ($this->is_public && $this->public_port) {
+ return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";
+ }
+
+ return null;
+ }
+ );
+ }
+
public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && ! $useInternal) {
- return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";
+ return $this->externalDbUrl;
} else {
- return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->uuid}:27017/?directConnection=true";
+ return $this->internalDbUrl;
}
}
diff --git a/app/Models/StandaloneMysql.php b/app/Models/StandaloneMysql.php
index bd160f877..79e7c37fa 100644
--- a/app/Models/StandaloneMysql.php
+++ b/app/Models/StandaloneMysql.php
@@ -13,6 +13,8 @@ class StandaloneMysql extends BaseModel
protected $guarded = [];
+ protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
+
protected $casts = [
'mysql_password' => 'encrypted',
'mysql_root_password' => 'encrypted',
@@ -157,6 +159,13 @@ public function link()
return null;
}
+ public function databaseType(): Attribute
+ {
+ return new Attribute(
+ get: fn () => $this->type(),
+ );
+ }
+
public function type(): string
{
return 'standalone-mysql';
@@ -184,12 +193,32 @@ public function portsMappingsArray(): Attribute
);
}
- public function get_db_url(bool $useInternal = false): string
+ protected function internalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: fn () => "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->uuid}:3306/{$this->mysql_database}",
+ );
+ }
+
+ protected function externalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: function () {
+ if ($this->is_public && $this->public_port) {
+ return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}";
+ }
+
+ return null;
+ }
+ );
+ }
+
+ public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && ! $useInternal) {
- return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}";
+ return $this->externalDbUrl;
} else {
- return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->uuid}:3306/{$this->mysql_database}";
+ return $this->internalDbUrl;
}
}
diff --git a/app/Models/StandalonePostgresql.php b/app/Models/StandalonePostgresql.php
index 114d376e8..1d5276cf3 100644
--- a/app/Models/StandalonePostgresql.php
+++ b/app/Models/StandalonePostgresql.php
@@ -13,6 +13,8 @@ class StandalonePostgresql extends BaseModel
protected $guarded = [];
+ protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
+
protected $casts = [
'init_scripts' => 'array',
'postgres_password' => 'encrypted',
@@ -179,17 +181,44 @@ public function team()
return data_get($this, 'environment.project.team');
}
+ public function databaseType(): Attribute
+ {
+ return new Attribute(
+ get: fn () => $this->type(),
+ );
+ }
+
public function type(): string
{
return 'standalone-postgresql';
}
- public function get_db_url(bool $useInternal = false): string
+ protected function internalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: fn () => "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}",
+ );
+ }
+
+ protected function externalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: function () {
+ if ($this->is_public && $this->public_port) {
+ return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}";
+ }
+
+ return null;
+ }
+ );
+ }
+
+ public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && ! $useInternal) {
- return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}";
+ return $this->externalDbUrl;
} else {
- return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}";
+ return $this->internalDbUrl;
}
}
diff --git a/app/Models/StandaloneRedis.php b/app/Models/StandaloneRedis.php
index 022cd8d09..e0f863aca 100644
--- a/app/Models/StandaloneRedis.php
+++ b/app/Models/StandaloneRedis.php
@@ -13,6 +13,8 @@ class StandaloneRedis extends BaseModel
protected $guarded = [];
+ protected $appends = ['internal_db_url', 'external_db_url', 'database_type'];
+
protected static function booted()
{
static::created(function ($database) {
@@ -179,12 +181,39 @@ public function type(): string
return 'standalone-redis';
}
- public function get_db_url(bool $useInternal = false): string
+ public function databaseType(): Attribute
+ {
+ return new Attribute(
+ get: fn () => $this->type(),
+ );
+ }
+
+ protected function internalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: fn () => "redis://:{$this->redis_password}@{$this->uuid}:6379/0",
+ );
+ }
+
+ protected function externalDbUrl(): Attribute
+ {
+ return new Attribute(
+ get: function () {
+ if ($this->is_public && $this->public_port) {
+ return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
+ }
+
+ return null;
+ }
+ );
+ }
+
+ public function get_db_url(bool $useInternal = false)
{
if ($this->is_public && ! $useInternal) {
- return "redis://:{$this->redis_password}@{$this->destination->server->getIp}:{$this->public_port}/0";
+ return $this->externalDbUrl;
} else {
- return "redis://:{$this->redis_password}@{$this->uuid}:6379/0";
+ return $this->internalDbUrl;
}
}
diff --git a/bootstrap/helpers/api.php b/bootstrap/helpers/api.php
index f5c99dbda..c5083534f 100644
--- a/bootstrap/helpers/api.php
+++ b/bootstrap/helpers/api.php
@@ -3,25 +3,27 @@
use App\Enums\BuildPackTypes;
use App\Enums\RedirectTypes;
use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
-function get_team_id_from_token()
+function getTeamIdFromToken()
{
$token = auth()->user()->currentAccessToken();
return data_get($token, 'team_id');
}
-function invalid_token()
+function invalidTokenResponse()
{
- return response()->json(['error' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api-reference/authorization'], 400);
+ return response()->json(['success' => false, 'message' => 'Invalid token.', 'docs' => 'https://coolify.io/docs/api-reference/authorization'], 400);
}
-function serialize_api_response($data)
+function serializeApiResponse($data)
{
if (! $data instanceof Collection) {
$data = collect($data);
}
$data = $data->sortKeys();
+
$created_at = data_get($data, 'created_at');
$updated_at = data_get($data, 'updated_at');
if ($created_at) {
@@ -33,6 +35,16 @@ function serialize_api_response($data)
unset($data['updated_at']);
$data['updated_at'] = $updated_at;
}
+ if (data_get($data, 'name')) {
+ $data = $data->prepend($data['name'], 'name');
+ }
+ if (data_get($data, 'description')) {
+ $data = $data->prepend($data['description'], 'description');
+ }
+ if (data_get($data, 'uuid')) {
+ $data = $data->prepend($data['uuid'], 'uuid');
+ }
+
if (data_get($data, 'id')) {
$data = $data->prepend($data['id'], 'id');
}
@@ -90,3 +102,36 @@ function sharedDataApplications()
'manual_webhook_secret_gitea' => 'string|nullable',
];
}
+
+function validateIncomingRequest(Request $request)
+{
+ // check if request is json
+ if (! $request->isJson()) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Invalid request.',
+ 'error' => 'Content-Type must be application/json.',
+ ], 400);
+ }
+ // check if request is valid json
+ if (! json_decode($request->getContent())) {
+ return response()->json([
+ 'success' => false,
+ 'message' => 'Invalid request.',
+ 'error' => 'Invalid JSON.',
+ ], 400);
+ }
+}
+
+function removeUnnecessaryFieldsFromRequest(Request $request)
+{
+ $request->offsetUnset('project_uuid');
+ $request->offsetUnset('environment_name');
+ $request->offsetUnset('destination_uuid');
+ $request->offsetUnset('server_uuid');
+ $request->offsetUnset('type');
+ $request->offsetUnset('domains');
+ $request->offsetUnset('instant_deploy');
+ $request->offsetUnset('github_app_uuid');
+ $request->offsetUnset('private_key_uuid');
+}
diff --git a/bootstrap/helpers/databases.php b/bootstrap/helpers/databases.php
index dba8aa543..ef3f8ac9b 100644
--- a/bootstrap/helpers/databases.php
+++ b/bootstrap/helpers/databases.php
@@ -19,131 +19,163 @@ function generate_database_name(string $type): string
return $type.'-database-'.$cuid;
}
-function create_standalone_postgresql($environment_id, $destination_uuid): StandalonePostgresql
+function create_standalone_postgresql($environmentId, $destinationUuid, ?array $otherData = null): StandalonePostgresql
{
- // TODO: If another type of destination is added, this will need to be updated.
- $destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
+ $destination = StandaloneDocker::where('uuid', $destinationUuid)->first();
if (! $destination) {
throw new Exception('Destination not found');
}
+ $database = new StandalonePostgresql();
+ $database->name = generate_database_name('postgresql');
+ $database->postgres_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->environment_id = $environmentId;
+ $database->destination_id = $destination->id;
+ $database->destination_type = $destination->getMorphClass();
+ if ($otherData) {
+ $database->fill($otherData);
+ }
+ $database->save();
- return StandalonePostgresql::create([
- 'name' => generate_database_name('postgresql'),
- 'postgres_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'environment_id' => $environment_id,
- 'destination_id' => $destination->id,
- 'destination_type' => $destination->getMorphClass(),
- ]);
+ return $database;
}
-function create_standalone_redis($environment_id, $destination_uuid): StandaloneRedis
+function create_standalone_redis($environment_id, $destination_uuid, ?array $otherData = null): StandaloneRedis
{
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
throw new Exception('Destination not found');
}
+ $database = new StandaloneRedis();
+ $database->name = generate_database_name('redis');
+ $database->redis_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->environment_id = $environment_id;
+ $database->destination_id = $destination->id;
+ $database->destination_type = $destination->getMorphClass();
+ if ($otherData) {
+ $database->fill($otherData);
+ }
+ $database->save();
- return StandaloneRedis::create([
- 'name' => generate_database_name('redis'),
- 'redis_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'environment_id' => $environment_id,
- 'destination_id' => $destination->id,
- 'destination_type' => $destination->getMorphClass(),
- ]);
+ return $database;
}
-function create_standalone_mongodb($environment_id, $destination_uuid): StandaloneMongodb
+function create_standalone_mongodb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMongodb
{
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
throw new Exception('Destination not found');
}
+ $database = new StandaloneMongodb();
+ $database->name = generate_database_name('mongodb');
+ $database->mongo_initdb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->environment_id = $environment_id;
+ $database->destination_id = $destination->id;
+ $database->destination_type = $destination->getMorphClass();
+ if ($otherData) {
+ $database->fill($otherData);
+ }
+ $database->save();
- return StandaloneMongodb::create([
- 'name' => generate_database_name('mongodb'),
- 'mongo_initdb_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'environment_id' => $environment_id,
- 'destination_id' => $destination->id,
- 'destination_type' => $destination->getMorphClass(),
- ]);
+ return $database;
}
-function create_standalone_mysql($environment_id, $destination_uuid): StandaloneMysql
+function create_standalone_mysql($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMysql
{
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
throw new Exception('Destination not found');
}
+ $database = new StandaloneMysql();
+ $database->name = generate_database_name('mysql');
+ $database->mysql_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->mysql_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->environment_id = $environment_id;
+ $database->destination_id = $destination->id;
+ $database->destination_type = $destination->getMorphClass();
+ if ($otherData) {
+ $database->fill($otherData);
+ }
+ $database->save();
- return StandaloneMysql::create([
- 'name' => generate_database_name('mysql'),
- 'mysql_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'mysql_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'environment_id' => $environment_id,
- 'destination_id' => $destination->id,
- 'destination_type' => $destination->getMorphClass(),
- ]);
+ return $database;
}
-function create_standalone_mariadb($environment_id, $destination_uuid): StandaloneMariadb
+function create_standalone_mariadb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneMariadb
{
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
throw new Exception('Destination not found');
}
+ $database = new StandaloneMariadb();
+ $database->name = generate_database_name('mariadb');
+ $database->mariadb_root_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->mariadb_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->environment_id = $environment_id;
+ $database->destination_id = $destination->id;
+ $database->destination_type = $destination->getMorphClass();
- return StandaloneMariadb::create([
- 'name' => generate_database_name('mariadb'),
- 'mariadb_root_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'mariadb_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'environment_id' => $environment_id,
- 'destination_id' => $destination->id,
- 'destination_type' => $destination->getMorphClass(),
- ]);
+ if ($otherData) {
+ $database->fill($otherData);
+ }
+ $database->save();
+
+ return $database;
}
-function create_standalone_keydb($environment_id, $destination_uuid): StandaloneKeydb
+function create_standalone_keydb($environment_id, $destination_uuid, ?array $otherData = null): StandaloneKeydb
{
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
throw new Exception('Destination not found');
}
+ $database = new StandaloneKeydb();
+ $database->name = generate_database_name('keydb');
+ $database->keydb_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->environment_id = $environment_id;
+ $database->destination_id = $destination->id;
+ $database->destination_type = $destination->getMorphClass();
+ if ($otherData) {
+ $database->fill($otherData);
+ }
+ $database->save();
- return StandaloneKeydb::create([
- 'name' => generate_database_name('keydb'),
- 'keydb_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'environment_id' => $environment_id,
- 'destination_id' => $destination->id,
- 'destination_type' => $destination->getMorphClass(),
- ]);
+ return $database;
}
-function create_standalone_dragonfly($environment_id, $destination_uuid): StandaloneDragonfly
+function create_standalone_dragonfly($environment_id, $destination_uuid, ?array $otherData = null): StandaloneDragonfly
{
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
throw new Exception('Destination not found');
}
+ $database = new StandaloneDragonfly();
+ $database->name = generate_database_name('dragonfly');
+ $database->dragonfly_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->environment_id = $environment_id;
+ $database->destination_id = $destination->id;
+ $database->destination_type = $destination->getMorphClass();
+ if ($otherData) {
+ $database->fill($otherData);
+ }
+ $database->save();
- return StandaloneDragonfly::create([
- 'name' => generate_database_name('dragonfly'),
- 'dragonfly_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'environment_id' => $environment_id,
- 'destination_id' => $destination->id,
- 'destination_type' => $destination->getMorphClass(),
- ]);
+ return $database;
}
-function create_standalone_clickhouse($environment_id, $destination_uuid): StandaloneClickhouse
+function create_standalone_clickhouse($environment_id, $destination_uuid, ?array $otherData = null): StandaloneClickhouse
{
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
if (! $destination) {
throw new Exception('Destination not found');
}
+ $database = new StandaloneClickhouse();
+ $database->name = generate_database_name('clickhouse');
+ $database->clickhouse_admin_password = \Illuminate\Support\Str::password(length: 64, symbols: false);
+ $database->environment_id = $environment_id;
+ $database->destination_id = $destination->id;
+ $database->destination_type = $destination->getMorphClass();
+ if ($otherData) {
+ $database->fill($otherData);
+ }
+ $database->save();
- return StandaloneClickhouse::create([
- 'name' => generate_database_name('clickhouse'),
- 'clickhouse_admin_password' => \Illuminate\Support\Str::password(length: 64, symbols: false),
- 'environment_id' => $environment_id,
- 'destination_id' => $destination->id,
- 'destination_type' => $destination->getMorphClass(),
- ]);
+ return $database;
}
/**
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 1d70b674c..5efc0f9ef 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -538,6 +538,43 @@ function getResourceByUuid(string $uuid, ?int $teamId = null)
return null;
}
+function queryDatabaseByUuidWithinTeam(string $uuid, string $teamId)
+{
+ $postgresql = StandalonePostgresql::whereUuid($uuid)->first();
+ if ($postgresql && $postgresql->team()->id == $teamId) {
+ return $postgresql->unsetRelation('environment')->unsetRelation('destination');
+ }
+ $redis = StandaloneRedis::whereUuid($uuid)->first();
+ if ($redis && $redis->team()->id == $teamId) {
+ return $redis->unsetRelation('environment');
+ }
+ $mongodb = StandaloneMongodb::whereUuid($uuid)->first();
+ if ($mongodb && $mongodb->team()->id == $teamId) {
+ return $mongodb->unsetRelation('environment');
+ }
+ $mysql = StandaloneMysql::whereUuid($uuid)->first();
+ if ($mysql && $mysql->team()->id == $teamId) {
+ return $mysql->unsetRelation('environment');
+ }
+ $mariadb = StandaloneMariadb::whereUuid($uuid)->first();
+ if ($mariadb && $mariadb->team()->id == $teamId) {
+ return $mariadb->unsetRelation('environment');
+ }
+ $keydb = StandaloneKeydb::whereUuid($uuid)->first();
+ if ($keydb && $keydb->team()->id == $teamId) {
+ return $keydb->unsetRelation('environment');
+ }
+ $dragonfly = StandaloneDragonfly::whereUuid($uuid)->first();
+ if ($dragonfly && $dragonfly->team()->id == $teamId) {
+ return $dragonfly->unsetRelation('environment');
+ }
+ $clickhouse = StandaloneClickhouse::whereUuid($uuid)->first();
+ if ($clickhouse && $clickhouse->team()->id == $teamId) {
+ return $clickhouse->unsetRelation('environment');
+ }
+
+ return null;
+}
function queryResourcesByUuid(string $uuid)
{
$resource = null;
diff --git a/database/migrations/2024_07_01_115528_add_is_api_allowed_and_iplist.php b/database/migrations/2024_07_01_115528_add_is_api_allowed_and_iplist.php
new file mode 100644
index 000000000..b319adb70
--- /dev/null
+++ b/database/migrations/2024_07_01_115528_add_is_api_allowed_and_iplist.php
@@ -0,0 +1,24 @@
+boolean('is_api_enabled')->default(true);
+ $table->text('allowed_ips')->nullable();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('instance_settings', function (Blueprint $table) {
+ $table->dropColumn('is_api_enabled');
+ $table->dropColumn('allowed_ips');
+ });
+ }
+};
diff --git a/resources/views/livewire/settings/configuration.blade.php b/resources/views/livewire/settings/configuration.blade.php
index b1c399bc3..b5fb49d3e 100644
--- a/resources/views/livewire/settings/configuration.blade.php
+++ b/resources/views/livewire/settings/configuration.blade.php
@@ -25,7 +25,16 @@