Add full REST API for all deployment operations (projects, servers, docker)

Port all IPC handlers to HTTP endpoints so the UI and LLM use the same
API. Adds routes/projects.js (scan, compare, init), routes/servers.js
(CRUD, containers, logs), routes/docker.js (build, deploy, pull, vscode-diff).
Enhanced ssh.js with full SSHService class (SFTP upload/download).
Updated renderer api.js to use fetch instead of window.api IPC.
Added concurrently for npm run dev (API + Vite + Electron).
OpenAPI spec now covers all 24 endpoints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 11:17:40 -06:00
parent 93d40455d9
commit feec35ffce
9 changed files with 1329 additions and 153 deletions

View File

@@ -1,45 +1,4 @@
const api = window.api;
export const serverApi = {
getAll: () => api.getServers(),
save: (server) => api.saveServer(server),
delete: (id) => api.deleteServer(id),
};
export const projectApi = {
scanLocal: () => api.scanLocalProjects(),
scanServer: (serverId) => api.scanServer(serverId),
getRunningContainers: (serverId) => api.getRunningContainers(serverId),
compare: (data) => api.compareProject(data),
init: (projectPath) => api.initProject(projectPath),
};
export const deployApi = {
buildTar: (projectPath) => api.buildTar(projectPath),
deploy: (data) => api.deployProject(data),
};
export const syncApi = {
pullFile: (data) => api.pullFile(data),
pullFiles: (data) => api.pullFiles(data),
};
export const logsApi = {
getContainerLogs: (data) => api.getContainerLogs(data),
};
export const configApi = {
get: () => api.getConfig(),
save: (config) => api.saveConfig(config),
};
export const toolsApi = {
openVSCodeDiff: (data) => api.openVSCodeDiff(data),
};
// ─── Coolify API (HTTP fetch to Express server) ────────────────────
const COOLIFY_BASE = '/api/coolify';
// ─── Shared fetch helper ────────────────────────────────────────────
async function fetchJson(url, options) {
const res = await fetch(url, {
@@ -53,6 +12,77 @@ async function fetchJson(url, options) {
return res.json();
}
// ─── Deployment Servers (CRUD + scanning) ───────────────────────────
export const serverApi = {
getAll: () => fetchJson('/api/servers'),
save: (server) => fetchJson('/api/servers', { method: 'POST', body: JSON.stringify(server) }),
delete: (id) => fetchJson(`/api/servers/${id}`, { method: 'DELETE' }),
scan: (serverId) => fetchJson(`/api/servers/${serverId}/scan`),
getRunningContainers: (serverId) => fetchJson(`/api/servers/${serverId}/containers`),
getLogs: ({ serverId, containerName, remotePath, lines }) => {
const params = new URLSearchParams();
if (containerName) params.set('containerName', containerName);
if (remotePath) params.set('remotePath', remotePath);
if (lines) params.set('lines', String(lines));
return fetchJson(`/api/servers/${serverId}/logs?${params}`);
},
};
// ─── Projects (scan, compare, init) ────────────────────────────────
export const projectApi = {
scanLocal: () => fetchJson('/api/projects'),
scanServer: (serverId) => fetchJson(`/api/servers/${serverId}/scan`).then(r => r.deployed || []),
getRunningContainers: (serverId) => fetchJson(`/api/servers/${serverId}/containers`).then(r => r.containers || []),
compare: (data) => fetchJson('/api/projects/compare', { method: 'POST', body: JSON.stringify(data) }),
init: (projectPath) => fetchJson('/api/projects/init', { method: 'POST', body: JSON.stringify({ projectPath }) }),
};
// ─── Docker (build, deploy, pull) ──────────────────────────────────
export const deployApi = {
buildTar: (projectPath) => fetchJson('/api/docker/build', { method: 'POST', body: JSON.stringify({ projectPath }) }),
deploy: (data) => fetchJson('/api/docker/deploy', { method: 'POST', body: JSON.stringify(data) }),
};
export const syncApi = {
pullFile: (data) => fetchJson('/api/docker/pull', {
method: 'POST',
body: JSON.stringify({ serverId: data.serverId, files: [{ name: data.remotePath, remotePath: data.remotePath, localPath: data.localPath, type: data.isDirectory ? 'directory' : 'file' }] }),
}).then(r => r),
pullFiles: (data) => fetchJson('/api/docker/pull', { method: 'POST', body: JSON.stringify(data) }),
};
// ─── Config ─────────────────────────────────────────────────────────
export const configApi = {
get: () => fetchJson('/api/servers').then(servers => {
// Reconstruct config shape from servers list
return { servers };
}),
save: (config) => {
// Save each server individually
return Promise.all((config.servers || []).map(s => serverApi.save(s)));
},
};
export const logsApi = {
getContainerLogs: (data) => serverApi.getLogs(data),
};
// ─── Coolify API (HTTP fetch to Express server) ────────────────────
const COOLIFY_BASE = '/api/coolify';
// ─── Tools (VS Code diff — opens locally, not via API) ─────────────
export const toolsApi = {
openVSCodeDiff: (data) => fetchJson('/api/docker/vscode-diff', { method: 'POST', body: JSON.stringify(data) }),
};
// ─── Coolify API (HTTP fetch to Express server) ────────────────────
export const coolifyApi = {
listApps: () => fetchJson(`${COOLIFY_BASE}/apps`),
findApp: (name) => fetchJson(`${COOLIFY_BASE}/apps/find/${encodeURIComponent(name)}`),