From b630267f33f560492475cc322cda9a7398a574ce Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 30 Apr 2026 01:43:41 +0800 Subject: [PATCH] perf: cache scoped site settings --- app/api/site-settings/route.ts | 44 ++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/app/api/site-settings/route.ts b/app/api/site-settings/route.ts index 0b29662aa..bb200ed83 100644 --- a/app/api/site-settings/route.ts +++ b/app/api/site-settings/route.ts @@ -463,7 +463,22 @@ const PUBLIC_SITE_SETTINGS_SCOPES = new Set([ "home", ]); -function createScopedSiteSettingsResponse(data: Record) { +const SCOPED_SITE_SETTINGS_SERVER_CACHE_TTL_MS = 30_000; +const scopedSiteSettingsServerCache = new Map< + PublicSiteSettingsScope, + { data: Record; expiresAt: number } +>(); + +function createScopedSiteSettingsResponse( + scope: PublicSiteSettingsScope, + data: Record, + cacheStatus: "HIT" | "MISS" = "MISS" +) { + scopedSiteSettingsServerCache.set(scope, { + data, + expiresAt: Date.now() + SCOPED_SITE_SETTINGS_SERVER_CACHE_TTL_MS, + }); + const response = NextResponse.json(data); // Lightweight public settings change rarely; keep them browser-local briefly // so page opens do not repeatedly wait on the large full settings endpoint. @@ -471,6 +486,8 @@ function createScopedSiteSettingsResponse(data: Record) { "Cache-Control", "private, max-age=30, stale-while-revalidate=120" ); + response.headers.set("X-Site-Settings-Scope", scope); + response.headers.set("X-Site-Settings-Cache", cacheStatus); return response; } @@ -509,7 +526,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { const { settings, valueByKey } = await readScopedSiteSettings([ "login_notification", ]); - return createScopedSiteSettingsResponse({ + return createScopedSiteSettingsResponse(scope, { ...createBrandingSettings(settings as Record | undefined), notification: valueByKey.get("login_notification") || { enabled: false, @@ -523,7 +540,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { const { valueByKey } = await readScopedSiteSettings([ AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY, ]); - return createScopedSiteSettingsResponse({ + return createScopedSiteSettingsResponse(scope, { agentWorkflowBrandModelConfig: normalizeAgentWorkflowBrandModelConfig( valueByKey.get(AGENT_WORKFLOW_BRAND_MODEL_CONFIG_KEY) ), @@ -535,7 +552,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { AGENT_PRESET_LIBRARY_CONFIG_KEY, AGENT_WORKFLOW_SHOWCASE_CONFIG_KEY, ]); - return createScopedSiteSettingsResponse({ + return createScopedSiteSettingsResponse(scope, { agentPresetLibraryConfig: normalizeAgentPresetLibraryConfig( valueByKey.get(AGENT_PRESET_LIBRARY_CONFIG_KEY) ), @@ -571,7 +588,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { valueByKey.get(AGENT_PSD_TEMPLATE_CATEGORY_TAG_CONFIG_KEY) ); - return createScopedSiteSettingsResponse({ + return createScopedSiteSettingsResponse(scope, { ...createBrandingSettings(typedSettings), agentEnabled: typedSettings?.agentEnabled ?? true, canvasAgentModeEnabled: normalizeCanvasAgentModeEnabled( @@ -615,6 +632,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { if (scope === "branding") { const { settings } = await readScopedSiteSettings([]); return createScopedSiteSettingsResponse( + scope, createBrandingSettings(settings as Record | undefined) ); } @@ -622,7 +640,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { if (scope === "chat") { const { settings } = await readScopedSiteSettings([]); const typedSettings = settings as Record | undefined; - return createScopedSiteSettingsResponse({ + return createScopedSiteSettingsResponse(scope, { ...createBrandingSettings(typedSettings), showAutoModel: typedSettings?.showAutoModel ?? true, usageTimeEnabled: typedSettings?.usageTimeEnabled ?? false, @@ -648,7 +666,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { valueByKey.get(VIDEO_CANVAS_SIDEBAR_ENABLED_KEY) ); - return createScopedSiteSettingsResponse({ + return createScopedSiteSettingsResponse(scope, { ...createBrandingSettings(typedSettings), agentEnabled: typedSettings?.agentEnabled ?? true, canvasAgentModeEnabled: normalizeCanvasAgentModeEnabled( @@ -685,7 +703,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { ]); const typedSettings = settings as Record | undefined; - return createScopedSiteSettingsResponse({ + return createScopedSiteSettingsResponse(scope, { ...createBrandingSettings(typedSettings), bannerEnabled: typedSettings?.bannerEnabled ?? true, bannerText: @@ -726,7 +744,7 @@ async function getScopedPublicSiteSettings(scope: PublicSiteSettingsScope) { ]); const typedSettings = settings as Record | undefined; - return createScopedSiteSettingsResponse({ + return createScopedSiteSettingsResponse(scope, { ...createBrandingSettings(typedSettings), canvasDefaultModelId: typedSettings?.canvasDefaultModelId || null, lovartComposerPrefix: normalizeLovartComposerPrefix( @@ -769,7 +787,13 @@ export async function GET(request: NextRequest) { try { const scope = request.nextUrl.searchParams.get("scope") || ""; if (PUBLIC_SITE_SETTINGS_SCOPES.has(scope)) { - return getScopedPublicSiteSettings(scope as PublicSiteSettingsScope); + const scopedScope = scope as PublicSiteSettingsScope; + const cached = scopedSiteSettingsServerCache.get(scopedScope); + if (cached && cached.expiresAt > Date.now()) { + return createScopedSiteSettingsResponse(scopedScope, cached.data, "HIT"); + } + + return getScopedPublicSiteSettings(scopedScope); } const session = await auth().catch(() => null);