113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
"use server";
|
|
|
|
import { revalidatePath } from "next/cache";
|
|
import { redirect } from "next/navigation";
|
|
import { isRedirectError } from "next/dist/client/components/redirect";
|
|
|
|
import { toInternalAdminPath } from "@/lib/admin-routing";
|
|
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
|
|
import { isCheckedFormValue } from "@/lib/form-data";
|
|
import {
|
|
getContactProtectionSettings,
|
|
updateContactProtectionSettings,
|
|
} from "@/lib/app-config";
|
|
import type { ContactProtectionSettings } from "@/lib/contact-protection";
|
|
|
|
function ensureAdmin() {
|
|
if (!isAdminAuthenticated()) {
|
|
clearAdminSessionCookie();
|
|
redirect("/");
|
|
}
|
|
}
|
|
|
|
function withMessage(pathname: string, type: "success" | "error", message: string) {
|
|
const params = new URLSearchParams();
|
|
params.set(type, message);
|
|
|
|
return `${pathname}?${params.toString()}`;
|
|
}
|
|
|
|
function parseCount(value: string, fallback: number, label: string) {
|
|
const parsed = Number.parseInt(value || String(fallback), 10);
|
|
|
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
throw new Error(`${label} must be a positive number.`);
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
function parseContactProtectionFormData(
|
|
formData: FormData,
|
|
existingSettings: ContactProtectionSettings,
|
|
): ContactProtectionSettings {
|
|
const turnstileEnabled = isCheckedFormValue(formData.get("turnstileEnabled"));
|
|
const turnstileSiteKey = String(formData.get("turnstileSiteKey") ?? "").trim();
|
|
const turnstileSecretKey = String(formData.get("turnstileSecretKey") ?? "");
|
|
const rateLimitEnabled = isCheckedFormValue(formData.get("contactRateLimitEnabled"));
|
|
|
|
if (turnstileEnabled && !turnstileSiteKey) {
|
|
throw new Error("Turnstile site key is required when Turnstile is enabled.");
|
|
}
|
|
|
|
if (turnstileEnabled && !turnstileSecretKey.trim() && !existingSettings.turnstile.secretKey) {
|
|
throw new Error("Turnstile secret key is required when Turnstile is enabled.");
|
|
}
|
|
|
|
return {
|
|
turnstile: {
|
|
enabled: turnstileEnabled,
|
|
siteKey: turnstileSiteKey,
|
|
secretKey: turnstileSecretKey.trim()
|
|
? turnstileSecretKey
|
|
: existingSettings.turnstile.secretKey,
|
|
},
|
|
rateLimit: {
|
|
enabled: rateLimitEnabled,
|
|
maxRequests: parseCount(
|
|
String(formData.get("contactRateLimitMaxRequests") ?? ""),
|
|
existingSettings.rateLimit.maxRequests,
|
|
"Rate limit max requests",
|
|
),
|
|
windowMinutes: parseCount(
|
|
String(formData.get("contactRateLimitWindowMinutes") ?? ""),
|
|
existingSettings.rateLimit.windowMinutes,
|
|
"Rate limit window minutes",
|
|
),
|
|
},
|
|
};
|
|
}
|
|
|
|
export async function saveContactProtectionSettingsAction(formData: FormData) {
|
|
ensureAdmin();
|
|
|
|
try {
|
|
const existingSettings = await getContactProtectionSettings();
|
|
const nextSettings = parseContactProtectionFormData(formData, existingSettings);
|
|
|
|
await updateContactProtectionSettings(nextSettings);
|
|
revalidatePath(toInternalAdminPath("/smtp/contact-protection"));
|
|
revalidatePath("/contact");
|
|
revalidatePath("/ar/contact");
|
|
revalidatePath("/en/contact");
|
|
redirect(
|
|
withMessage(
|
|
"/smtp/contact-protection",
|
|
"success",
|
|
"Contact Protection gespeichert.",
|
|
),
|
|
);
|
|
} catch (error) {
|
|
if (isRedirectError(error)) {
|
|
throw error;
|
|
}
|
|
|
|
const message =
|
|
error instanceof Error
|
|
? error.message
|
|
: "Contact Protection konnte nicht gespeichert werden.";
|
|
|
|
redirect(withMessage("/smtp/contact-protection", "error", message));
|
|
}
|
|
}
|