mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-27 17:55:59 -04:00
feat(api): support sqlite databases in the databases API
This commit is contained in:
@@ -21,6 +21,7 @@ use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneSqlite;
|
||||
use App\Models\SwarmDocker;
|
||||
use App\Support\ValidationPatterns;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -482,6 +483,7 @@ class DatabasesController extends Controller
|
||||
'mysql_user' => ['type' => 'string', 'description' => 'MySQL user'],
|
||||
'mysql_database' => ['type' => 'string', 'description' => 'MySQL database'],
|
||||
'mysql_conf' => ['type' => 'string', 'description' => 'MySQL conf'],
|
||||
'sqlite_databases' => ['type' => 'string', 'description' => 'Comma-separated SQLite database file names'],
|
||||
'health_check_enabled' => ['type' => 'boolean', 'description' => 'Enable the database healthcheck probe.', 'default' => true],
|
||||
'health_check_interval' => ['type' => 'integer', 'description' => 'Healthcheck interval in seconds.', 'minimum' => 1, 'default' => 15],
|
||||
'health_check_timeout' => ['type' => 'integer', 'description' => 'Healthcheck timeout in seconds.', 'minimum' => 1, 'default' => 5],
|
||||
@@ -516,7 +518,7 @@ class DatabasesController extends Controller
|
||||
)]
|
||||
public function update_by_uuid(Request $request)
|
||||
{
|
||||
$allowedFields = ['name', 'description', 'image', 'public_port', 'public_port_timeout', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf'];
|
||||
$allowedFields = ['name', 'description', 'image', 'public_port', 'public_port_timeout', 'is_public', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf', 'sqlite_databases'];
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
return invalidTokenResponse();
|
||||
@@ -752,6 +754,12 @@ class DatabasesController extends Controller
|
||||
$request->offsetSet('mysql_conf', $mysqlConf);
|
||||
}
|
||||
break;
|
||||
case 'standalone-sqlite':
|
||||
$allowedFields = ['name', 'description', 'image', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'sqlite_databases'];
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'sqlite_databases' => ['string', 'max:255', 'regex:'.StandaloneSqlite::DATABASES_PATTERN],
|
||||
]);
|
||||
break;
|
||||
}
|
||||
$allowedFields = array_merge($allowedFields, ['health_check_enabled', 'health_check_interval', 'health_check_timeout', 'health_check_retries', 'health_check_start_period']);
|
||||
$healthCheckValidator = customApiValidator($request->all(), [
|
||||
@@ -1008,6 +1016,8 @@ class DatabasesController extends Controller
|
||||
$backupData['databases_to_backup'] = $database->mariadb_database;
|
||||
} elseif ($database->type() === 'standalone-clickhouse') {
|
||||
$backupData['databases_to_backup'] = $database->clickhouse_db;
|
||||
} elseif ($database->type() === 'standalone-sqlite') {
|
||||
$backupData['databases_to_backup'] = $database->sqlite_databases;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1844,9 +1854,74 @@ class DatabasesController extends Controller
|
||||
return $this->create_database($request, NewDatabaseTypes::MONGODB);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
summary: 'Create (SQLite)',
|
||||
description: 'Create a new SQLite database.',
|
||||
path: '/databases/sqlite',
|
||||
operationId: 'create-database-sqlite',
|
||||
security: [
|
||||
['bearerAuth' => []],
|
||||
],
|
||||
tags: ['Databases'],
|
||||
|
||||
requestBody: new OA\RequestBody(
|
||||
description: 'Database data',
|
||||
required: true,
|
||||
content: new OA\MediaType(
|
||||
mediaType: 'application/json',
|
||||
schema: new OA\Schema(
|
||||
type: 'object',
|
||||
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
|
||||
properties: [
|
||||
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
|
||||
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
|
||||
'environment_name' => ['type' => 'string', 'description' => 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'],
|
||||
'environment_uuid' => ['type' => 'string', 'description' => 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'],
|
||||
'destination_uuid' => ['type' => 'string', 'description' => 'UUID of the destination if the server has multiple destinations'],
|
||||
'sqlite_databases' => ['type' => 'string', 'description' => 'Comma-separated SQLite database file names'],
|
||||
'name' => ['type' => 'string', 'description' => 'Name of the database'],
|
||||
'description' => ['type' => 'string', 'description' => 'Description of the database'],
|
||||
'image' => ['type' => 'string', 'description' => 'Docker Image of the database'],
|
||||
'limits_memory' => ['type' => 'string', 'description' => 'Memory limit of the database'],
|
||||
'limits_memory_swap' => ['type' => 'string', 'description' => 'Memory swap limit of the database'],
|
||||
'limits_memory_swappiness' => ['type' => 'integer', 'description' => 'Memory swappiness of the database'],
|
||||
'limits_memory_reservation' => ['type' => 'string', 'description' => 'Memory reservation of the database'],
|
||||
'limits_cpus' => ['type' => 'string', 'description' => 'CPU limit of the database'],
|
||||
'limits_cpuset' => ['type' => 'string', 'description' => 'CPU set of the database'],
|
||||
'limits_cpu_shares' => ['type' => 'integer', 'description' => 'CPU shares of the database'],
|
||||
'instant_deploy' => ['type' => 'boolean', 'description' => 'Instant deploy the database'],
|
||||
'tags' => ['type' => 'array', 'items' => new OA\Items(type: 'string'), 'description' => 'Tags to assign to the database.'],
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
responses: [
|
||||
new OA\Response(
|
||||
response: 200,
|
||||
description: 'Database updated',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 401,
|
||||
ref: '#/components/responses/401',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 400,
|
||||
ref: '#/components/responses/400',
|
||||
),
|
||||
new OA\Response(
|
||||
response: 422,
|
||||
ref: '#/components/responses/422',
|
||||
),
|
||||
]
|
||||
)]
|
||||
public function create_database_sqlite(Request $request)
|
||||
{
|
||||
return $this->create_database($request, NewDatabaseTypes::SQLITE);
|
||||
}
|
||||
|
||||
public function create_database(Request $request, NewDatabaseTypes $type)
|
||||
{
|
||||
$allowedFields = ['name', 'description', 'image', 'public_port', 'public_port_timeout', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf', 'tags'];
|
||||
$allowedFields = ['name', 'description', 'image', 'public_port', 'public_port_timeout', 'is_public', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'postgres_user', 'postgres_password', 'postgres_db', 'postgres_initdb_args', 'postgres_host_auth_method', 'postgres_conf', 'clickhouse_admin_user', 'clickhouse_admin_password', 'dragonfly_password', 'redis_password', 'redis_conf', 'keydb_password', 'keydb_conf', 'mariadb_conf', 'mariadb_root_password', 'mariadb_user', 'mariadb_password', 'mariadb_database', 'mongo_conf', 'mongo_initdb_root_username', 'mongo_initdb_root_password', 'mongo_initdb_database', 'mysql_root_password', 'mysql_password', 'mysql_user', 'mysql_database', 'mysql_conf', 'sqlite_databases', 'tags'];
|
||||
|
||||
$teamId = getTeamIdFromToken();
|
||||
if (is_null($teamId)) {
|
||||
@@ -2488,6 +2563,47 @@ class DatabasesController extends Controller
|
||||
]);
|
||||
|
||||
return response()->json(serializeApiResponse($payload))->setStatusCode(201);
|
||||
} elseif ($type === NewDatabaseTypes::SQLITE) {
|
||||
$allowedFields = ['name', 'description', 'image', 'project_uuid', 'environment_name', 'environment_uuid', 'server_uuid', 'destination_uuid', 'instant_deploy', 'limits_memory', 'limits_memory_swap', 'limits_memory_swappiness', 'limits_memory_reservation', 'limits_cpus', 'limits_cpuset', 'limits_cpu_shares', 'sqlite_databases', 'tags'];
|
||||
$validator = customApiValidator($request->all(), [
|
||||
'sqlite_databases' => ['string', 'max:255', 'regex:'.StandaloneSqlite::DATABASES_PATTERN],
|
||||
]);
|
||||
$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([
|
||||
'message' => 'Validation failed.',
|
||||
'errors' => $errors,
|
||||
], 422);
|
||||
}
|
||||
removeUnnecessaryFieldsFromRequest($request);
|
||||
$database = create_standalone_sqlite($environment->id, $destination, $request->only($allowedFields));
|
||||
if ($instantDeploy) {
|
||||
StartDatabase::dispatch($database);
|
||||
}
|
||||
if ($tagNames !== []) {
|
||||
$this->attachTagsToResource($database, $tagNames, $teamId);
|
||||
}
|
||||
|
||||
auditLog('api.database.created', [
|
||||
'team_id' => $teamId,
|
||||
'database_uuid' => $database->uuid,
|
||||
'database_name' => $database->name,
|
||||
'database_type' => $type->value,
|
||||
'server_uuid' => $serverUuid,
|
||||
'is_public' => (bool) $database->is_public,
|
||||
'instant_deploy' => (bool) $instantDeploy,
|
||||
]);
|
||||
|
||||
return response()->json(serializeApiResponse([
|
||||
'uuid' => $database->uuid,
|
||||
]))->setStatusCode(201);
|
||||
}
|
||||
|
||||
return response()->json(['message' => 'Invalid database type requested.'], 400);
|
||||
@@ -4900,6 +5016,7 @@ class DatabasesController extends Controller
|
||||
str_starts_with($originalName, 'mongodb-data-') => 'mongodb-data-'.$newDatabase->uuid,
|
||||
str_starts_with($originalName, 'keydb-data-') => 'keydb-data-'.$newDatabase->uuid,
|
||||
str_starts_with($originalName, 'dragonfly-data-') => 'dragonfly-data-'.$newDatabase->uuid,
|
||||
str_starts_with($originalName, 'sqlite-data-') => 'sqlite-data-'.$newDatabase->uuid,
|
||||
str_starts_with($volume->name, $database->uuid) => str($volume->name)->replace($database->uuid, $newDatabase->uuid)->toString(),
|
||||
default => $newDatabase->uuid.'-'.$volume->name,
|
||||
};
|
||||
|
||||
@@ -405,6 +405,7 @@ class DestinationsController extends Controller
|
||||
|| (method_exists($destination, 'keydbs') && $destination->keydbs()->exists())
|
||||
|| (method_exists($destination, 'dragonflies') && $destination->dragonflies()->exists())
|
||||
|| (method_exists($destination, 'clickhouses') && $destination->clickhouses()->exists())
|
||||
|| (method_exists($destination, 'sqlites') && $destination->sqlites()->exists())
|
||||
|| (method_exists($destination, 'services') && $destination->services()->exists());
|
||||
if ($hasAttached) {
|
||||
return response()->json(['message' => 'Destination has attached resources, detach first.'], 409);
|
||||
|
||||
+127
@@ -6706,6 +6706,10 @@
|
||||
"type": "string",
|
||||
"description": "MySQL conf"
|
||||
},
|
||||
"sqlite_databases": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated SQLite database file names"
|
||||
},
|
||||
"health_check_enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Enable the database healthcheck probe.",
|
||||
@@ -8118,6 +8122,129 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/databases\/sqlite": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Databases"
|
||||
],
|
||||
"summary": "Create (SQLite)",
|
||||
"description": "Create a new SQLite database.",
|
||||
"operationId": "create-database-sqlite",
|
||||
"requestBody": {
|
||||
"description": "Database data",
|
||||
"required": true,
|
||||
"content": {
|
||||
"application\/json": {
|
||||
"schema": {
|
||||
"required": [
|
||||
"server_uuid",
|
||||
"project_uuid",
|
||||
"environment_name",
|
||||
"environment_uuid"
|
||||
],
|
||||
"properties": {
|
||||
"server_uuid": {
|
||||
"type": "string",
|
||||
"description": "UUID of the server"
|
||||
},
|
||||
"project_uuid": {
|
||||
"type": "string",
|
||||
"description": "UUID of the project"
|
||||
},
|
||||
"environment_name": {
|
||||
"type": "string",
|
||||
"description": "Name of the environment. You need to provide at least one of environment_name or environment_uuid."
|
||||
},
|
||||
"environment_uuid": {
|
||||
"type": "string",
|
||||
"description": "UUID of the environment. You need to provide at least one of environment_name or environment_uuid."
|
||||
},
|
||||
"destination_uuid": {
|
||||
"type": "string",
|
||||
"description": "UUID of the destination if the server has multiple destinations"
|
||||
},
|
||||
"sqlite_databases": {
|
||||
"type": "string",
|
||||
"description": "Comma-separated SQLite database file names"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the database"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Description of the database"
|
||||
},
|
||||
"image": {
|
||||
"type": "string",
|
||||
"description": "Docker Image of the database"
|
||||
},
|
||||
"limits_memory": {
|
||||
"type": "string",
|
||||
"description": "Memory limit of the database"
|
||||
},
|
||||
"limits_memory_swap": {
|
||||
"type": "string",
|
||||
"description": "Memory swap limit of the database"
|
||||
},
|
||||
"limits_memory_swappiness": {
|
||||
"type": "integer",
|
||||
"description": "Memory swappiness of the database"
|
||||
},
|
||||
"limits_memory_reservation": {
|
||||
"type": "string",
|
||||
"description": "Memory reservation of the database"
|
||||
},
|
||||
"limits_cpus": {
|
||||
"type": "string",
|
||||
"description": "CPU limit of the database"
|
||||
},
|
||||
"limits_cpuset": {
|
||||
"type": "string",
|
||||
"description": "CPU set of the database"
|
||||
},
|
||||
"limits_cpu_shares": {
|
||||
"type": "integer",
|
||||
"description": "CPU shares of the database"
|
||||
},
|
||||
"instant_deploy": {
|
||||
"type": "boolean",
|
||||
"description": "Instant deploy the database"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Tags to assign to the database."
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Database updated"
|
||||
},
|
||||
"401": {
|
||||
"$ref": "#\/components\/responses\/401"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#\/components\/responses\/400"
|
||||
},
|
||||
"422": {
|
||||
"$ref": "#\/components\/responses\/422"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"\/databases\/{uuid}\/logs": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
@@ -4343,6 +4343,9 @@ paths:
|
||||
mysql_conf:
|
||||
type: string
|
||||
description: 'MySQL conf'
|
||||
sqlite_databases:
|
||||
type: string
|
||||
description: 'Comma-separated SQLite database file names'
|
||||
health_check_enabled:
|
||||
type: boolean
|
||||
description: 'Enable the database healthcheck probe.'
|
||||
@@ -5342,6 +5345,93 @@ paths:
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
/databases/sqlite:
|
||||
post:
|
||||
tags:
|
||||
- Databases
|
||||
summary: 'Create (SQLite)'
|
||||
description: 'Create a new SQLite database.'
|
||||
operationId: create-database-sqlite
|
||||
requestBody:
|
||||
description: 'Database data'
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
required:
|
||||
- server_uuid
|
||||
- project_uuid
|
||||
- environment_name
|
||||
- environment_uuid
|
||||
properties:
|
||||
server_uuid:
|
||||
type: string
|
||||
description: 'UUID of the server'
|
||||
project_uuid:
|
||||
type: string
|
||||
description: 'UUID of the project'
|
||||
environment_name:
|
||||
type: string
|
||||
description: 'Name of the environment. You need to provide at least one of environment_name or environment_uuid.'
|
||||
environment_uuid:
|
||||
type: string
|
||||
description: 'UUID of the environment. You need to provide at least one of environment_name or environment_uuid.'
|
||||
destination_uuid:
|
||||
type: string
|
||||
description: 'UUID of the destination if the server has multiple destinations'
|
||||
sqlite_databases:
|
||||
type: string
|
||||
description: 'Comma-separated SQLite database file names'
|
||||
name:
|
||||
type: string
|
||||
description: 'Name of the database'
|
||||
description:
|
||||
type: string
|
||||
description: 'Description of the database'
|
||||
image:
|
||||
type: string
|
||||
description: 'Docker Image of the database'
|
||||
limits_memory:
|
||||
type: string
|
||||
description: 'Memory limit of the database'
|
||||
limits_memory_swap:
|
||||
type: string
|
||||
description: 'Memory swap limit of the database'
|
||||
limits_memory_swappiness:
|
||||
type: integer
|
||||
description: 'Memory swappiness of the database'
|
||||
limits_memory_reservation:
|
||||
type: string
|
||||
description: 'Memory reservation of the database'
|
||||
limits_cpus:
|
||||
type: string
|
||||
description: 'CPU limit of the database'
|
||||
limits_cpuset:
|
||||
type: string
|
||||
description: 'CPU set of the database'
|
||||
limits_cpu_shares:
|
||||
type: integer
|
||||
description: 'CPU shares of the database'
|
||||
instant_deploy:
|
||||
type: boolean
|
||||
description: 'Instant deploy the database'
|
||||
tags:
|
||||
type: array
|
||||
items: { type: string }
|
||||
description: 'Tags to assign to the database.'
|
||||
type: object
|
||||
responses:
|
||||
'200':
|
||||
description: 'Database updated'
|
||||
'401':
|
||||
$ref: '#/components/responses/401'
|
||||
'400':
|
||||
$ref: '#/components/responses/400'
|
||||
'422':
|
||||
$ref: '#/components/responses/422'
|
||||
security:
|
||||
-
|
||||
bearerAuth: []
|
||||
'/databases/{uuid}/logs':
|
||||
get:
|
||||
tags:
|
||||
|
||||
@@ -309,6 +309,7 @@ Route::group([
|
||||
Route::post('/databases/clickhouse', [DatabasesController::class, 'create_database_clickhouse'])->middleware(['api.ability:write']);
|
||||
Route::post('/databases/dragonfly', [DatabasesController::class, 'create_database_dragonfly'])->middleware(['api.ability:write']);
|
||||
Route::post('/databases/keydb', [DatabasesController::class, 'create_database_keydb'])->middleware(['api.ability:write']);
|
||||
Route::post('/databases/sqlite', [DatabasesController::class, 'create_database_sqlite'])->middleware(['api.ability:write']);
|
||||
|
||||
Route::get('/databases/{uuid}', [DatabasesController::class, 'database_by_uuid'])->middleware(['api.ability:read']);
|
||||
Route::post('/databases/{uuid}/imports/uploads', [DatabasesController::class, 'upload_import'])->middleware(['api.ability:deploy'])->name('api.databases.imports.upload');
|
||||
|
||||
Reference in New Issue
Block a user