Files
vans-diy-bastelbedarf/server/index.js
T
qcigano ab3a5da892
Build & Deploy / deploy (push) Waiting to run
Sicherheits-, Stabilitaets- und Domainkorrekturen nach vollstaendiger Pruefung
- Absturzsicherheit: Express 4 faengt Fehler aus async-Handlern nicht ab, eine
  einzige fehlerhafte Anfrage konnte den ganzen Shop beenden - besonders heikel
  bei capture-order, wo das direkt nach der PayPal-Abbuchung passieren wuerde.
  Neu: lib/async-wrap.js um alle Handler, zentraler Fehler-Handler in index.js,
  .catch(next) in der Zugangsschranke, unhandledRejection/uncaughtException-Netz.
- Fehlerhaftes JSON lieferte bisher Express' HTML-Fehlerseite mit komplettem
  Stacktrace und Serverpfaden (weil NODE_ENV nicht gesetzt war). Jetzt saubere
  JSON-Antwort, NODE_ENV=production in .env.example ergaenzt.
- Sicherheits-Header und x-powered-by wie bei der DogFather-Seite.
- Falsche Domain .de statt .com korrigiert: astro.config.mjs (betraf alle
  Canonical-URLs und die komplette Sitemap), robots.txt, Layout.astro sowie den
  Verwaltungs-Link in jeder Bestellbenachrichtigung (war ein toter Link).
- Fehlende Uebersetzung nav.cart in EN/CH/FR ergaenzt: das Warenkorb-Symbol hatte
  in drei von vier Sprachen keinen Namen fuer Screenreader.
2026-08-05 22:33:49 +02:00

108 lines
4.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =====================================================================
index.js — Haupteinstiegspunkt. Baut den kompletten Express-Server auf: liefert die fertig
gebaute Astro-Seite (../dist/) aus UND die API-Routen, genau wie Cloudflare Pages beides in
einem Deployment vereint hat. Die Zugangscode-Schranke (middleware/gate.js) läuft vor JEDER
Anfrage, exakt wie functions/_middleware.js das vorher bei Cloudflare tat.
Umgebungsvariablen kommen aus einer .env-Datei im server/-Ordner (siehe .env.example), geladen
über das "dotenv"-Paket direkt beim Start.
===================================================================== */
import "dotenv/config";
import express from "express";
import cookieParser from "cookie-parser";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { existsSync } from "node:fs";
import { initDb } from "./db.js";
import { gateMiddleware } from "./middleware/gate.js";
import { ordersRouter } from "./routes/orders.js";
import { bestsellerRouter } from "./routes/bestseller.js";
import { paypalRouter } from "./routes/paypal.js";
import { authRouter } from "./routes/auth.js";
import { accountRouter } from "./routes/account.js";
import { verwaltungRouter } from "./routes/verwaltung.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const DIST_DIR = join(__dirname, "..", "dist");
const PORT = Number(process.env.PORT || 4000);
initDb();
const app = express();
// Läuft hinter Caddy (Reverse Proxy) — nötig, damit req.protocol/req.get("host") und
// "secure"-Cookies korrekt funktionieren, statt anzunehmen, die Verbindung sei unverschlüsselt.
app.set("trust proxy", 1);
app.disable("x-powered-by"); // verrät sonst unnötig den eingesetzten Technik-Stack
/* Sicherheits-Header, ergänzt 05.08.2026 (bei Cloudflare Pages kamen die teils von der Plattform;
auf dem eigenen Server muss der Server sie selbst setzen). `payment=(self)` bleibt erlaubt,
damit die PayPal-Zahlung im eigenen Fenster weiter funktioniert. */
app.use((req, res, next) => {
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Frame-Options", "SAMEORIGIN");
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
res.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=(), payment=(self)");
res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
next();
});
app.use(cookieParser());
app.use(express.json({ limit: "2mb" }));
// Zugangscode-Schranke vor allem anderen — exakt wie functions/_middleware.js bei Cloudflare.
app.use(gateMiddleware);
app.use(ordersRouter);
app.use(bestsellerRouter);
app.use(paypalRouter);
app.use(authRouter);
app.use(accountRouter);
app.use(verwaltungRouter);
// Statische Auslieferung der fertig gebauten Astro-Seite (nach "npm run build" in ../).
if (existsSync(DIST_DIR)) {
app.use(express.static(DIST_DIR, { extensions: ["html"] }));
} else {
console.warn(`⚠️ ${DIST_DIR} existiert nicht — "npm run build" im Astro-Projekt (../) noch nicht ausgeführt.`);
}
// 404 für alles, was weder Datei noch bekannte Route ist.
app.use((req, res) => {
res.status(404).sendFile(join(DIST_DIR, "404.html"), (err) => {
if (err) res.status(404).send("Nicht gefunden.");
});
});
/* ---------- Fehler-Handler (muss NACH allen Routen stehen), ergänzt 05.08.2026 ----------
Express 4 fängt Fehler aus async-Handlern nicht selbst ab. Ohne diesen Handler landete kaputtes
JSON in Express' Standard-Fehlerseite — inklusive komplettem Stacktrace mit Serverpfaden, solange
NODE_ENV nicht auf "production" steht. Jetzt kommt in jedem Fall eine saubere JSON-Antwort. */
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
if (err?.type === "entity.parse.failed") {
return res.status(400).json({ ok: false, error: "Ungültige Anfrage." });
}
if (err?.type === "entity.too.large") {
return res.status(413).json({ ok: false, error: "Die Anfrage ist zu groß." });
}
console.error(`[fehler] ${req.method} ${req.originalUrl}:`, err?.stack || err);
if (res.headersSent) return;
if (req.path.startsWith("/api/")) {
return res.status(500).json({ ok: false, error: "Interner Fehler." });
}
return res.status(500).send("Es ist ein Fehler aufgetreten. Bitte später erneut versuchen.");
});
/* Letztes Netz: Ein unbehandelter Fehler darf den Shop nicht offline nehmen — besonders wichtig
direkt nach einer PayPal-Abbuchung, wo ein Absturz die Kundin ohne Rückmeldung zurücklassen
würde, obwohl das Geld bereits eingezogen ist. */
process.on("unhandledRejection", (grund) => console.error("[unhandledRejection]", grund));
process.on("uncaughtException", (fehler) => console.error("[uncaughtException]", fehler?.stack || fehler));
app.listen(PORT, "127.0.0.1", () => {
console.log(`Van's DIY & Bastelbedarf Server läuft auf http://127.0.0.1:${PORT}`);
});