import { ResponseError } from "@homarr/common/server"; import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http"; import type { IntegrationTestingInput } from "../base/integration"; import { Integration } from "../base/integration"; import type { TestingResult } from "../base/test-connection/test-connection-service"; import type { CoolifyApplication, CoolifyApplicationWithContext, CoolifyEnvironment, CoolifyEnvironmentVariable, CoolifyHealthcheck, CoolifyInstanceInfo, CoolifyProject, CoolifyProjectWithEnvironments, CoolifyResource, CoolifyServer, CoolifyService, CoolifyServiceWithContext, } from "./coolify-types"; import { coolifyApplicationLogSchema, coolifyApplicationSchema, coolifyEnvironmentSchema, coolifyEnvironmentVariableSchema, coolifyHealthcheckSchema, coolifyProjectSchema, coolifyProjectWithEnvironmentsSchema, coolifyResourceSchema, coolifyServerSchema, coolifyServiceSchema, } from "./coolify-types"; export class CoolifyIntegration extends Integration { protected async testingAsync(input: IntegrationTestingInput): Promise { const healthUrl = this.url("/api/health"); const healthResponse = await input.fetchAsync(healthUrl, {}); if (!healthResponse.ok) { throw new ResponseError(healthResponse); } // Test API key authentication const versionUrl = this.url("/api/v1/version"); const versionResponse = await input.fetchAsync(versionUrl, { headers: this.getAuthHeaders(), }); if (!versionResponse.ok) { throw new ResponseError(versionResponse); } return { success: true }; } /** * Get Coolify version information * https://coolify.io/docs/api-reference/api/operations/version */ public async getVersionAsync(): Promise { const url = this.url("/api/v1/version"); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } const version = await response.text(); return version.trim(); } /** * Get Coolify healthcheck status * https://coolify.io/docs/api-reference/api/operations/healthcheck */ public async getHealthcheckAsync(): Promise { const url = this.url("/api/v1/healthcheck"); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyHealthcheckSchema.parse(await response.json()); } /** * List all applications * https://coolify.io/docs/api-reference/api/operations/list-applications */ public async listApplicationsAsync(): Promise { const url = this.url("/api/v1/applications"); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyApplicationSchema.array().parse(await response.json()); } /** * Get application by UUID * https://coolify.io/docs/api-reference/api/operations/get-application-by-uuid */ public async getApplicationByUuidAsync(uuid: string): Promise { const url = this.url(`/api/v1/applications/${uuid}`); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyApplicationSchema.parse(await response.json()); } /** * Get application logs by UUID * https://coolify.io/docs/api-reference/api/operations/get-application-logs-by-uuid */ public async getApplicationLogsAsync(uuid: string): Promise { const url = this.url(`/api/v1/applications/${uuid}/logs`); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } const result = coolifyApplicationLogSchema.parse(await response.json()); return result.logs; } /** * List environment variables by application UUID * https://coolify.io/docs/api-reference/api/operations/list-envs-by-application-uuid */ public async listEnvsByApplicationUuidAsync(uuid: string): Promise { const url = this.url(`/api/v1/applications/${uuid}/envs`); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyEnvironmentVariableSchema.array().parse(await response.json()); } /** * List projects * https://coolify.io/docs/api-reference/api/operations/list-projects */ public async listProjectsAsync(): Promise { const url = this.url("/api/v1/projects"); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyProjectSchema.array().parse(await response.json()); } /** * Get project by UUID (includes environments) * https://coolify.io/docs/api-reference/api/operations/get-project-by-uuid */ public async getProjectByUuidAsync(uuid: string): Promise { const url = this.url(`/api/v1/projects/${uuid}`); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyProjectWithEnvironmentsSchema.parse(await response.json()); } /** * Get environments * https://coolify.io/docs/api-reference/api/operations/get-environments */ public async getEnvironmentsAsync(): Promise { const url = this.url("/api/v1/environments"); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyEnvironmentSchema.array().parse(await response.json()); } /** * Get environment by name or UUID * https://coolify.io/docs/api-reference/api/operations/get-environment-by-name-or-uuid */ public async getEnvironmentByNameOrUuidAsync(nameOrUuid: string): Promise { const url = this.url(`/api/v1/environments/${nameOrUuid}`); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyEnvironmentSchema.parse(await response.json()); } /** * List resources * https://coolify.io/docs/api-reference/api/operations/list-resources */ public async listResourcesAsync(): Promise { const url = this.url("/api/v1/resources"); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyResourceSchema.array().parse(await response.json()); } /** * List servers * https://coolify.io/docs/api-reference/api/operations/list-servers */ public async listServersAsync(): Promise { const url = this.url("/api/v1/servers"); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyServerSchema.array().parse(await response.json()); } /** * List services * https://coolify.io/docs/api-reference/api/operations/list-services */ public async listServicesAsync(): Promise { const url = this.url("/api/v1/services"); const response = await fetchWithTrustedCertificatesAsync(url, { headers: this.getAuthHeaders(), }); if (!response.ok) { throw new ResponseError(response); } return coolifyServiceSchema.array().parse(await response.json()); } public async getInstanceInfoAsync(): Promise { const [version, applications, services, projectsWithEnvs, servers] = await Promise.all([ this.getVersionAsync(), this.listApplicationsAsync(), this.listServicesAsync(), this.listProjectsWithEnvironmentsAsync(), this.listServersAsync(), ]); const envToProjectMap = new Map< number, { projectName: string; projectUuid: string; environmentName: string; environmentUuid?: string } >(); for (const project of projectsWithEnvs) { for (const env of project.environments ?? []) { if (env.id != null) { envToProjectMap.set(env.id, { projectName: project.name, projectUuid: project.uuid, environmentName: env.name, environmentUuid: env.uuid ?? undefined, }); } } } const applicationsWithContext: CoolifyApplicationWithContext[] = applications.map((app) => { const context = app.environment_id ? envToProjectMap.get(app.environment_id) : undefined; return { ...app, projectName: context?.projectName, projectUuid: context?.projectUuid, environmentName: context?.environmentName, environmentUuid: context?.environmentUuid, }; }); const servicesWithContext: CoolifyServiceWithContext[] = services.map((service) => { const context = service.environment_id ? envToProjectMap.get(service.environment_id) : undefined; const fqdn = service.fqdn ?? service.applications?.find((app) => app.fqdn)?.fqdn ?? undefined; return { ...service, fqdn, projectName: context?.projectName, projectUuid: context?.projectUuid, environmentName: context?.environmentName, environmentUuid: context?.environmentUuid, }; }); return { version, applications: applicationsWithContext, services: servicesWithContext, servers, }; } /** * List all projects with their environments */ private async listProjectsWithEnvironmentsAsync(): Promise { const projects = await this.listProjectsAsync(); const projectsWithEnvs = await Promise.all(projects.map((project) => this.getProjectByUuidAsync(project.uuid))); return projectsWithEnvs; } private getAuthHeaders(): Record { return { Authorization: `Bearer ${this.getSecretValue("apiKey")}`, }; } }