Files
cobble/src/functions/server/pwa/handle-precache-gate.ts
T
tbenandClaude Opus 5.5 3b7c05bfa0 Name each install, fix the notification badge, switch to Archivo
- Settings > General: an App name (super admin) for the installed app.
  The manifest's name/short_name, the iOS head tags and the loading
  page's title use it, so two installs on one phone can be told apart.
- Notifications get a real badge (white stone silhouette): Android masks
  the badge by alpha, so the opaque app icon showed as a square.
- The loading and offline pages load the app's font.
- The font is now Archivo (variable, one request for every weight),
  matching the marketing site.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 10:17:40 +01:00

75 lines
2.4 KiB
TypeScript

import path from "path";
import { AppData } from "@/src/data/app-data";
import { PwaLoadingNamePlaceholder, PwaLoadingPath } from "@/src/data/pwa-data";
import grabInstallName from "@/src/functions/backend/db/app-settings/grab-install-name";
import installDisplayName from "@/src/utils/install-display-name";
import escapeHtml from "@/src/utils/escape-html";
import { getCookie } from "@/src/utils/cookies";
import needsPrecachePage from "@/src/utils/needs-precache-page";
import grabPrecacheManifest from "./grab-precache-manifest";
const LOADING_PAGE = path.join(
path.resolve(import.meta.dir, "../../../../public"),
PwaLoadingPath,
);
/**
* The placeholder `public/loading.html` carries for the running build.
*/
const BuildPlaceholder = "{{build}}";
/**
* Production only: a browser opening a page without the running build in
* its cookie gets the loading page in its place, at the same address. The
* page downloads the build's `.bunext/public` files into the build's
* cache, checks they are all there, sets the cookie and reloads, and the
* service worker then serves the app's code from the device. Null lets
* the request through.
*/
export default async function handlePrecacheGate({
req,
url,
}: {
req: Request;
url: URL;
}): Promise<Response | null> {
const manifest = grabPrecacheManifest();
const needs_page = needsPrecachePage({
method: req.method,
pathname: url.pathname,
fetch_mode: req.headers.get("Sec-Fetch-Mode"),
fetch_dest: req.headers.get("Sec-Fetch-Dest"),
accept: req.headers.get("Accept"),
cookie_build_id: getCookie(req, AppData["BuildCookieName"]),
build_id: manifest?.build_id,
});
if (!needs_page || !manifest) return null;
const file = Bun.file(LOADING_PAGE);
if (!(await file.exists())) return null;
const build = JSON.stringify({
build_id: manifest.build_id,
total: manifest.files.length,
}).replace(/</g, "\\u003c");
const name = escapeHtml({
text: installDisplayName({ install_name: await grabInstallName().catch(() => "") }),
});
const page = (await file.text())
.replace(BuildPlaceholder, build)
.replace(PwaLoadingNamePlaceholder, name);
return new Response(page, {
headers: {
"Content-Type": "text/html; charset=utf-8",
"Cache-Control": "no-store, max-age=0, must-revalidate",
"X-Content-Type-Options": "nosniff",
},
});
}