Files
starface-outlook-sync-addin/installer/local-server.js
T
duffyduck 69b3c417f1 Fix cert export: use PFX instead of PEM for .NET Framework compat
ExportPkcs8PrivateKey() is only available in .NET Core 3.0+
but Windows PowerShell 5.1 uses .NET Framework 4.x.
Switch to PFX export which works on all Windows versions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 10:02:27 +02:00

149 lines
4.2 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 pfxFile = path.join(CERT_DIR, "localhost.pfx");
const pfxPasswordFile = path.join(CERT_DIR, "pfx-password.txt");
if (!fs.existsSync(pfxFile) || !fs.existsSync(pfxPasswordFile)) {
console.error("Zertifikate nicht gefunden in:", CERT_DIR);
console.error("Bitte zuerst setup.ps1 ausfuehren.");
process.exit(1);
}
const pfxPassword = fs.readFileSync(pfxPasswordFile, "utf-8").trim();
const serverOptions = {
pfx: fs.readFileSync(pfxFile),
passphrase: pfxPassword,
};
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));
});