54 lines
1.8 KiB
TypeScript
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;
|
|
}
|