Add Coolify REST API server with Scalar docs and UI integration

Express API server on :3100 exposing all Coolify operations:
- CRUD for apps, env vars, servers
- Full upsert pipeline (create/update + env + route + deploy)
- Drift detection, Traefik route management via SSH
- Scalar API docs at /reference, OpenAPI 3.1 spec

UI: New Coolify page with app cards, deploy/delete actions,
env var expansion. Sidebar nav + React Query hooks + fetch client.

Both UI and LLM/CLI use the same HTTP endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 11:02:17 -06:00
parent 2fe49b6725
commit 93d40455d9
16 changed files with 1426 additions and 5 deletions

329
api/openapi.json Normal file
View File

@@ -0,0 +1,329 @@
{
"openapi": "3.1.0",
"info": {
"title": "Coolify Deployment API",
"description": "REST API for managing Coolify applications, Traefik routing, and automated deployments. Used by both the desktop UI and LLM agents.",
"version": "1.0.0"
},
"servers": [
{ "url": "http://localhost:3100", "description": "Local dev" }
],
"paths": {
"/api/coolify/apps": {
"get": {
"operationId": "listApps",
"summary": "List all Coolify apps",
"description": "Returns all applications from Coolify, enriched with HOST_PORT env vars and env list.",
"tags": ["Apps"],
"responses": {
"200": {
"description": "Array of Coolify applications",
"content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/App" } } } }
}
}
},
"post": {
"operationId": "createApp",
"summary": "Create a new Coolify application",
"tags": ["Apps"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/CreateAppRequest" }
}
}
},
"responses": {
"200": { "description": "Created app object" }
}
}
},
"/api/coolify/apps/find/{name}": {
"get": {
"operationId": "findApp",
"summary": "Find app by name",
"tags": ["Apps"],
"parameters": [
{ "name": "name", "in": "path", "required": true, "schema": { "type": "string" } }
],
"responses": {
"200": { "description": "App object or null" }
}
}
},
"/api/coolify/apps/{uuid}": {
"patch": {
"operationId": "updateApp",
"summary": "Update an existing app",
"tags": ["Apps"],
"parameters": [
{ "name": "uuid", "in": "path", "required": true, "schema": { "type": "string" } }
],
"requestBody": {
"required": true,
"content": { "application/json": { "schema": { "type": "object" } } }
},
"responses": {
"200": { "description": "Updated app" }
}
},
"delete": {
"operationId": "deleteApp",
"summary": "Delete an app from Coolify",
"tags": ["Apps"],
"parameters": [
{ "name": "uuid", "in": "path", "required": true, "schema": { "type": "string" } }
],
"responses": {
"200": { "description": "Deletion confirmation" }
}
}
},
"/api/coolify/apps/{uuid}/envs": {
"get": {
"operationId": "listEnvs",
"summary": "List env vars for an app",
"tags": ["Environment"],
"parameters": [
{ "name": "uuid", "in": "path", "required": true, "schema": { "type": "string" } }
],
"responses": {
"200": { "description": "Array of env vars" }
}
},
"post": {
"operationId": "setEnv",
"summary": "Set an environment variable",
"tags": ["Environment"],
"parameters": [
{ "name": "uuid", "in": "path", "required": true, "schema": { "type": "string" } }
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"key": { "type": "string" },
"value": { "type": "string" }
},
"required": ["key", "value"]
}
}
}
},
"responses": {
"200": { "description": "Env var created/updated" }
}
}
},
"/api/coolify/apps/{uuid}/deploy": {
"post": {
"operationId": "deployApp",
"summary": "Trigger deployment for an app",
"tags": ["Deploy"],
"parameters": [
{ "name": "uuid", "in": "path", "required": true, "schema": { "type": "string" } }
],
"responses": {
"200": { "description": "Deployment triggered" }
}
}
},
"/api/coolify/servers": {
"get": {
"operationId": "listServers",
"summary": "List all Coolify servers",
"tags": ["Servers"],
"responses": {
"200": { "description": "Array of servers" }
}
}
},
"/api/coolify/next-port": {
"get": {
"operationId": "getNextPort",
"summary": "Get next available HOST_PORT",
"description": "Scans Coolify env vars and Traefik config to find the highest used port, returns max+1.",
"tags": ["Infrastructure"],
"responses": {
"200": {
"description": "Next port info",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"nextPort": { "type": "integer" },
"usedPorts": { "type": "array", "items": { "type": "integer" } }
}
}
}
}
}
}
}
},
"/api/coolify/routes": {
"post": {
"operationId": "addRoute",
"summary": "Add a Traefik route via SSH",
"tags": ["Infrastructure"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"routeName": { "type": "string", "description": "Traefik service/router name (alphanumeric)" },
"domain": { "type": "string", "description": "Domain name (e.g. timer.dotrepo.com)" },
"port": { "type": "integer", "description": "HOST_PORT on target server" }
},
"required": ["routeName", "domain", "port"]
}
}
}
},
"responses": {
"200": { "description": "Route added or already exists" }
}
}
},
"/api/coolify/drift": {
"post": {
"operationId": "checkDrift",
"summary": "Check drift between coolify.json and live Coolify state",
"tags": ["Deploy"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"projectPath": { "type": "string", "description": "Absolute path to project directory" },
"appName": { "type": "string", "description": "Optional app name when coolify.json has multiple entries" }
},
"required": ["projectPath"]
}
}
}
},
"responses": {
"200": { "description": "Drift check result with diffs" }
}
}
},
"/api/coolify/upsert": {
"post": {
"operationId": "upsertApp",
"summary": "Full deploy pipeline: read config → create/update → env → route → deploy",
"description": "Reads coolify.json from the project, creates or updates the Coolify app, sets HOST_PORT, configures Traefik routing, writes changelog, and triggers deployment. This is the primary endpoint for deploying apps.",
"tags": ["Deploy"],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"projectPath": { "type": "string", "description": "Absolute path to project directory containing coolify.json" },
"appName": { "type": "string", "description": "Optional app name when coolify.json has multiple entries" }
},
"required": ["projectPath"]
}
}
}
},
"responses": {
"200": {
"description": "Upsert result with steps and changelog",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/UpsertResult" }
}
}
}
}
}
},
"/api/coolify/config": {
"get": {
"operationId": "readCoolifyConfig",
"summary": "Read coolify.json from a project directory",
"tags": ["Config"],
"parameters": [
{ "name": "path", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Absolute path to project directory" }
],
"responses": {
"200": { "description": "Parsed coolify.json contents" }
}
}
},
"/api/health": {
"get": {
"operationId": "healthCheck",
"summary": "Health check",
"tags": ["System"],
"responses": {
"200": { "description": "Server status" }
}
}
}
},
"components": {
"schemas": {
"App": {
"type": "object",
"properties": {
"uuid": { "type": "string" },
"name": { "type": "string" },
"build_pack": { "type": "string", "enum": ["dockercompose", "nixpacks", "dockerfile"] },
"git_repository": { "type": "string" },
"git_branch": { "type": "string" },
"status": { "type": "string" },
"fqdn": { "type": "string" },
"_host_port": { "type": "string", "nullable": true },
"_envs": { "type": "array" }
}
},
"CreateAppRequest": {
"type": "object",
"properties": {
"name": { "type": "string", "description": "App name in Coolify" },
"buildpack": { "type": "string", "enum": ["dockercompose", "nixpacks"], "default": "dockercompose" },
"gitRepo": { "type": "string", "description": "Git SSH URL" },
"gitBranch": { "type": "string", "default": "master" },
"isStatic": { "type": "boolean", "default": false },
"publishDir": { "type": "string", "default": "dist" },
"portsExposes": { "type": "string", "default": "3000" },
"baseDirectory": { "type": "string", "default": "/" },
"dockerComposeLocation": { "type": "string", "default": "/docker-compose.yml" },
"serverUuid": { "type": "string", "description": "Override server UUID" }
},
"required": ["name", "gitRepo"]
},
"UpsertResult": {
"type": "object",
"properties": {
"success": { "type": "boolean" },
"uuid": { "type": "string" },
"domain": { "type": "string" },
"changelogEntry": { "type": "object" },
"steps": { "type": "array", "items": { "$ref": "#/components/schemas/Step" } }
}
},
"Step": {
"type": "object",
"properties": {
"step": { "type": "string" },
"status": { "type": "string", "enum": ["running", "done", "error", "skipped", "warn"] },
"detail": { "type": "string" }
}
}
}
}
}