● Zion Boggan
repos/Zion Pm/lib/workers/ssh.ts
zionboggan.com ↗
31 lines · typescript
History for this file →
1
import { Client } from 'ssh2';
2
import fs from 'fs';
3
 
4
const KEY_PATH = process.env.ZPM_SSH_KEY || '/root/.ssh/id_ed25519';
5
 
6
export interface SSHResult { code: number; stdout: string; stderr: string; }
7
 
8
export function runSSH(host: string, user: string, command: string, timeoutMs = 120_000): Promise<SSHResult> {
9
  return new Promise((resolve, reject) => {
10
    const conn = new Client();
11
    let stdout = '';
12
    let stderr = '';
13
    let settled = false;
14
    const done = (fn: () => void) => { if (!settled) { settled = true; clearTimeout(timer); try { conn.end(); } catch {} fn(); } };
15
    const timer = setTimeout(() => done(() => reject(new Error(`ssh timeout after ${timeoutMs}ms`))), timeoutMs);
16
    conn
17
      .on('ready', () => {
18
        conn.exec(command, (err, stream) => {
19
          if (err) return done(() => reject(err));
20
          stream
21
            .on('close', (code: number) => done(() => resolve({ code: code ?? 0, stdout, stderr })))
22
            .on('data', (d: Buffer) => { stdout += d.toString(); })
23
            .stderr.on('data', (d: Buffer) => { stderr += d.toString(); });
24
        });
25
      })
26
      .on('error', (e) => done(() => reject(e)))
27
      .connect({ host, port: 22, username: user, privateKey: fs.readFileSync(KEY_PATH), readyTimeout: Math.min(timeoutMs, 20_000) });
28
  });
29
}
30
 
31
export const b64 = (s: string) => Buffer.from(s, 'utf8').toString('base64');