starface-outlook-sync-addin/installer/local-server.js

147 lines
4.1 KiB
JavaScript

/**
* 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));
});