149 lines
5.5 KiB
JavaScript
149 lines
5.5 KiB
JavaScript
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)),
|
|
),
|
|
details,
|
|
sendConfirmation,
|
|
sendContact,
|
|
contactFormStorage = createContactFormStorage(
|
|
fileURLToPath(new URL("./data/contact-requests.csv", import.meta.url)),
|
|
),
|
|
sendContactForm,
|
|
} = {}) {
|
|
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,
|
|
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;
|
|
// 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}`);
|
|
});
|
|
}
|