fix(api): document database and resource list responses in OpenAPI

Add a Database schema and use it for GET /databases and
GET /databases/{uuid}, type the GET /resources items, and stop
requiring both environment_name and environment_uuid on create
endpoints. KeyDB now returns database_type like the other engines.

Closes #10449

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-10-27 14:42:29 +01:00
co-authored by Claude Opus 5.5
parent 5a08c35e7d
commit 80df5431dc
10 changed files with 984 additions and 115 deletions
@@ -381,7 +381,7 @@ class ApplicationsController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'git_repository', 'git_branch', 'build_pack'],
required: ['project_uuid', 'server_uuid', 'git_repository', 'git_branch', 'build_pack'],
properties: [
'project_uuid' => ['type' => 'string', 'description' => 'The project UUID.'],
'server_uuid' => ['type' => 'string', 'description' => 'The server UUID.'],
@@ -591,7 +591,7 @@ class ApplicationsController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'github_app_uuid', 'git_repository', 'git_branch', 'build_pack'],
required: ['project_uuid', 'server_uuid', 'github_app_uuid', 'git_repository', 'git_branch', 'build_pack'],
properties: [
'project_uuid' => ['type' => 'string', 'description' => 'The project UUID.'],
'server_uuid' => ['type' => 'string', 'description' => 'The server UUID.'],
@@ -801,7 +801,7 @@ class ApplicationsController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'private_key_uuid', 'git_repository', 'git_branch', 'build_pack'],
required: ['project_uuid', 'server_uuid', 'private_key_uuid', 'git_repository', 'git_branch', 'build_pack'],
properties: [
'project_uuid' => ['type' => 'string', 'description' => 'The project UUID.'],
'server_uuid' => ['type' => 'string', 'description' => 'The server UUID.'],
@@ -1011,7 +1011,7 @@ class ApplicationsController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'dockerfile'],
required: ['project_uuid', 'server_uuid', 'dockerfile'],
properties: [
'project_uuid' => ['type' => 'string', 'description' => 'The project UUID.'],
'server_uuid' => ['type' => 'string', 'description' => 'The server UUID.'],
@@ -1190,7 +1190,7 @@ class ApplicationsController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['project_uuid', 'server_uuid', 'environment_name', 'environment_uuid', 'docker_registry_image_name'],
required: ['project_uuid', 'server_uuid', 'docker_registry_image_name'],
properties: [
'project_uuid' => ['type' => 'string', 'description' => 'The project UUID.'],
'server_uuid' => ['type' => 'string', 'description' => 'The server UUID.'],
@@ -352,8 +352,8 @@ class DatabasesController extends Controller
response: 200,
description: 'Get all databases',
content: new OA\JsonContent(
type: 'string',
example: 'Content is very complex. Will be implemented later.',
type: 'array',
items: new OA\Items(ref: '#/components/schemas/Database'),
),
),
new OA\Response(
@@ -511,11 +511,8 @@ class DatabasesController extends Controller
responses: [
new OA\Response(
response: 200,
description: 'Get all databases',
content: new OA\JsonContent(
type: 'string',
example: 'Content is very complex. Will be implemented later.',
),
description: 'Get database by UUID',
content: new OA\JsonContent(ref: '#/components/schemas/Database'),
),
new OA\Response(
response: 401,
@@ -1550,7 +1547,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
@@ -1623,7 +1620,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
@@ -1692,7 +1689,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
@@ -1760,7 +1757,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
@@ -1829,7 +1826,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
@@ -1898,7 +1895,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
@@ -1970,7 +1967,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
@@ -2042,7 +2039,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
@@ -2111,7 +2108,7 @@ class DatabasesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'server_uuid' => ['type' => 'string', 'description' => 'UUID of the server'],
'project_uuid' => ['type' => 'string', 'description' => 'UUID of the project'],
+97
View File
@@ -35,6 +35,103 @@ use OpenApi\Attributes as OA;
new OA\Property(property: 'finished_at', type: 'string', format: 'date-time', nullable: true),
],
),
new OA\Schema(
schema: 'Database',
description: 'Standalone database. The fields that start with an engine name (for example postgres_ or redis_) are only returned for that engine. Passwords, connection URLs and init_scripts are only returned for tokens with the read:sensitive or root ability.',
type: 'object',
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'name', type: 'string'),
new OA\Property(property: 'description', type: 'string', nullable: true),
new OA\Property(property: 'database_type', type: 'string', enum: ['standalone-postgresql', 'standalone-redis', 'standalone-mongodb', 'standalone-mysql', 'standalone-mariadb', 'standalone-keydb', 'standalone-dragonfly', 'standalone-clickhouse', 'standalone-sqlite']),
new OA\Property(property: 'image', type: 'string'),
new OA\Property(property: 'status', type: 'string', description: 'Container status and health, for example running:healthy or exited:unhealthy.'),
new OA\Property(property: 'server_status', type: 'boolean', description: 'True when the server of the database is reachable and usable.'),
new OA\Property(property: 'environment_id', type: 'integer'),
new OA\Property(property: 'environment', ref: '#/components/schemas/Environment', description: 'Only returned by GET /databases/{uuid}. Includes the project.'),
new OA\Property(property: 'destination_id', type: 'integer'),
new OA\Property(property: 'destination_type', type: 'string'),
new OA\Property(
property: 'destination',
description: 'Docker network destination of the database, with its server.',
type: 'object',
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'name', type: 'string'),
new OA\Property(property: 'network', type: 'string'),
new OA\Property(property: 'server_id', type: 'integer'),
new OA\Property(property: 'server', ref: '#/components/schemas/Server'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
],
),
new OA\Property(property: 'is_public', type: 'boolean', description: 'Not returned for SQLite.'),
new OA\Property(property: 'public_port', type: 'integer', nullable: true, description: 'Not returned for SQLite.'),
new OA\Property(property: 'public_port_timeout', type: 'integer', nullable: true, description: 'Proxy timeout of the public port in seconds. Not returned for SQLite.'),
new OA\Property(property: 'ports_mappings', type: 'string', nullable: true, description: 'Comma-separated host:container port mappings. Not returned for SQLite.'),
new OA\Property(property: 'enable_ssl', type: 'boolean', description: 'Not returned for ClickHouse and SQLite.'),
new OA\Property(property: 'ssl_mode', type: 'string', nullable: true, description: 'PostgreSQL, MongoDB and MySQL only.'),
new OA\Property(property: 'internal_db_url', type: 'string', nullable: true, description: 'Connection URL inside the Docker network. Contains the password. Not returned for SQLite.'),
new OA\Property(property: 'external_db_url', type: 'string', nullable: true, description: 'Public connection URL, when the database is public. Contains the password. Not returned for SQLite.'),
new OA\Property(property: 'init_scripts', type: 'array', nullable: true, items: new OA\Items(type: 'object', properties: [new OA\Property(property: 'index', type: 'integer'), new OA\Property(property: 'filename', type: 'string'), new OA\Property(property: 'content', type: 'string')]), description: 'PostgreSQL only.'),
new OA\Property(property: 'custom_docker_run_options', type: 'string', nullable: true),
new OA\Property(property: 'is_log_drain_enabled', type: 'boolean'),
new OA\Property(property: 'is_include_timestamps', type: 'boolean', description: 'Not returned for MariaDB.'),
new OA\Property(property: 'include_coolify_default_variables', type: 'boolean'),
new OA\Property(property: 'redact_all_env_values_in_logs', type: 'boolean'),
new OA\Property(property: 'health_check_enabled', type: 'boolean'),
new OA\Property(property: 'health_check_interval', type: 'integer'),
new OA\Property(property: 'health_check_timeout', type: 'integer'),
new OA\Property(property: 'health_check_retries', type: 'integer'),
new OA\Property(property: 'health_check_start_period', type: 'integer'),
new OA\Property(property: 'limits_memory', type: 'string'),
new OA\Property(property: 'limits_memory_swap', type: 'string'),
new OA\Property(property: 'limits_memory_swappiness', type: 'integer'),
new OA\Property(property: 'limits_memory_reservation', type: 'string'),
new OA\Property(property: 'limits_cpus', type: 'string'),
new OA\Property(property: 'limits_cpuset', type: 'string', nullable: true),
new OA\Property(property: 'limits_cpu_shares', type: 'integer'),
new OA\Property(property: 'config_hash', type: 'string', nullable: true, description: 'Internal hash that Coolify uses to find configuration changes that need a restart. It changes only when the database starts, so do not use it to compare configurations.'),
new OA\Property(property: 'restart_count', type: 'integer'),
new OA\Property(property: 'last_restart_at', type: 'string', format: 'date-time', nullable: true),
new OA\Property(property: 'last_restart_type', type: 'string', nullable: true),
new OA\Property(property: 'last_online_at', type: 'string', nullable: true),
new OA\Property(property: 'started_at', type: 'string', format: 'date-time', nullable: true),
new OA\Property(property: 'backup_configs', type: 'array', items: new OA\Items(type: 'object'), description: 'Only returned by GET /databases. The scheduled backups of the database, as returned by GET /databases/{uuid}/backups.'),
new OA\Property(property: 'postgres_user', type: 'string'),
new OA\Property(property: 'postgres_password', type: 'string', format: 'password'),
new OA\Property(property: 'postgres_db', type: 'string'),
new OA\Property(property: 'postgres_initdb_args', type: 'string', nullable: true),
new OA\Property(property: 'postgres_host_auth_method', type: 'string', nullable: true),
new OA\Property(property: 'postgres_conf', type: 'string', nullable: true),
new OA\Property(property: 'redis_password', type: 'string', format: 'password'),
new OA\Property(property: 'redis_conf', type: 'string', nullable: true),
new OA\Property(property: 'mongo_initdb_root_username', type: 'string'),
new OA\Property(property: 'mongo_initdb_root_password', type: 'string', format: 'password'),
new OA\Property(property: 'mongo_initdb_database', type: 'string'),
new OA\Property(property: 'mongo_conf', type: 'string', nullable: true),
new OA\Property(property: 'mysql_user', type: 'string'),
new OA\Property(property: 'mysql_password', type: 'string', format: 'password'),
new OA\Property(property: 'mysql_root_password', type: 'string', format: 'password'),
new OA\Property(property: 'mysql_database', type: 'string'),
new OA\Property(property: 'mysql_conf', type: 'string', nullable: true),
new OA\Property(property: 'mariadb_user', type: 'string'),
new OA\Property(property: 'mariadb_password', type: 'string', format: 'password'),
new OA\Property(property: 'mariadb_root_password', type: 'string', format: 'password'),
new OA\Property(property: 'mariadb_database', type: 'string'),
new OA\Property(property: 'mariadb_conf', type: 'string', nullable: true),
new OA\Property(property: 'keydb_password', type: 'string', format: 'password'),
new OA\Property(property: 'keydb_conf', type: 'string', nullable: true),
new OA\Property(property: 'dragonfly_password', type: 'string', format: 'password'),
new OA\Property(property: 'clickhouse_admin_user', type: 'string'),
new OA\Property(property: 'clickhouse_admin_password', type: 'string', format: 'password'),
new OA\Property(property: 'clickhouse_db', type: 'string'),
new OA\Property(property: 'sqlite_databases', type: 'string', description: 'Comma-separated SQLite database file names.'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
new OA\Property(property: 'deleted_at', type: 'string', format: 'date-time', nullable: true),
],
),
],
responses: [
new OA\Response(
@@ -21,10 +21,23 @@ class ResourcesController extends Controller
responses: [
new OA\Response(
response: 200,
description: 'Get all resources',
description: 'Get all applications, services and standalone databases. The other fields of each resource are the fields of the Application, Service or Database schema.',
content: new OA\JsonContent(
type: 'string',
example: 'Content is very complex. Will be implemented later.',
type: 'array',
items: new OA\Items(
type: 'object',
properties: [
new OA\Property(property: 'uuid', type: 'string'),
new OA\Property(property: 'name', type: 'string'),
new OA\Property(property: 'description', type: 'string', nullable: true),
new OA\Property(property: 'type', type: 'string', description: 'application, service, or standalone-<engine> for databases (for example standalone-postgresql).'),
new OA\Property(property: 'status', type: 'string', description: 'For example running:healthy or exited:unhealthy.'),
new OA\Property(property: 'environment_id', type: 'integer'),
new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
],
additionalProperties: true,
),
),
),
new OA\Response(
@@ -302,7 +302,7 @@ class ServicesController extends Controller
mediaType: 'application/json',
schema: new OA\Schema(
type: 'object',
required: ['server_uuid', 'project_uuid', 'environment_name', 'environment_uuid'],
required: ['server_uuid', 'project_uuid'],
properties: [
'type' => ['description' => 'The one-click service type (e.g. "actualbudget", "calibre-web", "gitea-with-mysql" ...)', 'type' => 'string'],
'name' => ['type' => 'string', 'maxLength' => 255, 'description' => 'Name of the service.'],
+1 -1
View File
@@ -55,7 +55,7 @@ class StandaloneKeydb extends BaseModel
'health_check_start_period',
];
protected $appends = ['internal_db_url', 'external_db_url', 'server_status'];
protected $appends = ['internal_db_url', 'external_db_url', 'database_type', 'server_status'];
/**
* Sensitive fields hidden by default in serialized output (toArray/toJson).
+452 -51
View File
@@ -534,8 +534,6 @@
"required": [
"project_uuid",
"server_uuid",
"environment_name",
"environment_uuid",
"git_repository",
"git_branch",
"build_pack"
@@ -1187,8 +1185,6 @@
"required": [
"project_uuid",
"server_uuid",
"environment_name",
"environment_uuid",
"github_app_uuid",
"git_repository",
"git_branch",
@@ -1845,8 +1841,6 @@
"required": [
"project_uuid",
"server_uuid",
"environment_name",
"environment_uuid",
"private_key_uuid",
"git_repository",
"git_branch",
@@ -2503,8 +2497,6 @@
"required": [
"project_uuid",
"server_uuid",
"environment_name",
"environment_uuid",
"dockerfile"
],
"properties": {
@@ -3046,8 +3038,6 @@
"required": [
"project_uuid",
"server_uuid",
"environment_name",
"environment_uuid",
"docker_registry_image_name"
],
"properties": {
@@ -7032,9 +7022,11 @@
"content": {
"application\/json": {
"schema": {
"type": "string"
},
"example": "Content is very complex. Will be implemented later."
"type": "array",
"items": {
"$ref": "#\/components\/schemas\/Database"
}
}
}
}
},
@@ -7278,13 +7270,12 @@
],
"responses": {
"200": {
"description": "Get all databases",
"description": "Get database by UUID",
"content": {
"application\/json": {
"schema": {
"type": "string"
},
"example": "Content is very complex. Will be implemented later."
"$ref": "#\/components\/schemas\/Database"
}
}
}
},
@@ -7900,9 +7891,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -8055,9 +8044,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -8194,9 +8181,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -8329,9 +8314,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -8468,9 +8451,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -8607,9 +8588,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -8758,9 +8737,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -8909,9 +8886,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -9048,9 +9023,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"server_uuid": {
@@ -14651,13 +14624,49 @@
"operationId": "list-resources",
"responses": {
"200": {
"description": "Get all resources",
"description": "Get all applications, services and standalone databases. The other fields of each resource are the fields of the Application, Service or Database schema.",
"content": {
"application\/json": {
"schema": {
"type": "string"
},
"example": "Content is very complex. Will be implemented later."
"type": "array",
"items": {
"properties": {
"uuid": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": [
"string",
"null"
]
},
"type": {
"description": "application, service, or standalone-<engine> for databases (for example standalone-postgresql).",
"type": "string"
},
"status": {
"description": "For example running:healthy or exited:unhealthy.",
"type": "string"
},
"environment_id": {
"type": "integer"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object",
"additionalProperties": true
}
}
}
}
},
@@ -20227,9 +20236,7 @@
"schema": {
"required": [
"server_uuid",
"project_uuid",
"environment_name",
"environment_uuid"
"project_uuid"
],
"properties": {
"type": {
@@ -24184,6 +24191,400 @@
},
"type": "object"
},
"Database": {
"description": "Standalone database. The fields that start with an engine name (for example postgres_ or redis_) are only returned for that engine. Passwords, connection URLs and init_scripts are only returned for tokens with the read:sensitive or root ability.",
"properties": {
"uuid": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": [
"string",
"null"
]
},
"database_type": {
"type": "string",
"enum": [
"standalone-postgresql",
"standalone-redis",
"standalone-mongodb",
"standalone-mysql",
"standalone-mariadb",
"standalone-keydb",
"standalone-dragonfly",
"standalone-clickhouse",
"standalone-sqlite"
]
},
"image": {
"type": "string"
},
"status": {
"description": "Container status and health, for example running:healthy or exited:unhealthy.",
"type": "string"
},
"server_status": {
"description": "True when the server of the database is reachable and usable.",
"type": "boolean"
},
"environment_id": {
"type": "integer"
},
"environment": {
"$ref": "#\/components\/schemas\/Environment",
"description": "Only returned by GET \/databases\/{uuid}. Includes the project."
},
"destination_id": {
"type": "integer"
},
"destination_type": {
"type": "string"
},
"destination": {
"description": "Docker network destination of the database, with its server.",
"properties": {
"uuid": {
"type": "string"
},
"name": {
"type": "string"
},
"network": {
"type": "string"
},
"server_id": {
"type": "integer"
},
"server": {
"$ref": "#\/components\/schemas\/Server"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
},
"type": "object"
},
"is_public": {
"description": "Not returned for SQLite.",
"type": "boolean"
},
"public_port": {
"description": "Not returned for SQLite.",
"type": [
"integer",
"null"
]
},
"public_port_timeout": {
"description": "Proxy timeout of the public port in seconds. Not returned for SQLite.",
"type": [
"integer",
"null"
]
},
"ports_mappings": {
"description": "Comma-separated host:container port mappings. Not returned for SQLite.",
"type": [
"string",
"null"
]
},
"enable_ssl": {
"description": "Not returned for ClickHouse and SQLite.",
"type": "boolean"
},
"ssl_mode": {
"description": "PostgreSQL, MongoDB and MySQL only.",
"type": [
"string",
"null"
]
},
"internal_db_url": {
"description": "Connection URL inside the Docker network. Contains the password. Not returned for SQLite.",
"type": [
"string",
"null"
]
},
"external_db_url": {
"description": "Public connection URL, when the database is public. Contains the password. Not returned for SQLite.",
"type": [
"string",
"null"
]
},
"init_scripts": {
"description": "PostgreSQL only.",
"type": [
"array",
"null"
],
"items": {
"properties": {
"index": {
"type": "integer"
},
"filename": {
"type": "string"
},
"content": {
"type": "string"
}
},
"type": "object"
}
},
"custom_docker_run_options": {
"type": [
"string",
"null"
]
},
"is_log_drain_enabled": {
"type": "boolean"
},
"is_include_timestamps": {
"description": "Not returned for MariaDB.",
"type": "boolean"
},
"include_coolify_default_variables": {
"type": "boolean"
},
"redact_all_env_values_in_logs": {
"type": "boolean"
},
"health_check_enabled": {
"type": "boolean"
},
"health_check_interval": {
"type": "integer"
},
"health_check_timeout": {
"type": "integer"
},
"health_check_retries": {
"type": "integer"
},
"health_check_start_period": {
"type": "integer"
},
"limits_memory": {
"type": "string"
},
"limits_memory_swap": {
"type": "string"
},
"limits_memory_swappiness": {
"type": "integer"
},
"limits_memory_reservation": {
"type": "string"
},
"limits_cpus": {
"type": "string"
},
"limits_cpuset": {
"type": [
"string",
"null"
]
},
"limits_cpu_shares": {
"type": "integer"
},
"config_hash": {
"description": "Internal hash that Coolify uses to find configuration changes that need a restart. It changes only when the database starts, so do not use it to compare configurations.",
"type": [
"string",
"null"
]
},
"restart_count": {
"type": "integer"
},
"last_restart_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"last_restart_type": {
"type": [
"string",
"null"
]
},
"last_online_at": {
"type": [
"string",
"null"
]
},
"started_at": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"backup_configs": {
"description": "Only returned by GET \/databases. The scheduled backups of the database, as returned by GET \/databases\/{uuid}\/backups.",
"type": "array",
"items": {
"type": "object"
}
},
"postgres_user": {
"type": "string"
},
"postgres_password": {
"type": "string",
"format": "password"
},
"postgres_db": {
"type": "string"
},
"postgres_initdb_args": {
"type": [
"string",
"null"
]
},
"postgres_host_auth_method": {
"type": [
"string",
"null"
]
},
"postgres_conf": {
"type": [
"string",
"null"
]
},
"redis_password": {
"type": "string",
"format": "password"
},
"redis_conf": {
"type": [
"string",
"null"
]
},
"mongo_initdb_root_username": {
"type": "string"
},
"mongo_initdb_root_password": {
"type": "string",
"format": "password"
},
"mongo_initdb_database": {
"type": "string"
},
"mongo_conf": {
"type": [
"string",
"null"
]
},
"mysql_user": {
"type": "string"
},
"mysql_password": {
"type": "string",
"format": "password"
},
"mysql_root_password": {
"type": "string",
"format": "password"
},
"mysql_database": {
"type": "string"
},
"mysql_conf": {
"type": [
"string",
"null"
]
},
"mariadb_user": {
"type": "string"
},
"mariadb_password": {
"type": "string",
"format": "password"
},
"mariadb_root_password": {
"type": "string",
"format": "password"
},
"mariadb_database": {
"type": "string"
},
"mariadb_conf": {
"type": [
"string",
"null"
]
},
"keydb_password": {
"type": "string",
"format": "password"
},
"keydb_conf": {
"type": [
"string",
"null"
]
},
"dragonfly_password": {
"type": "string",
"format": "password"
},
"clickhouse_admin_user": {
"type": "string"
},
"clickhouse_admin_password": {
"type": "string",
"format": "password"
},
"clickhouse_db": {
"type": "string"
},
"sqlite_databases": {
"description": "Comma-separated SQLite database file names.",
"type": "string"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"updated_at": {
"type": "string",
"format": "date-time"
},
"deleted_at": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"type": "object"
},
"ApplicationPreview": {
"properties": {
"uuid": {
+287 -38
View File
@@ -331,8 +331,6 @@ paths:
required:
- project_uuid
- server_uuid
- environment_name
- environment_uuid
- git_repository
- git_branch
- build_pack
@@ -765,8 +763,6 @@ paths:
required:
- project_uuid
- server_uuid
- environment_name
- environment_uuid
- github_app_uuid
- git_repository
- git_branch
@@ -1203,8 +1199,6 @@ paths:
required:
- project_uuid
- server_uuid
- environment_name
- environment_uuid
- private_key_uuid
- git_repository
- git_branch
@@ -1641,8 +1635,6 @@ paths:
required:
- project_uuid
- server_uuid
- environment_name
- environment_uuid
- dockerfile
properties:
project_uuid:
@@ -2012,8 +2004,6 @@ paths:
required:
- project_uuid
- server_uuid
- environment_name
- environment_uuid
- docker_registry_image_name
properties:
project_uuid:
@@ -4538,8 +4528,9 @@ paths:
content:
application/json:
schema:
type: string
example: 'Content is very complex. Will be implemented later.'
type: array
items:
$ref: '#/components/schemas/Database'
'401':
$ref: '#/components/responses/401'
'400':
@@ -4703,12 +4694,11 @@ paths:
type: string
responses:
'200':
description: 'Get all databases'
description: 'Get database by UUID'
content:
application/json:
schema:
type: string
example: 'Content is very complex. Will be implemented later.'
$ref: '#/components/schemas/Database'
'401':
$ref: '#/components/responses/401'
'400':
@@ -5147,8 +5137,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -5258,8 +5246,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -5357,8 +5343,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -5453,8 +5437,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -5552,8 +5534,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -5651,8 +5631,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -5759,8 +5737,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -5867,8 +5843,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -5966,8 +5940,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
server_uuid:
type: string
@@ -9404,12 +9376,15 @@ paths:
operationId: list-resources
responses:
'200':
description: 'Get all resources'
description: 'Get all applications, services and standalone databases. The other fields of each resource are the fields of the Application, Service or Database schema.'
content:
application/json:
schema:
type: string
example: 'Content is very complex. Will be implemented later.'
type: array
items:
properties: { uuid: { type: string }, name: { type: string }, description: { type: [string, 'null'] }, type: { description: 'application, service, or standalone-<engine> for databases (for example standalone-postgresql).', type: string }, status: { description: 'For example running:healthy or exited:unhealthy.', type: string }, environment_id: { type: integer }, created_at: { type: string, format: date-time }, updated_at: { type: string, format: date-time } }
type: object
additionalProperties: true
'401':
$ref: '#/components/responses/401'
'400':
@@ -12809,8 +12784,6 @@ paths:
required:
- server_uuid
- project_uuid
- environment_name
- environment_uuid
properties:
type:
description: 'The one-click service type (e.g. "actualbudget", "calibre-web", "gitea-with-mysql" ...)'
@@ -15358,6 +15331,282 @@ components:
- 'null'
format: date-time
type: object
Database:
description: 'Standalone database. The fields that start with an engine name (for example postgres_ or redis_) are only returned for that engine. Passwords, connection URLs and init_scripts are only returned for tokens with the read:sensitive or root ability.'
properties:
uuid:
type: string
name:
type: string
description:
type:
- string
- 'null'
database_type:
type: string
enum:
- standalone-postgresql
- standalone-redis
- standalone-mongodb
- standalone-mysql
- standalone-mariadb
- standalone-keydb
- standalone-dragonfly
- standalone-clickhouse
- standalone-sqlite
image:
type: string
status:
description: 'Container status and health, for example running:healthy or exited:unhealthy.'
type: string
server_status:
description: 'True when the server of the database is reachable and usable.'
type: boolean
environment_id:
type: integer
environment:
$ref: '#/components/schemas/Environment'
description: 'Only returned by GET /databases/{uuid}. Includes the project.'
destination_id:
type: integer
destination_type:
type: string
destination:
description: 'Docker network destination of the database, with its server.'
properties:
uuid:
type: string
name:
type: string
network:
type: string
server_id:
type: integer
server:
$ref: '#/components/schemas/Server'
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
type: object
is_public:
description: 'Not returned for SQLite.'
type: boolean
public_port:
description: 'Not returned for SQLite.'
type:
- integer
- 'null'
public_port_timeout:
description: 'Proxy timeout of the public port in seconds. Not returned for SQLite.'
type:
- integer
- 'null'
ports_mappings:
description: 'Comma-separated host:container port mappings. Not returned for SQLite.'
type:
- string
- 'null'
enable_ssl:
description: 'Not returned for ClickHouse and SQLite.'
type: boolean
ssl_mode:
description: 'PostgreSQL, MongoDB and MySQL only.'
type:
- string
- 'null'
internal_db_url:
description: 'Connection URL inside the Docker network. Contains the password. Not returned for SQLite.'
type:
- string
- 'null'
external_db_url:
description: 'Public connection URL, when the database is public. Contains the password. Not returned for SQLite.'
type:
- string
- 'null'
init_scripts:
description: 'PostgreSQL only.'
type:
- array
- 'null'
items:
properties:
index:
type: integer
filename:
type: string
content:
type: string
type: object
custom_docker_run_options:
type:
- string
- 'null'
is_log_drain_enabled:
type: boolean
is_include_timestamps:
description: 'Not returned for MariaDB.'
type: boolean
include_coolify_default_variables:
type: boolean
redact_all_env_values_in_logs:
type: boolean
health_check_enabled:
type: boolean
health_check_interval:
type: integer
health_check_timeout:
type: integer
health_check_retries:
type: integer
health_check_start_period:
type: integer
limits_memory:
type: string
limits_memory_swap:
type: string
limits_memory_swappiness:
type: integer
limits_memory_reservation:
type: string
limits_cpus:
type: string
limits_cpuset:
type:
- string
- 'null'
limits_cpu_shares:
type: integer
config_hash:
description: 'Internal hash that Coolify uses to find configuration changes that need a restart. It changes only when the database starts, so do not use it to compare configurations.'
type:
- string
- 'null'
restart_count:
type: integer
last_restart_at:
type:
- string
- 'null'
format: date-time
last_restart_type:
type:
- string
- 'null'
last_online_at:
type:
- string
- 'null'
started_at:
type:
- string
- 'null'
format: date-time
backup_configs:
description: 'Only returned by GET /databases. The scheduled backups of the database, as returned by GET /databases/{uuid}/backups.'
type: array
items:
type: object
postgres_user:
type: string
postgres_password:
type: string
format: password
postgres_db:
type: string
postgres_initdb_args:
type:
- string
- 'null'
postgres_host_auth_method:
type:
- string
- 'null'
postgres_conf:
type:
- string
- 'null'
redis_password:
type: string
format: password
redis_conf:
type:
- string
- 'null'
mongo_initdb_root_username:
type: string
mongo_initdb_root_password:
type: string
format: password
mongo_initdb_database:
type: string
mongo_conf:
type:
- string
- 'null'
mysql_user:
type: string
mysql_password:
type: string
format: password
mysql_root_password:
type: string
format: password
mysql_database:
type: string
mysql_conf:
type:
- string
- 'null'
mariadb_user:
type: string
mariadb_password:
type: string
format: password
mariadb_root_password:
type: string
format: password
mariadb_database:
type: string
mariadb_conf:
type:
- string
- 'null'
keydb_password:
type: string
format: password
keydb_conf:
type:
- string
- 'null'
dragonfly_password:
type: string
format: password
clickhouse_admin_user:
type: string
clickhouse_admin_password:
type: string
format: password
clickhouse_db:
type: string
sqlite_databases:
description: 'Comma-separated SQLite database file names.'
type: string
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
deleted_at:
type:
- string
- 'null'
format: date-time
type: object
ApplicationPreview:
properties:
uuid:
@@ -0,0 +1,88 @@
<?php
use App\Models\Environment;
use App\Models\InstanceSettings;
use App\Models\Project;
use App\Models\Server;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
uses(RefreshDatabase::class);
function openApiDocument(): array
{
return json_decode((string) file_get_contents(base_path('openapi.json')), true, flags: JSON_THROW_ON_ERROR);
}
beforeEach(function () {
InstanceSettings::query()->delete();
$settings = new InstanceSettings;
$settings->id = 0;
$settings->save();
$team = Team::factory()->create();
$user = User::factory()->create();
$team->members()->attach($user->id, ['role' => 'owner']);
session(['currentTeam' => $team]);
$server = Server::factory()->create(['team_id' => $team->id]);
$project = Project::factory()->create(['team_id' => $team->id]);
$environment = Environment::factory()->create(['project_id' => $project->id]);
$destination = $server->standaloneDockers()->firstOrFail();
$passwords = [
'postgresql' => ['postgres_password' => 'secret'],
'redis' => ['redis_password' => 'secret'],
'mongodb' => ['mongo_initdb_root_password' => 'secret'],
'mysql' => ['mysql_password' => 'secret', 'mysql_root_password' => 'secret'],
'mariadb' => ['mariadb_password' => 'secret', 'mariadb_root_password' => 'secret'],
'keydb' => ['keydb_password' => 'secret'],
'dragonfly' => ['dragonfly_password' => 'secret'],
'clickhouse' => ['clickhouse_admin_password' => 'secret'],
'sqlite' => [],
];
$this->databases = collect(STANDALONE_DATABASE_MODELS)->map(fn (string $class, string $engine) => $class::create($passwords[$engine] + [
'name' => "db-{$engine}",
'environment_id' => $environment->id,
'destination_id' => $destination->id,
'destination_type' => $destination->getMorphClass(),
]));
$token = $user->createToken('schema-test', ['root']);
DB::table('personal_access_tokens')->where('id', $token->accessToken->id)->update(['team_id' => $team->id]);
$this->headers = ['Authorization' => 'Bearer '.$token->plainTextToken];
});
test('database responses only contain fields documented in the Database schema', function () {
$documented = array_keys(openApiDocument()['components']['schemas']['Database']['properties']);
foreach ($this->databases as $engine => $database) {
$single = $this->withHeaders($this->headers)->getJson("/api/v1/databases/{$database->uuid}")->assertOk()->json();
expect(array_diff(array_keys($single), $documented))->toBe([], "GET /databases/{uuid} for {$engine}")
->and($single['database_type'])->toBe($database->type());
}
$list = $this->withHeaders($this->headers)->getJson('/api/v1/databases')->assertOk()->json();
expect($list)->toHaveCount($this->databases->count());
foreach ($list as $item) {
expect(array_diff(array_keys($item), $documented))->toBe([], "GET /databases for {$item['database_type']}");
}
});
test('resource list items contain the documented common fields', function () {
$schema = openApiDocument()['paths']['/resources']['get']['responses']['200']['content']['application/json']['schema'];
$commonFields = array_keys($schema['items']['properties']);
$resources = $this->withHeaders($this->headers)->getJson('/api/v1/resources')->assertOk()->json();
expect($schema['type'])->toBe('array')
->and($resources)->toHaveCount($this->databases->count());
foreach ($resources as $resource) {
expect($resource)->toHaveKeys($commonFields);
}
});
+24
View File
@@ -0,0 +1,24 @@
<?php
test('create endpoints do not require both environment_name and environment_uuid', function () {
$document = json_decode((string) file_get_contents(__DIR__.'/../../openapi.json'), true, flags: JSON_THROW_ON_ERROR);
$createPaths = collect($document['paths'])
->filter(fn (array $operations, string $path) => isset($operations['post']['requestBody']['content']['application/json']['schema']['properties']['environment_name']));
expect($createPaths)->toHaveCount(15);
foreach ($createPaths as $path => $operations) {
$required = $operations['post']['requestBody']['content']['application/json']['schema']['required'] ?? [];
expect(array_intersect(['environment_name', 'environment_uuid'], $required))->toBe([], $path);
}
});
test('database and resource list endpoints document typed responses', function () {
$document = json_decode((string) file_get_contents(__DIR__.'/../../openapi.json'), true, flags: JSON_THROW_ON_ERROR);
$response = fn (string $path) => $document['paths'][$path]['get']['responses']['200']['content']['application/json']['schema'];
expect($response('/databases'))->toBe(['type' => 'array', 'items' => ['$ref' => '#/components/schemas/Database']])
->and($response('/databases/{uuid}'))->toBe(['$ref' => '#/components/schemas/Database'])
->and($response('/resources')['type'])->toBe('array');
});