feat: expand discovery with systemd services, LXC/VMs, SSH terminal, and filebrowser

- Add systemd service discovery to backend
- Add Proxmox LXC/VM detection
- Add hostType field to config for better host categorization
- Fix SSH trust between hosts (ubuntu/grizzley -> truenas/proxmox)
- Add SSH terminal support via xterm.js
- Add filebrowser for browsing host filesystems
- Update frontend types and components for new node types
This commit is contained in:
2026-02-20 17:18:33 -08:00
parent a4cff9894c
commit 3dc5d236a2
23 changed files with 2680 additions and 70 deletions

60
server/routes/terminal.ts Normal file
View File

@@ -0,0 +1,60 @@
import { Router } from 'express';
import { execSync } from 'child_process';
import { homedir } from 'os';
import { getHostConfigs } from '../config';
const router = Router();
interface TerminalRequest {
host: string;
command: string;
}
router.post('/terminal/exec', async (req, res) => {
try {
const { host: hostName, command }: TerminalRequest = req.body;
if (!hostName || !command) {
return res.status(400).json({ error: 'Missing host or command' });
}
const hosts = getHostConfigs();
const hostConfig = hosts.find(h => h.name === hostName);
if (!hostConfig) {
return res.status(404).json({ error: `Host not found: ${hostName}` });
}
const keyPath = (hostConfig.sshKeyPath || `${homedir()}/.ssh/id_ed25519`).replace(/^~/, homedir());
const keyArg = `-i ${keyPath}`;
const portArg = hostConfig.sshPort && hostConfig.sshPort !== 22 ? `-p ${hostConfig.sshPort}` : '';
const fullCommand = `ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no ${keyArg} ${portArg} ${hostConfig.sshUser}@${hostConfig.ip} ${command} 2>&1`;
const output = execSync(fullCommand, { encoding: 'utf-8', timeout: 30000 });
res.json({ output, error: null });
} catch (error: any) {
res.status(500).json({
output: '',
error: error.message || 'Command execution failed'
});
}
});
router.get('/terminal/hosts', async (_req, res) => {
try {
const hosts = getHostConfigs();
res.json({
hosts: hosts.map(h => ({
name: h.name,
ip: h.ip,
user: h.sshUser
}))
});
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
export default router;