Sicherheits-, Stabilitaets- und Domainkorrekturen nach vollstaendiger Pruefung
Build & Deploy / deploy (push) Waiting to run

- 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.
This commit is contained in:
qcigano
2026-08-05 22:33:49 +02:00
parent 654f4455cc
commit ab3a5da892
14 changed files with 94 additions and 28 deletions
+7 -6
View File
@@ -4,26 +4,27 @@
===================================================================== */
import { Router } from "express";
import { wrap } from "../lib/async-wrap.js";
import { getCustomer, clearCustomerCookie } from "../lib/customer-auth.js";
import { db } from "../db.js";
export const accountRouter = Router();
accountRouter.get("/api/account/me", async (req, res) => {
accountRouter.get("/api/account/me", wrap(async (req, res) => {
const customer = await getCustomer(req);
if (!customer) return res.json({ ok: false });
return res.json({
ok: true,
customer: { email: customer.email, name: customer.name, provider: customer.provider, seit: customer.created_at },
});
});
}));
accountRouter.post("/api/account/logout", (req, res) => {
clearCustomerCookie(res);
return res.json({ ok: true });
});
accountRouter.post("/api/account/delete", async (req, res) => {
accountRouter.post("/api/account/delete", wrap(async (req, res) => {
const customer = await getCustomer(req);
if (!customer) return res.status(401).json({ ok: false, error: "Nicht angemeldet." });
@@ -35,9 +36,9 @@ accountRouter.post("/api/account/delete", async (req, res) => {
clearCustomerCookie(res);
return res.json({ ok: true });
});
}));
accountRouter.get("/api/account/export", async (req, res) => {
accountRouter.get("/api/account/export", wrap(async (req, res) => {
const customer = await getCustomer(req);
if (!customer) return res.status(401).json({ ok: false, error: "Nicht angemeldet." });
@@ -51,4 +52,4 @@ accountRouter.get("/api/account/export", async (req, res) => {
res.setHeader("Content-Disposition", 'attachment; filename="meine-daten.json"');
return res.status(200).json(daten);
});
}));
+8 -4
View File
@@ -9,7 +9,11 @@ import { starteOAuth, verarbeiteOAuthCallback } from "../lib/oauth-handlers.js";
export const authRouter = Router();
authRouter.get("/api/auth/google/start", (req, res) => starteOAuth(req, res, "google"));
authRouter.get("/api/auth/google/callback", (req, res) => verarbeiteOAuthCallback(req, res, "google"));
authRouter.get("/api/auth/paypal/start", (req, res) => starteOAuth(req, res, "paypal"));
authRouter.get("/api/auth/paypal/callback", (req, res) => verarbeiteOAuthCallback(req, res, "paypal"));
/* wrap() ergänzt 05.08.2026: Express 4 fängt Fehler aus async-Handlern nicht selbst ab — ohne das
würde z.B. ein Netzwerkfehler beim Token-Austausch mit Google/PayPal den ganzen Shop beenden. */
const wrap = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
authRouter.get("/api/auth/google/start", wrap((req, res) => starteOAuth(req, res, "google")));
authRouter.get("/api/auth/google/callback", wrap((req, res) => verarbeiteOAuthCallback(req, res, "google")));
authRouter.get("/api/auth/paypal/start", wrap((req, res) => starteOAuth(req, res, "paypal")));
authRouter.get("/api/auth/paypal/callback", wrap((req, res) => verarbeiteOAuthCallback(req, res, "paypal")));
+3 -2
View File
@@ -5,11 +5,12 @@
===================================================================== */
import { Router } from "express";
import { wrap } from "../lib/async-wrap.js";
import { erstelleBestellung } from "../lib/bestellung-erstellen.js";
export const ordersRouter = Router();
ordersRouter.post("/api/orders", async (req, res) => {
ordersRouter.post("/api/orders", wrap(async (req, res) => {
const body = req.body || {};
const zahlungsart = String(body.zahlungsart || "");
@@ -28,4 +29,4 @@ ordersRouter.post("/api/orders", async (req, res) => {
return res.status(ergebnis.httpStatus || 500).json({ ok: false, error: ergebnis.error });
}
return res.status(201).json({ ok: true, id: ergebnis.id, bestellnummer: ergebnis.bestellnummer });
});
}));
+5 -4
View File
@@ -6,6 +6,7 @@
===================================================================== */
import { Router } from "express";
import { wrap } from "../lib/async-wrap.js";
import { paypalKonfiguriert, erstellePaypalBestellung, erfassePaypalZahlung } from "../lib/paypal.js";
import { erstelleBestellung } from "../lib/bestellung-erstellen.js";
@@ -13,7 +14,7 @@ export const paypalRouter = Router();
const BETRAG_TOLERANZ_EURO = 0.02;
paypalRouter.post("/api/paypal/create-order", async (req, res) => {
paypalRouter.post("/api/paypal/create-order", wrap(async (req, res) => {
if (!paypalKonfiguriert()) {
return res.status(503).json({
ok: false,
@@ -33,9 +34,9 @@ paypalRouter.post("/api/paypal/create-order", async (req, res) => {
} catch (err) {
return res.status(502).json({ ok: false, error: err instanceof Error ? err.message : "PayPal-Bestellung fehlgeschlagen." });
}
});
}));
paypalRouter.post("/api/paypal/capture-order", async (req, res) => {
paypalRouter.post("/api/paypal/capture-order", wrap(async (req, res) => {
if (!paypalKonfiguriert()) {
return res.status(503).json({ ok: false, error: "PayPal ist auf dieser Seite noch nicht eingerichtet." });
}
@@ -72,4 +73,4 @@ paypalRouter.post("/api/paypal/capture-order", async (req, res) => {
}
return res.status(201).json({ ok: true, id: ergebnis.id, bestellnummer: ergebnis.bestellnummer });
});
}));
+5 -4
View File
@@ -6,6 +6,7 @@
===================================================================== */
import { Router } from "express";
import { wrap } from "../lib/async-wrap.js";
import { getGateRole, unauthorizedJson } from "../lib/auth.js";
import { hatGueltigeVerwaltungSitzung } from "../lib/verwaltung-auth.js";
import { db } from "../db.js";
@@ -27,7 +28,7 @@ async function pruefeSchranken(req, res) {
return true;
}
verwaltungRouter.get("/api/verwaltung/orders", async (req, res) => {
verwaltungRouter.get("/api/verwaltung/orders", wrap(async (req, res) => {
if (!(await pruefeSchranken(req, res))) return;
try {
@@ -50,9 +51,9 @@ verwaltungRouter.get("/api/verwaltung/orders", async (req, res) => {
} catch (err) {
return res.status(500).json({ ok: false, error: "Bestellungen konnten nicht geladen werden." });
}
});
}));
verwaltungRouter.patch("/api/verwaltung/orders/:id", async (req, res) => {
verwaltungRouter.patch("/api/verwaltung/orders/:id", wrap(async (req, res) => {
if (!(await pruefeSchranken(req, res))) return;
const id = Number(req.params.id);
@@ -92,4 +93,4 @@ verwaltungRouter.patch("/api/verwaltung/orders/:id", async (req, res) => {
} catch (err) {
return res.status(500).json({ ok: false, error: "Bestellung konnte nicht aktualisiert werden." });
}
});
}));