import { createReviewStorage } from "./services/review-storage.js"; import { handleReview } from "./routes/reviews.js"; import { createContactService } from "./services/contact-service.js"; import { createContactEmailService } from "./services/contact-email.js"; import { handleContact } from "./routes/contact.js"; import { createServer } from "node:http"; import { fileURLToPath, pathToFileURL } from "node:url"; import { packages } from "../shared/packages.js"; import { BookingError, createBookingService, } from "./services/booking-service.js"; import { createCsvStorage } from "./services/csv-storage.js"; import { createEmailService } from "./services/email-service.js"; import { handleBooking, json } from "./routes/bookings.js"; import { handleContactForm } from "./routes/contact-form.js"; import { createContactFormService } from "./services/contact-form-service.js"; import { createContactFormStorage } from "./services/contact-form-storage.js"; import { createContactFormEmailService } from "./services/contact-form-email.js"; export function createApp({ catalog = packages, storage = createCsvStorage( fileURLToPath(new URL("./data/bookings.csv", import.meta.url)), ), reviewStorage = createReviewStorage( fileURLToPath(new URL("./data/reviews.csv", import.meta.url)), ), details, sendConfirmation, sendContact, contactFormStorage = createContactFormStorage( fileURLToPath(new URL("./data/contact-requests.csv", import.meta.url)), ), sendContactForm, advisorContactStorage = createContactFormStorage( fileURLToPath(new URL("./data/advisor-contacts.csv", import.meta.url)), [ "createdAt", "email", "advisorId", "reason", "simulationAccepted", "consent", ], ), } = {}) { const ids = new Set(); for (const entry of catalog) { if ( !entry || typeof entry.id !== "string" || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(entry.id) || entry.id.length > 80 || ids.has(entry.id) || typeof entry.name !== "string" || !entry.name.trim() || typeof entry.summary !== "string" || !entry.summary.trim() ) { throw new Error( "Paketdaten erfüllen den Vertrag in src/shared/packages.js nicht.", ); } ids.add(entry.id); } const service = createBookingService({ storage, details, packages: catalog, sendConfirmation: sendConfirmation ?? createEmailService(), }); const contact = createContactService({ storage, requestStorage: advisorContactStorage, sendContact: sendContact ?? createContactEmailService(), }); // Registriert den separaten Kontaktformular-Service mit eigener CSV und Mailpit-Versand. const contactForm = createContactFormService({ storage: contactFormStorage, sendContactForm: sendContactForm ?? createContactFormEmailService(), }); return createServer(async (request, response) => { const path = new URL(request.url, "http://localhost").pathname; if (path === "/api/reviews") { return handleReview(request, response, storage, reviewStorage); } // Leitet die Ernährungsseite an ihren eigenen Kontaktformular-Endpunkt. if (path === "/api/nutrition-contact") { return handleContactForm(request, response, contactForm); } if (path === "/api/contact" || path === "/api/contact/validate") { return handleContact( request, response, contact, path.endsWith("/validate"), ); } if (path === "/api/angebote" && request.method === "GET") { return json(response, 200, { packages: catalog.map(({ id, name, summary }) => ({ id, name, summary, })), }); } if (path === "/api/packages" && request.method === "GET") { return json( response, 200, catalog.map(({ name, summary, ...entry }) => ({ ...entry, title: name, description: summary, })), ); } if (path.startsWith("/api/packages/") && request.method === "GET") { try { return json( response, 200, service.getPackage(path.slice("/api/packages/".length)), ); } catch (error) { if (!(error instanceof BookingError)) throw error; return json(response, error.status, { error: { code: error.code, message: error.message, fields: error.fields, }, }); } } if (path === "/api/bookings" || path === "/api/bookings/preview") { if (request.method !== "POST") { response.setHeader("Allow", "POST"); return json(response, 405, { error: { code: "METHOD_NOT_ALLOWED", message: "Bitte POST verwenden.", }, }); } return handleBooking(request, response, service, { preview: path.endsWith("/preview"), }); } // Ausschließlich API; keine Auslieferung von CSV oder Serverdateien. return json(response, 404, { error: { code: "NOT_FOUND", message: "Adresse nicht gefunden." }, }); }); } if ( process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href ) { const port = Number(process.env.PORT || 3000); const server = createApp(); server.listen(port, process.env.HOST || "127.0.0.1", () => { console.log(`Buchungs-API auf Port ${port}`); }); }