const express = require("express"); const fs = require("fs"); const path = require("path"); const { exec } = require("child_process"); const crypto = require("crypto"); const PORT = process.env.PORT || 3000; const DATA_FILE = path.join(__dirname, "commands.json"); const CEC_TIMEOUT_MS = 15000; const app = express(); app.use(express.json()); app.use(express.static(path.join(__dirname, "public"))); function loadCommands() { try { return JSON.parse(fs.readFileSync(DATA_FILE, "utf8")); } catch { return []; } } function saveCommands(commands) { fs.writeFileSync(DATA_FILE, JSON.stringify(commands, null, 2) + "\n"); } // Alle gespeicherten Befehle app.get("/api/commands", (req, res) => { res.json(loadCommands()); }); // Neuen Befehl anlegen app.post("/api/commands", (req, res) => { const name = (req.body.name || "").trim(); const command = (req.body.command || "").trim(); if (!name || !command) { return res.status(400).json({ error: "Name und Command dürfen nicht leer sein." }); } const commands = loadCommands(); const entry = { id: crypto.randomUUID(), name, command }; commands.push(entry); saveCommands(commands); res.status(201).json(entry); }); // Befehl löschen app.delete("/api/commands/:id", (req, res) => { const commands = loadCommands(); const remaining = commands.filter((c) => c.id !== req.params.id); if (remaining.length === commands.length) { return res.status(404).json({ error: "Befehl nicht gefunden." }); } saveCommands(remaining); res.json({ ok: true }); }); // Befehl als Shell-Kommando ausführen app.post("/api/commands/:id/run", (req, res) => { const entry = loadCommands().find((c) => c.id === req.params.id); if (!entry) { return res.status(404).json({ error: "Befehl nicht gefunden." }); } exec(entry.command, { timeout: CEC_TIMEOUT_MS }, (error, stdout, stderr) => { const output = (stdout + stderr).trim(); if (error?.killed) { return res.status(504).json({ error: `Befehl wurde nach ${CEC_TIMEOUT_MS / 1000}s abgebrochen (${error.signal}).`, command: entry.command, output, }); } if (error && typeof error.code !== "number") { return res.status(500).json({ error: `Befehl konnte nicht ausgeführt werden: ${error.message}`, command: entry.command, }); } const exitCode = error ? error.code : 0; res.json({ ok: exitCode === 0, exitCode, command: entry.command, output }); }); }); app.listen(PORT, () => { console.log(`CEC-Tool läuft auf http://localhost:${PORT}`); });