Fix E-Mail-Versand und verbessere Fehlerbehandlung

- Behebe Port-Konfiguration für interne RPC-Verbindungen (5173 -> 3000)
- Verbessere oRPC-Fehlerbehandlung: ursprüngliche Fehlermeldungen werden beibehalten
- Erweitere Frontend-Fehlerbehandlung für bessere oRPC-Integration
- Deaktiviere Duplikat-Prüfung in Development-Modus (NODE_ENV=development)
- Lokale Entwicklung ermöglicht mehrere Buchungen pro E-Mail-Adresse
- Produktion behält Duplikat-Schutz bei
This commit is contained in:
2025-10-02 10:01:01 +02:00
parent f2e12df6d5
commit 73cf733c5f
6 changed files with 38 additions and 13 deletions

1
.gitignore vendored
View File

@@ -22,6 +22,7 @@ Thumbs.db
*.njsproj
*.sln
*.sw?
.notes.txt
# Turbo
.turbo

View File

@@ -15,4 +15,4 @@ services:
retries: 3
start_period: 40s
environment:
- NODE_ENV=production
- NODE_ENV=development

View File

@@ -1,6 +1,6 @@
@echo off
docker compose -f docker-compose.yml down
git pull
docker compose -f docker-compose.yml build --no-cache
docker compose -f docker-compose.yml build
docker compose -f docker-compose.yml up -d
docker compose -f docker-compose.yml logs -f stargirlnails

View File

@@ -205,7 +205,22 @@ export function BookingForm() {
},
onError: (error: any) => {
console.error("Booking error:", error);
const errorText = error?.message || error?.toString() || "Ein unbekannter Fehler ist aufgetreten.";
// Extract error message from oRPC error structure
let errorText = "Ein unbekannter Fehler ist aufgetreten.";
if (error?.message) {
errorText = error.message;
} else if (error?.data?.message) {
errorText = error.data.message;
} else if (error?.data?.error) {
errorText = error.data.error;
} else if (typeof error === 'string') {
errorText = error;
} else if (error?.toString) {
errorText = error.toString();
}
setErrorMessage(errorText);
},
}

View File

@@ -20,6 +20,11 @@ rpcApp.all("/*", async (c) => {
return c.json({ error: "Not found" }, 404);
} catch (error) {
console.error("RPC Handler error:", error);
return c.json({ error: "Internal server error" }, 500);
// Preserve the original error message if it's a known error
const errorMessage = error instanceof Error ? error.message : "Internal server error";
const statusCode = error instanceof Error && error.message.includes("bereits eine Buchung") ? 400 : 500;
return c.json({ error: errorMessage }, statusCode);
}
});

View File

@@ -12,7 +12,8 @@ import { checkBookingRateLimit, getClientIP } from "../lib/rate-limiter.js";
import { validateEmail } from "../lib/email-validator.js";
// Create a server-side client to call other RPC endpoints
const link = new RPCLink({ url: "http://localhost:5173/rpc" });
const serverPort = process.env.PORT ? parseInt(process.env.PORT) : 3000;
const link = new RPCLink({ url: `http://localhost:${serverPort}/rpc` });
// Typisierung über any, um Build-Inkompatibilität mit NestedClient zu vermeiden (nur für interne Server-Calls)
const queryClient = createORPCClient<any>(link);
@@ -117,14 +118,17 @@ const create = os
}
// Prevent double booking: same customer email with pending/confirmed on same date
const existing = await kv.getAllItems();
const hasConflict = existing.some(b =>
b.customerEmail.toLowerCase() === input.customerEmail.toLowerCase() &&
b.appointmentDate === input.appointmentDate &&
(b.status === "pending" || b.status === "confirmed")
);
if (hasConflict) {
throw new Error("Du hast bereits eine Buchung für dieses Datum. Bitte wähle einen anderen Tag oder storniere zuerst.");
// Skip duplicate check in development mode
if (process.env.NODE_ENV !== 'development') {
const existing = await kv.getAllItems();
const hasConflict = existing.some(b =>
b.customerEmail.toLowerCase() === input.customerEmail.toLowerCase() &&
b.appointmentDate === input.appointmentDate &&
(b.status === "pending" || b.status === "confirmed")
);
if (hasConflict) {
throw new Error("Du hast bereits eine Buchung für dieses Datum. Bitte wähle einen anderen Tag oder storniere zuerst.");
}
}
const id = randomUUID();
const booking = {