export const CONTACT_PROTECTION_SETTINGS_KEY = "contact_protection_settings"; export const CONTACT_RATE_LIMIT_KEY_PREFIX = "contact_rate_limit"; export type ContactProtectionSettings = { turnstile: { enabled: boolean; siteKey: string; secretKey: string; }; rateLimit: { enabled: boolean; maxRequests: number; windowMinutes: number; }; }; export type ContactProtectionFormValues = { turnstile: { enabled: boolean; siteKey: string; secretKey: string; hasSecretKey: boolean; }; rateLimit: { enabled: boolean; maxRequests: number; windowMinutes: number; }; }; export type PublicContactProtectionSettings = { turnstile: { enabled: boolean; siteKey: string; }; rateLimit: { enabled: boolean; }; }; export function buildDefaultContactProtectionSettings(): ContactProtectionSettings { return { turnstile: { enabled: false, siteKey: "", secretKey: "", }, rateLimit: { enabled: true, maxRequests: 5, windowMinutes: 10, }, }; } function parsePositiveInt(value: unknown, fallback: number) { const parsed = typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : fallback; return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback; } export function parseContactProtectionValue( rawValue: string | null | undefined, ): ContactProtectionSettings { const defaults = buildDefaultContactProtectionSettings(); if (!rawValue) { return defaults; } try { const parsed = JSON.parse(rawValue) as Record; const turnstile = parsed.turnstile && typeof parsed.turnstile === "object" ? (parsed.turnstile as Record) : {}; const rateLimit = parsed.rateLimit && typeof parsed.rateLimit === "object" ? (parsed.rateLimit as Record) : {}; return { turnstile: { enabled: Boolean(turnstile.enabled), siteKey: typeof turnstile.siteKey === "string" ? turnstile.siteKey.trim() : "", secretKey: typeof turnstile.secretKey === "string" ? turnstile.secretKey : "", }, rateLimit: { enabled: rateLimit.enabled === undefined ? defaults.rateLimit.enabled : Boolean(rateLimit.enabled), maxRequests: parsePositiveInt(rateLimit.maxRequests, defaults.rateLimit.maxRequests), windowMinutes: parsePositiveInt(rateLimit.windowMinutes, defaults.rateLimit.windowMinutes), }, }; } catch { return defaults; } } export function toContactProtectionFormValues( settings: ContactProtectionSettings, ): ContactProtectionFormValues { return { turnstile: { enabled: settings.turnstile.enabled, siteKey: settings.turnstile.siteKey, secretKey: "", hasSecretKey: Boolean(settings.turnstile.secretKey), }, rateLimit: { enabled: settings.rateLimit.enabled, maxRequests: settings.rateLimit.maxRequests, windowMinutes: settings.rateLimit.windowMinutes, }, }; } export function toPublicContactProtectionSettings( settings: ContactProtectionSettings, ): PublicContactProtectionSettings { return { turnstile: { enabled: settings.turnstile.enabled && Boolean(settings.turnstile.siteKey), siteKey: settings.turnstile.siteKey, }, rateLimit: { enabled: settings.rateLimit.enabled, }, }; }