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.
97 lines
3.5 KiB
JavaScript
97 lines
3.5 KiB
JavaScript
/* =====================================================================
|
|
routes/verwaltung.js — GET /api/verwaltung/orders (Liste) + PATCH /api/verwaltung/orders/:id
|
|
(Status/Tracking ändern). 1:1 portiert aus functions/api/verwaltung/orders.js +
|
|
functions/api/verwaltung/orders/[id].js. Prüft zusätzlich zur gate-Middleware (siehe
|
|
middleware/gate.js) hier nochmal eigenständig beide Schranken — Verteidigung in der Tiefe.
|
|
===================================================================== */
|
|
|
|
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";
|
|
|
|
export const verwaltungRouter = Router();
|
|
|
|
const ERLAUBTE_STATUS = ["zahlungOffen", "bezahlt", "bearbeitung", "versandVorbereitet", "versendet", "abgeschlossen", "storniert"];
|
|
|
|
async function pruefeSchranken(req, res) {
|
|
const role = await getGateRole(req);
|
|
if (!role) {
|
|
unauthorizedJson(res);
|
|
return false;
|
|
}
|
|
if (!(await hatGueltigeVerwaltungSitzung(req))) {
|
|
res.status(401).json({ ok: false, error: "Zusätzlicher Verwaltungs-Code erforderlich." });
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
verwaltungRouter.get("/api/verwaltung/orders", wrap(async (req, res) => {
|
|
if (!(await pruefeSchranken(req, res))) return;
|
|
|
|
try {
|
|
const orders = db.prepare(`SELECT * FROM orders ORDER BY created_at DESC`).all();
|
|
if (orders.length === 0) return res.json({ ok: true, orders: [] });
|
|
|
|
const ids = orders.map((o) => o.id);
|
|
const placeholders = ids.map(() => "?").join(",");
|
|
const items = db.prepare(`SELECT * FROM order_items WHERE order_id IN (${placeholders}) ORDER BY id ASC`).all(...ids);
|
|
|
|
const itemsByOrder = new Map();
|
|
items.forEach((item) => {
|
|
const liste = itemsByOrder.get(item.order_id) ?? [];
|
|
liste.push(item);
|
|
itemsByOrder.set(item.order_id, liste);
|
|
});
|
|
|
|
const result = orders.map((o) => ({ ...o, artikel: itemsByOrder.get(o.id) ?? [] }));
|
|
return res.json({ ok: true, orders: result });
|
|
} catch (err) {
|
|
return res.status(500).json({ ok: false, error: "Bestellungen konnten nicht geladen werden." });
|
|
}
|
|
}));
|
|
|
|
verwaltungRouter.patch("/api/verwaltung/orders/:id", wrap(async (req, res) => {
|
|
if (!(await pruefeSchranken(req, res))) return;
|
|
|
|
const id = Number(req.params.id);
|
|
if (!Number.isInteger(id) || id <= 0) {
|
|
return res.status(400).json({ ok: false, error: "Ungültige Bestell-ID." });
|
|
}
|
|
|
|
const body = req.body || {};
|
|
const updates = [];
|
|
const values = [];
|
|
|
|
if (body.status !== undefined) {
|
|
if (!ERLAUBTE_STATUS.includes(body.status)) {
|
|
return res.status(400).json({ ok: false, error: "Ungültiger Status." });
|
|
}
|
|
updates.push("status = ?");
|
|
values.push(body.status);
|
|
}
|
|
if (body.tracking !== undefined) {
|
|
updates.push("tracking = ?");
|
|
values.push(String(body.tracking || ""));
|
|
}
|
|
if (updates.length === 0) {
|
|
return res.status(400).json({ ok: false, error: "Nichts zu ändern übergeben." });
|
|
}
|
|
|
|
updates.push("updated_at = ?");
|
|
values.push(new Date().toISOString());
|
|
values.push(id);
|
|
|
|
try {
|
|
const result = db.prepare(`UPDATE orders SET ${updates.join(", ")} WHERE id = ?`).run(...values);
|
|
if (result.changes === 0) {
|
|
return res.status(404).json({ ok: false, error: "Bestellung nicht gefunden." });
|
|
}
|
|
return res.json({ ok: true });
|
|
} catch (err) {
|
|
return res.status(500).json({ ok: false, error: "Bestellung konnte nicht aktualisiert werden." });
|
|
}
|
|
}));
|