Skip to content

Commit 2c1db8a

Browse files
committed
feat(admin): add ssh-exec utility script
1 parent 133005b commit 2c1db8a

File tree

1 file changed

+60
-0
lines changed

1 file changed

+60
-0
lines changed

apps/admin/scripts/ssh-exec.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env bun
2+
/**
3+
* Execute a command on the Sandchest node server via SSH.
4+
* Uses the admin app's DB + encrypted SSH key.
5+
*
6+
* Usage: bun run apps/admin/scripts/ssh-exec.ts "command here"
7+
* or: bun run apps/admin/scripts/ssh-exec.ts --script path/to/script.sh
8+
*/
9+
import 'dotenv/config'
10+
import { eq } from 'drizzle-orm'
11+
import { getDb } from '../src/lib/db'
12+
import { adminServers } from '@sandchest/db/schema'
13+
import { decrypt } from '../src/lib/encryption'
14+
import { createSshConnection, execCommand } from '../src/lib/ssh'
15+
16+
const args = process.argv.slice(2)
17+
if (args.length === 0) {
18+
console.error('Usage: bun run apps/admin/scripts/ssh-exec.ts "command"')
19+
console.error(' bun run apps/admin/scripts/ssh-exec.ts --script path/to/script.sh')
20+
process.exit(1)
21+
}
22+
23+
let command: string
24+
if (args[0] === '--script') {
25+
const scriptPath = args[1]
26+
if (!scriptPath) {
27+
console.error('Missing script path')
28+
process.exit(1)
29+
}
30+
const script = await Bun.file(scriptPath).text()
31+
const scriptB64 = Buffer.from(script).toString('base64')
32+
command = `echo '${scriptB64}' | base64 -d > /tmp/_ssh_exec.sh && chmod +x /tmp/_ssh_exec.sh && /tmp/_ssh_exec.sh; EXIT=$?; rm -f /tmp/_ssh_exec.sh; exit $EXIT`
33+
} else {
34+
command = args.join(' ')
35+
}
36+
37+
const db = getDb()
38+
const servers = await db.select().from(adminServers).limit(1)
39+
if (servers.length === 0) {
40+
console.error('No servers found in admin DB')
41+
process.exit(1)
42+
}
43+
const server = servers[0]
44+
45+
const sshKey = decrypt(server.sshKeyEncrypted, server.sshKeyIv, server.sshKeyTag)
46+
const conn = await createSshConnection({
47+
host: server.ip,
48+
port: server.sshPort,
49+
username: server.sshUser,
50+
privateKey: sshKey,
51+
})
52+
53+
const timeoutMs = parseInt(process.env.SSH_TIMEOUT ?? '120000', 10)
54+
const result = await execCommand(conn, command, timeoutMs)
55+
56+
process.stdout.write(result.stdout)
57+
if (result.stderr) process.stderr.write(result.stderr)
58+
59+
conn.end()
60+
process.exit(result.code)

0 commit comments

Comments
 (0)