Files
idea.llm.gitea.repo.docker.…/api/lib/ssh.js
Clint Masden 93d40455d9 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>
2026-02-27 11:02:17 -06:00

46 lines
1.3 KiB
JavaScript

import { Client } from 'ssh2';
/**
* Execute a command on the remote server via SSH.
* Uses the ssh2 library (already a project dependency) instead of
* the plink/sshpass fallback chain from gitea.repo.management.
*/
export function sshExec(config, command) {
const host = config.coolify?.sshHost;
const user = config.coolify?.sshUser;
const password = config.coolify?.sshPassword;
if (!host || !user) {
return Promise.reject(new Error('SSH not configured — set coolify.sshHost and coolify.sshUser in config.json'));
}
return new Promise((resolve, reject) => {
const conn = new Client();
conn.on('ready', () => {
conn.exec(command, (err, stream) => {
if (err) { conn.end(); return reject(err); }
let stdout = '';
let stderr = '';
stream.on('close', (code) => {
conn.end();
if (code !== 0 && stderr) {
reject(new Error(stderr.trim()));
} else {
resolve(stdout.trim());
}
});
stream.on('data', (data) => { stdout += data.toString(); });
stream.stderr.on('data', (data) => { stderr += data.toString(); });
});
});
conn.on('error', (err) => reject(err));
conn.connect({ host, port: 22, username: user, password });
});
}