Warm the server cache from client-side navigations, PUBLIC_SSR_WARM opts out

This commit is contained in:
dtonon 2026-08-20 14:42:48 +01:00
parent 7883e6df8a
commit 5329c611b1
4 changed files with 15 additions and 2 deletions

View file

@ -21,6 +21,11 @@ if (MODE === "simple" && !GROUP_ID) {
// Server-side rendering: opt-in, decided at build time (vite.config.ts bakes
// PUBLIC_SSR in; svelte.config.js picks the matching adapter).
export const SSR_ENABLED = __SQUALK_SSR__;
// With SSR on, a client-side navigation also asks the server for the same
// snapshot (without waiting for it) so the page is already cached for the
// next refresh, shared link or crawler. Costs one server→relay query per
// navigation; opt out with PUBLIC_SSR_WARM=no.
export const SSR_WARM = SSR_ENABLED && env.PUBLIC_SSR_WARM !== "no";
// Server-rendered pages and snapshots are anonymous, so a shared cache may
// hold them: fresh for 5 minutes, served stale for an hour while revalidating.
// Browsers always revalidate (max-age=0) so a login shows its content at once.

View file

@ -1,5 +1,6 @@
import { browser } from "$app/environment";
import { error } from "@sveltejs/kit";
import { SSR_WARM } from "$lib/config";
import type { NostrUser } from "$lib/gadgets";
import type { GroupMetadata, GroupSummary } from "./groups";
import type { Resource } from "./resources";
@ -37,14 +38,18 @@ type Fetch = typeof fetch;
// Read a snapshot endpoint. On the server the response is inlined into the
// HTML; in the browser it is consumed only while hydrating that HTML. Client
// navigations get null, leaving the stores to fetch live (and authenticated)
// data as they always did — the same path a client-only build takes.
// data as they always did — the same path a client-only build takes — but
// may still poke the endpoint so the server has the page cached.
// A 404 from the endpoint becomes a real 404 page on the server, so crawlers
// never index an empty shell; the client keeps its own not-found handling.
export async function snapshot<T>(
fetch: Fetch,
url: string,
): Promise<T | null> {
if (browser && !hasInlinedResponse(url)) return null;
if (browser && !hasInlinedResponse(url)) {
if (SSR_WARM) fetch(url).catch(() => {});
return null;
}
let res: Response;
try {
res = await fetch(url);