133 lines
3.4 KiB
TypeScript
133 lines
3.4 KiB
TypeScript
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,
|
|
},
|
|
};
|
|
}
|