myaidev-method
Version:
Comprehensive development framework with SPARC methodology for AI-assisted software development, multi-platform publishing (WordPress, PayloadCMS, Astro, Docusaurus, Mintlify), and Coolify deployment
381 lines (327 loc) • 9.46 kB
JavaScript
/**
* Coolify API Utilities
* Reusable functions for Coolify deployment and resource management
* Optimized for Claude Code 2.0 agent integration
*/
import fetch from 'node-fetch';
import { readFileSync } from 'fs';
import { parse } from 'dotenv';
export class CoolifyUtils {
constructor(config = {}) {
// Load config from .env if not provided
if (!config.url || !config.apiKey) {
const envConfig = this.loadEnvConfig();
config = { ...envConfig, ...config };
}
this.url = config.url?.replace(/\/$/, '');
this.apiKey = config.apiKey;
this.headers = {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
};
}
loadEnvConfig() {
try {
const envPath = process.env.ENV_PATH || '.env';
const envContent = readFileSync(envPath, 'utf8');
const parsed = parse(envContent);
return {
url: parsed.COOLIFY_URL,
apiKey: parsed.COOLIFY_API_KEY
};
} catch (error) {
throw new Error(`Failed to load Coolify configuration: ${error.message}`);
}
}
/**
* Make authenticated Coolify API request
*/
async request(endpoint, options = {}) {
const url = `${this.url}${endpoint}`;
try {
const response = await fetch(url, {
...options,
headers: { ...this.headers, ...options.headers }
});
const data = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${data.message || response.statusText}`);
}
return data;
} catch (error) {
throw new Error(`Coolify API request failed: ${error.message}`);
}
}
/**
* Server Management
*/
async listServers() {
return await this.request('/api/v1/servers');
}
async getServer(uuid) {
return await this.request(`/api/v1/servers/${uuid}`);
}
async getServerResources(uuid) {
return await this.request(`/api/v1/servers/${uuid}/resources`);
}
/**
* Project Management
*/
async listProjects() {
return await this.request('/api/v1/projects');
}
async getProject(uuid) {
return await this.request(`/api/v1/projects/${uuid}`);
}
async createProject(data) {
return await this.request('/api/v1/projects', {
method: 'POST',
body: JSON.stringify(data)
});
}
/**
* Application Management
*/
async listApplications() {
return await this.request('/api/v1/applications');
}
async getApplication(uuid) {
return await this.request(`/api/v1/applications/${uuid}`);
}
async createApplication(data) {
// Coolify API requires /applications/public endpoint
return await this.request('/api/v1/applications/public', {
method: 'POST',
body: JSON.stringify(data)
});
}
async updateApplication(uuid, data) {
return await this.request(`/api/v1/applications/${uuid}`, {
method: 'PATCH',
body: JSON.stringify(data)
});
}
async deleteApplication(uuid) {
return await this.request(`/api/v1/applications/${uuid}`, {
method: 'DELETE'
});
}
/**
* Deployment Operations
*/
async deployApplication(uuid, options = {}) {
const endpoint = options.tag
? `/api/v1/deploy?tag=${options.tag}`
: `/api/v1/deploy?uuid=${uuid}`;
return await this.request(endpoint, {
method: 'POST',
body: JSON.stringify({
force: options.force || false,
instant_deploy: options.instant || false,
...options.deployConfig
})
});
}
async getDeploymentLogs(uuid) {
return await this.request(`/api/v1/applications/${uuid}/logs`);
}
async restartApplication(uuid) {
return await this.request(`/api/v1/applications/${uuid}/restart`, {
method: 'POST'
});
}
async stopApplication(uuid) {
return await this.request(`/api/v1/applications/${uuid}/stop`, {
method: 'POST'
});
}
/**
* Environment Variables
*/
async getEnvironmentVariables(uuid) {
return await this.request(`/api/v1/applications/${uuid}/envs`);
}
async updateEnvironmentVariables(uuid, envVars) {
return await this.request(`/api/v1/applications/${uuid}/envs`, {
method: 'POST',
body: JSON.stringify(envVars)
});
}
/**
* Database Management
*/
async listDatabases() {
return await this.request('/api/v1/databases');
}
async getDatabase(uuid) {
return await this.request(`/api/v1/databases/${uuid}`);
}
async createDatabase(data) {
return await this.request('/api/v1/databases', {
method: 'POST',
body: JSON.stringify(data)
});
}
/**
* Service Management
*/
async listServices() {
return await this.request('/api/v1/services');
}
async getService(uuid) {
return await this.request(`/api/v1/services/${uuid}`);
}
/**
* Team Management
*/
async listTeams() {
return await this.request('/api/v1/teams');
}
async getCurrentTeam() {
return await this.request('/api/v1/teams/current');
}
/**
* Health & Status
*/
async healthCheck() {
try {
const response = await fetch(`${this.url}/api/health`);
return response.ok;
} catch (error) {
return false;
}
}
async getSystemStatus() {
const [servers, projects, applications] = await Promise.all([
this.listServers().catch(() => []),
this.listProjects().catch(() => []),
this.listApplications().catch(() => [])
]);
return {
healthy: await this.healthCheck(),
servers: {
total: servers.length,
reachable: servers.filter(s => s.is_reachable).length,
usable: servers.filter(s => s.is_usable).length
},
projects: {
total: projects.length
},
applications: {
total: applications.length
}
};
}
/**
* Deployment Helpers
*/
async findApplicationByName(name) {
const apps = await this.listApplications();
return apps.find(app => app.name === name);
}
async findServerByName(name) {
const servers = await this.listServers();
return servers.find(server => server.name === name);
}
async findProjectByName(name) {
const projects = await this.listProjects();
return projects.find(project => project.name === name);
}
async waitForDeployment(uuid, options = {}) {
const maxAttempts = options.maxAttempts || 60;
const interval = options.interval || 5000;
for (let i = 0; i < maxAttempts; i++) {
const app = await this.getApplication(uuid);
if (app.status === 'running') {
return { success: true, status: 'running', attempts: i + 1 };
}
if (app.status === 'error' || app.status === 'failed') {
return { success: false, status: app.status, attempts: i + 1 };
}
await new Promise(resolve => setTimeout(resolve, interval));
}
return { success: false, status: 'timeout', attempts: maxAttempts };
}
/**
* Deployment Configuration Builders
*/
buildApplicationConfig(options) {
return {
name: options.name,
description: options.description || '',
project_uuid: options.projectUuid,
server_uuid: options.serverUuid,
destination_uuid: options.destinationUuid,
source: {
type: options.sourceType || 'git',
repository: options.repository,
branch: options.branch || 'main',
commit: options.commit || null
},
build_pack: options.buildPack || 'nixpacks',
ports_exposes: options.ports || '3000',
domains: options.domains || [],
environment_variables: options.envVars || {},
settings: {
instant_deploy: options.instantDeploy || false,
auto_deploy: options.autoDeploy || true,
...options.settings
}
};
}
buildDatabaseConfig(options) {
return {
name: options.name,
description: options.description || '',
project_uuid: options.projectUuid,
server_uuid: options.serverUuid,
destination_uuid: options.destinationUuid,
type: options.type, // postgresql, mysql, mongodb, redis, etc.
version: options.version || 'latest',
public_port: options.publicPort || null,
environment_variables: options.envVars || {}
};
}
/**
* Formatted Output Helpers
*/
formatServerList(servers) {
return servers.map(s => ({
name: s.name,
uuid: s.uuid,
ip: s.ip,
status: s.is_reachable ? '✓ Reachable' : '✗ Unreachable',
usable: s.is_usable ? '✓ Usable' : '✗ Not Usable',
description: s.description || 'No description'
}));
}
formatApplicationList(applications) {
return applications.map(a => ({
name: a.name,
uuid: a.uuid,
status: a.status,
url: a.fqdn || 'Not configured',
project: a.project?.name || 'Unknown'
}));
}
generateDeploymentReport(deployment, application) {
return {
timestamp: new Date().toISOString(),
application: {
name: application.name,
uuid: application.uuid,
url: application.fqdn
},
deployment: {
status: deployment.status,
commit: deployment.commit,
branch: deployment.branch,
started_at: deployment.started_at,
finished_at: deployment.finished_at
},
success: deployment.status === 'success'
};
}
}
export default CoolifyUtils;