Files
kita-planer/src/actions/contact.ts
T

99 lines
2.7 KiB
TypeScript

"use server";
import { createElement } from "react";
import { headers } from "next/headers";
import { ContactConfirmationEmail } from "@/emails/ContactConfirmationEmail";
import { ContactRequestEmail } from "@/emails/ContactRequestEmail";
import {
contactRequestSchema,
contactRequestTypeLabels,
type ContactSubmissionPayload,
} from "@/lib/contact-schema";
import { getAppEmailConfigError, sendAppEmail } from "@/lib/mail";
import { rateLimit } from "@/lib/rate-limit";
const DEFAULT_ADMIN_EMAIL = "kontakt@kita-planer.local";
const RATE_LIMIT_MAX = 5;
const RATE_LIMIT_WINDOW_SECONDS = 60 * 60;
async function resolveClientIp(): Promise<string> {
const h = await headers();
const forwarded = h.get("x-forwarded-for");
if (forwarded) {
return forwarded.split(",")[0]?.trim() || "unknown";
}
return h.get("x-real-ip") ?? "unknown";
}
export async function submitContactRequest(data: ContactSubmissionPayload) {
// Honeypot: silently absorb bot submissions without informing them.
if (data.website && data.website.trim().length > 0) {
return { success: true };
}
const ip = await resolveClientIp();
const limit = rateLimit(
`contact:${ip}`,
RATE_LIMIT_MAX,
RATE_LIMIT_WINDOW_SECONDS,
);
if (!limit.allowed) {
const minutes = Math.max(1, Math.ceil(limit.retryAfterSeconds / 60));
return {
success: false,
error: `Zu viele Anfragen. Bitte versuche es in ${minutes} Minuten erneut.`,
};
}
const parsed = contactRequestSchema.safeParse(data);
if (!parsed.success) {
return {
success: false,
error: "Bitte prüfe deine Eingaben und versuche es erneut.",
};
}
const configError = getAppEmailConfigError();
if (configError) {
return {
success: false,
error: configError,
};
}
const adminEmail = process.env.ADMIN_EMAIL || DEFAULT_ADMIN_EMAIL;
const inquiryLabel = contactRequestTypeLabels[parsed.data.requestType];
const [adminResult, confirmationResult] = await Promise.all([
sendAppEmail({
to: adminEmail,
subject: `Kontaktanfrage: ${inquiryLabel} von ${parsed.data.name}`,
replyTo: parsed.data.email,
react: createElement(ContactRequestEmail, parsed.data),
}),
sendAppEmail({
to: parsed.data.email,
subject: "Wir haben deine Anfrage erhalten — Kita-Planer",
replyTo: adminEmail,
react: createElement(ContactConfirmationEmail, parsed.data),
}),
]);
if (!adminResult.success) {
return {
success: false,
error: adminResult.error,
};
}
if (!confirmationResult.success) {
console.error(
"Bestätigungs-E-Mail konnte nicht zugestellt werden:",
confirmationResult.error,
);
}
return { success: true };
}