'use strict'; const { app, BrowserWindow, ipcMain } = require('electron'); const path = require('path'); const os = require('os'); const { execSync } = require('child_process'); // ── ウィンドウ作成 ────────────────────────────────────────────── function createWindow() { const win = new BrowserWindow({ width: 1920, height: 1080, fullscreen: process.env.KIOSK === '1', // KIOSK=1 で全画面起動 backgroundColor: '#0C1221', autoHideMenuBar: true, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, // セキュリティ必須 nodeIntegration: false, // セキュリティ必須 }, }); // Design B(バイナリ雨オーロラ背景)をメインとして起動 // Design A に切り替えたい場合: DESIGN=a npm start const design = process.env.DESIGN === 'a' ? 'station.html' : 'station-b.html'; win.loadFile(path.join(__dirname, 'renderer', design)); } app.whenReady().then(createWindow); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); }); // ── CPU サンプリング(100ms 2回測定) ────────────────────────── function getCpuSample() { return os.cpus().map(c => { const total = Object.values(c.times).reduce((a, b) => a + b, 0); return { total, idle: c.times.idle }; }); } // ── システムメトリクス取得 IPC ───────────────────────────────── ipcMain.handle('get-metrics', async () => { const s1 = getCpuSample(); await new Promise(r => setTimeout(r, 100)); const s2 = getCpuSample(); const cpuPct = s1.reduce((sum, c1, i) => { const c2 = s2[i]; const dIdle = c2.idle - c1.idle; const dTotal = c2.total - c1.total; return sum + (dTotal > 0 ? (1 - dIdle / dTotal) * 100 : 0); }, 0) / s1.length; const mem = os.freemem(); const total = os.totalmem(); let disk = null; try { const dfOut = execSync('df -B1 / 2>/dev/null', { timeout: 2000 }).toString(); const parts = dfOut.trim().split('\n')[1].split(/\s+/); disk = { total_gb: Math.round(parseInt(parts[1]) / 1e9 * 10) / 10, used_gb: Math.round(parseInt(parts[2]) / 1e9 * 10) / 10, use_pct: Math.round(parseInt(parts[2]) / parseInt(parts[1]) * 100), }; } catch (_) {} return { ok: true, hostname: os.hostname(), uptime_s: Math.floor(os.uptime()), cpu_pct: Math.round(cpuPct), cpu_count: os.cpus().length, load_avg: os.loadavg().map(l => Math.round(l * 100) / 100), mem_used_mb: Math.round((total - mem) / 1024 / 1024), mem_total_mb: Math.round(total / 1024 / 1024), disk, active_sessions: 0, node_version: process.version, platform: os.platform(), timestamp: new Date().toISOString(), }; }); // ── サービス死活チェック IPC ─────────────────────────────────── ipcMain.handle('check-service', async (_event, url) => { const t0 = Date.now(); try { const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), 5000); const r = await fetch(url, { method: 'HEAD', signal: ctrl.signal }); clearTimeout(timer); return { ok: true, status: r.status, latency_ms: Date.now() - t0 }; } catch (e) { return { ok: false, error: e.message, latency_ms: Date.now() - t0 }; } });