const express = require('express'); const { Client } = require('ssh2'); const { exec } = require('child_process'); const fs = require('fs'); const path = require('path');
const app = express(); const PORT = 3000;
const hosts = [ { host: 'minibookx.local', username: 'arch', privateKeyPath: '/home/arch/.ssh/id_rsa' }, { host: 'raspi4b.local', username: 'ubuntu', privateKeyPath: '/home/arch/.ssh/id_rsa' }, { host: 'zotac.local', username: 'arch', privateKeyPath: '/home/arch/.ssh/id_rsa' } ];
function fetchFastfetch(config) { return new Promise((resolve) => { const conn = new Client(); conn.on('ready', () => { conn.exec('fastfetch -c none --structure OS:Kernel:Uptime:CPU:Memory:Disk:GPU --format json', (err, stream) => { if (err) return resolve({ host: config.host, error: err.message }); let data = ''; stream.on('data', (chunk) => { data += chunk; }); stream.on('close', () => { try { resolve({ host: config.host, data: JSON.parse(data) }); } catch (e) { resolve({ host: config.host, error: 'JSONパースエラー' }); } conn.end(); }); }); }).on('error', (err) => { resolve({ host: config.host, error: err.message }); }).connect({ host: config.host, username: config.username, privateKey: fs.readFileSync(config.privateKeyPath) }); }); }
function fetchLocalFastfetch() { return new Promise((resolve) => { exec('fastfetch -c none --structure OS:Kernel:Uptime:CPU:Memory:Disk:GPU --format json', (error, stdout) => { if (error) return resolve({ host: 'WSL Server (Local)', error: error.message }); try { resolve({ host: 'WSL Server (Local)', data: JSON.parse(stdout) }); } catch (e) { resolve({ host: 'WSL Server (Local)', error: 'JSONパースエラー' }); } }); }); }
function runPowerCommand(config, action) { return new Promise((resolve) => { const conn = new Client(); const cmd = action === 'reboot' ? 'sudo reboot' : 'sudo shutdown -h now';
conn.on('ready', () => { conn.exec(cmd, (err, stream) => { if (err) return resolve({ success: false, error: err.message }); stream.on('close', () => { resolve({ success: true }); conn.end(); }); }); }).on('error', (err) => { if (err.code === 'ECONNRESET' || err.message.includes('read ECONNRESET')) { resolve({ success: true }); } else { resolve({ success: false, error: err.message }); } }).connect({ host: config.host, username: config.username, privateKey: fs.readFileSync(config.privateKeyPath) }); }); }
app.use(express.static(path.join(__dirname, 'public')));
app.get('/api/status', async (req, res) => { const remotePromises = hosts.map(fetchFastfetch); const [localResult, ...remoteResults] = await Promise.all([ fetchLocalFastfetch(), ...remotePromises ]); res.json([localResult, ...remoteResults]); });
app.post('/api/power', express.json(), async (req, res) => { const { host, action } = req.body; const config = hosts.find(h => h.host === host); if (!config) return res.status(404).json({ error: '対象PCが見つかりません' }); if (action !== 'reboot' && action !== 'shutdown') return res.status(400).json({ error: '無効な操作' });
const result = await runPowerCommand(config, action); res.json(result); });
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
|