/** * Minimaler lokaler HTTPS-Server für das Starface Outlook Sync Add-in. * Liefert nur statische Dateien aus dem webroot-Verzeichnis aus. * Wird als Windows-Dienst oder Scheduled Task ausgeführt. */ const https = require("https"); const http = require("http"); const fs = require("fs"); const path = require("path"); const url = require("url"); // Konfiguration aus config.json laden const configPath = path.join(__dirname, "config.json"); if (!fs.existsSync(configPath)) { console.error("config.json nicht gefunden:", configPath); process.exit(1); } const config = JSON.parse(fs.readFileSync(configPath, "utf-8")); const PORT = config.port || 444; const WEBROOT = path.join(__dirname, "webroot"); const CERT_DIR = path.join(__dirname, "certs"); // MIME-Types const MIME_TYPES = { ".html": "text/html; charset=utf-8", ".js": "application/javascript; charset=utf-8", ".css": "text/css; charset=utf-8", ".json": "application/json; charset=utf-8", ".png": "image/png", ".jpg": "image/jpeg", ".svg": "image/svg+xml", ".ico": "image/x-icon", ".xml": "application/xml; charset=utf-8", }; function getMimeType(filePath) { const ext = path.extname(filePath).toLowerCase(); return MIME_TYPES[ext] || "application/octet-stream"; } function handleRequest(req, res) { // CORS-Header für Office Add-in res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS"); res.setHeader("Access-Control-Allow-Headers", "Content-Type"); if (req.method === "OPTIONS") { res.writeHead(204); res.end(); return; } if (req.method !== "GET") { res.writeHead(405, { "Content-Type": "text/plain" }); res.end("Method Not Allowed"); return; } // URL parsen und Pfad bereinigen const parsedUrl = url.parse(req.url); let filePath = decodeURIComponent(parsedUrl.pathname || "/"); // Directory index if (filePath === "/" || filePath === "") { filePath = "/taskpane.html"; } // Pfad-Traversal verhindern const safePath = path.normalize(filePath).replace(/^(\.\.[\/\\])+/, ""); const fullPath = path.join(WEBROOT, safePath); // Sicherstellen, dass der Pfad innerhalb des webroot liegt if (!fullPath.startsWith(WEBROOT)) { res.writeHead(403, { "Content-Type": "text/plain" }); res.end("Forbidden"); return; } // Datei ausliefern fs.stat(fullPath, (err, stats) => { if (err || !stats.isFile()) { res.writeHead(404, { "Content-Type": "text/plain" }); res.end("Not Found: " + safePath); return; } const mimeType = getMimeType(fullPath); res.writeHead(200, { "Content-Type": mimeType, "Cache-Control": "no-cache", }); const stream = fs.createReadStream(fullPath); stream.pipe(res); stream.on("error", () => { res.writeHead(500, { "Content-Type": "text/plain" }); res.end("Internal Server Error"); }); }); } // Zertifikate laden und HTTPS-Server starten const certFile = path.join(CERT_DIR, "localhost.crt"); const keyFile = path.join(CERT_DIR, "localhost.key"); if (!fs.existsSync(certFile) || !fs.existsSync(keyFile)) { console.error("Zertifikate nicht gefunden in:", CERT_DIR); console.error("Bitte zuerst setup.ps1 ausführen."); process.exit(1); } const serverOptions = { cert: fs.readFileSync(certFile), key: fs.readFileSync(keyFile), }; const server = https.createServer(serverOptions, handleRequest); server.listen(PORT, "127.0.0.1", () => { console.log(`Starface Outlook Sync - Lokaler Server`); console.log(`Läuft auf: https://localhost:${PORT}`); console.log(`Webroot: ${WEBROOT}`); console.log(`PID: ${process.pid}`); }); server.on("error", (err) => { if (err.code === "EADDRINUSE") { console.error(`Port ${PORT} ist bereits belegt.`); console.error("Bitte anderen Port in config.json wählen."); } else { console.error("Server-Fehler:", err.message); } process.exit(1); }); // Graceful Shutdown process.on("SIGINT", () => { console.log("Server wird beendet..."); server.close(() => process.exit(0)); }); process.on("SIGTERM", () => { console.log("Server wird beendet..."); server.close(() => process.exit(0)); });