Files
gerbeur/api/lib/index-html.ts

54 lines
1.8 KiB
TypeScript

/**
* Fills the placeholders of the frontend's `index.html` with settings read at
* runtime, so they change with a restart and no rebuild. The static and OG
* middlewares serve what it returns.
*
* @module
*/
import {
BANDCAMP_PLAYER,
DEFAULT_STYLE,
EMAIL_ENABLED,
FEDERATION,
OG_SITE_NAME,
SITE_EMOJI,
} from "../config.ts";
import { emojiToCodepoint } from "./site-icons.ts";
/**
* Versions the icon URLs, so browsers fetch new icons when the emoji changes.
*/
const ICON_VERSION = emojiToCodepoint(SITE_EMOJI);
/**
* Caches each root's filled file until its mtime changes, as a rebuild under a
* running server does, so a navigation costs a stat instead of a read.
*/
const indexHtml = new Map<string, { mtime: number; html: string }>();
/**
* Returns `${root}/index.html` with the site name, emoji, icon version,
* Bandcamp player mode, default style, and whether email and each way of
* federated sign-in are on filled in.
*
* @throws {Error} When the file can't be read.
*/
export async function loadIndexHtml(root: string): Promise<string> {
const path = `${root}/index.html`;
const mtime = (await Deno.stat(path)).mtime?.getTime() ?? 0;
const cached = indexHtml.get(root);
if (cached && cached.mtime === mtime) return cached.html;
const html = (await Deno.readTextFile(path))
.replaceAll("__SITE_NAME__", OG_SITE_NAME)
.replaceAll("__SITE_EMOJI__", SITE_EMOJI)
.replaceAll("__ICON_VERSION__", ICON_VERSION)
.replaceAll("__BANDCAMP_PLAYER__", BANDCAMP_PLAYER)
.replaceAll("__DEFAULT_STYLE__", DEFAULT_STYLE)
.replaceAll("__EMAIL_ENABLED__", EMAIL_ENABLED ? "on" : "off")
.replaceAll("__FEDERATION_INCOMING__", FEDERATION.incoming ? "on" : "off")
.replaceAll("__FEDERATION_OUTGOING__", FEDERATION.outgoing ? "on" : "off");
indexHtml.set(root, { mtime, html });
return html;
}