diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 50f71f46dc..91161f4000 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -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.'], diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index efa2a4a5d1..106216f7c9 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -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'], diff --git a/app/Http/Controllers/Api/OpenApi.php b/app/Http/Controllers/Api/OpenApi.php index 77741ea537..1279442b28 100644 --- a/app/Http/Controllers/Api/OpenApi.php +++ b/app/Http/Controllers/Api/OpenApi.php @@ -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( diff --git a/app/Http/Controllers/Api/ResourcesController.php b/app/Http/Controllers/Api/ResourcesController.php index 53d542d44b..5b7923e19f 100644 --- a/app/Http/Controllers/Api/ResourcesController.php +++ b/app/Http/Controllers/Api/ResourcesController.php @@ -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- 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( diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index 7eecdd30b6..8cb4235d6a 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -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.'], diff --git a/app/Models/StandaloneKeydb.php b/app/Models/StandaloneKeydb.php index fa59e5a51e..c5e03e6df0 100644 --- a/app/Models/StandaloneKeydb.php +++ b/app/Models/StandaloneKeydb.php @@ -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). diff --git a/openapi.json b/openapi.json index 3d24978d9c..999cdb2e2a 100644 --- a/openapi.json +++ b/openapi.json @@ -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- 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": { diff --git a/openapi.yaml b/openapi.yaml index 4f02ba373d..c3872be12a 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -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- 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: diff --git a/tests/Feature/Api/DatabaseApiResponseSchemaTest.php b/tests/Feature/Api/DatabaseApiResponseSchemaTest.php new file mode 100644 index 0000000000..3f70a603b2 --- /dev/null +++ b/tests/Feature/Api/DatabaseApiResponseSchemaTest.php @@ -0,0 +1,88 @@ +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); + } +}); diff --git a/tests/Unit/ResourceCreateOpenApiTest.php b/tests/Unit/ResourceCreateOpenApiTest.php new file mode 100644 index 0000000000..70d549bc22 --- /dev/null +++ b/tests/Unit/ResourceCreateOpenApiTest.php @@ -0,0 +1,24 @@ +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'); +});