perf(app): batch catalog updates, tune caches, add captcha env fallback
- catalog: apply bulk offer edits in a single transaction with one RCON refresh instead of N round-trips, virtualize the items table past 150 rows and cap the shop preview at 200 tiles - cache: lift the public cache TTLs and the boot warm-up into env-tunable constants (src/lib/perf-ttls.ts; CMS_*_TTL_SEC, CMS_WARMUP_ENABLED) - auth: resolve the captcha provider, site key and secret from CAPTCHA_PROVIDER, TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY when the admin settings are unset; an explicit DB value (including "none") still wins, so Turnstile can be enabled per-deploy without touching the DB.
This commit is contained in:
1 parent
05c2706eaf
commit
9f024dc6ac
15 files changed
+529
-83
No files matched your search
+40
-16
@@ -1,20 +1,28 @@
|
||||
import { env } from "@/env";
|
||||
import { siteSettings } from "@/lib/services/site-settings";
|
||||
|
||||
/**
|
||||
* Server-side CAPTCHA verification, driven by website_settings so staff pick the
|
||||
* provider in housekeeping. Supports Cloudflare Turnstile, Google reCAPTCHA,
|
||||
* and hCaptcha (three AtomCMS offers, mutually exclusive).
|
||||
* Server-side CAPTCHA verification. The provider is driven by website_settings
|
||||
* (staff pick it in housekeeping) with an ENV FALLBACK so a deploy can enable
|
||||
* Turnstile without touching the DB: unset DB keys fall back to
|
||||
* CAPTCHA_PROVIDER / TURNSTILE_SITE_KEY / TURNSTILE_SECRET_KEY. An explicit DB
|
||||
* value — including "none" — always wins over env. Supports Cloudflare
|
||||
* Turnstile, Google reCAPTCHA, and hCaptcha (mutually exclusive).
|
||||
*
|
||||
* Behaviour:
|
||||
* - provider "none" (or unset) → fail-open (allow)
|
||||
* - provider "none" (or unset everywhere) → fail-open (allow)
|
||||
* - provider configured but site key / secret missing, token absent, provider
|
||||
* API error, or network failure → fail-closed (deny)
|
||||
*
|
||||
* Settings keys:
|
||||
* captcha_provider = "turnstile" | "recaptcha" | "hcaptcha" | "none" (default none)
|
||||
* turnstile_secret / turnstile_site_key
|
||||
* recaptcha_secret / recaptcha_site_key
|
||||
* hcaptcha_secret / hcaptcha_site_key
|
||||
* Settings keys (DB) / env fallbacks:
|
||||
* captcha_provider = "turnstile" | "recaptcha" | "hcaptcha" | "none"
|
||||
* CAPTCHA_PROVIDER (env, used when the DB key is unset)
|
||||
* turnstile_site_key / TURNSTILE_SITE_KEY
|
||||
* turnstile_secret / TURNSTILE_SECRET_KEY
|
||||
* recaptcha_site_key (DB only)
|
||||
* recaptcha_secret (DB only)
|
||||
* hcaptcha_site_key (DB only)
|
||||
* hcaptcha_secret (DB only)
|
||||
*/
|
||||
export interface CaptchaConfig {
|
||||
provider: "turnstile" | "recaptcha" | "hcaptcha" | "none";
|
||||
@@ -28,15 +36,32 @@ const TURNSTILE_URL =
|
||||
const RECAPTCHA_URL = "https://www.google.com/recaptcha/api/siteverify";
|
||||
const HCAPTCHA_URL = "https://hcaptcha.com/siteverify";
|
||||
|
||||
/** DB first (staff can override live), env fallback for unconfigured deploys. */
|
||||
async function settingOrEnv(
|
||||
settingKey: string,
|
||||
settingDefault: string,
|
||||
envValue: string | undefined,
|
||||
): Promise<string> {
|
||||
const dbValue = (await siteSettings.get(settingKey, settingDefault)) ?? "";
|
||||
if (dbValue.trim() !== "") return dbValue.trim();
|
||||
return envValue?.trim() || "";
|
||||
}
|
||||
|
||||
/** Public config the register/login pages need to render the widget (no secrets). */
|
||||
export async function captchaConfig(): Promise<CaptchaConfig> {
|
||||
// Empty DB fallback distinguishes "unset" from an explicit "none": only an
|
||||
// explicit DB value short-circuits the env fallback.
|
||||
const provider = (
|
||||
(await siteSettings.get("captcha_provider", "none")) ?? "none"
|
||||
await settingOrEnv("captcha_provider", "", env.CAPTCHA_PROVIDER)
|
||||
).toLowerCase();
|
||||
if (provider === "turnstile") {
|
||||
return {
|
||||
provider: "turnstile",
|
||||
siteKey: (await siteSettings.get("turnstile_site_key", "")) ?? "",
|
||||
siteKey: await settingOrEnv(
|
||||
"turnstile_site_key",
|
||||
"",
|
||||
env.TURNSTILE_SITE_KEY,
|
||||
),
|
||||
field: "cf-turnstile-response",
|
||||
};
|
||||
}
|
||||
@@ -67,13 +92,12 @@ export async function verifyCaptcha(
|
||||
|
||||
if (!cfg.siteKey) return false;
|
||||
|
||||
const secretKey: string =
|
||||
const secret =
|
||||
cfg.provider === "turnstile"
|
||||
? "turnstile_secret"
|
||||
? await settingOrEnv("turnstile_secret", "", env.TURNSTILE_SECRET_KEY)
|
||||
: cfg.provider === "recaptcha"
|
||||
? "recaptcha_secret"
|
||||
: "hcaptcha_secret";
|
||||
const secret = (await siteSettings.get(secretKey, "")) ?? "";
|
||||
? ((await siteSettings.get("recaptcha_secret", "")) ?? "")
|
||||
: ((await siteSettings.get("hcaptcha_secret", "")) ?? "");
|
||||
if (!secret) return false;
|
||||
if (!token) return false;
|
||||
|
||||
|
||||
Reference in new issue
Block a user