Add SMTP admin settings and contact form delivery
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
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<string, unknown>;
|
||||
const turnstile = parsed.turnstile && typeof parsed.turnstile === "object"
|
||||
? (parsed.turnstile as Record<string, unknown>)
|
||||
: {};
|
||||
const rateLimit = parsed.rateLimit && typeof parsed.rateLimit === "object"
|
||||
? (parsed.rateLimit as Record<string, unknown>)
|
||||
: {};
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user