import { env } from "$env/dynamic/private"; const cleanUrl = (value: string | undefined, fallback: string) => { const raw = value?.trim() || fallback; return raw.replace(/\/+$/, ""); }; export type WebConfig = { bobbinUrl: string; knotMirrorUrl: string; apiUrl: string; /** the domain user sites are served under, e.g. "tngl.io" */ sitesDomain: string; camoUrl: string; avatarUrl: string; /** the secrets camo and avatar sign with, so neither leaves the server */ camoSecret: string; avatarSecret: string; }; export type PublicWebConfig = Pick< WebConfig, "bobbinUrl" | "knotMirrorUrl" | "apiUrl" | "sitesDomain" > & { /** camo has a secret, so markup can route images through it */ camoEnabled: boolean; }; type WebConfigEnv = { BOBBIN_URL?: string; KNOTMIRROR_URL?: string; TANGLED_API_URL?: string; API_URL?: string; KNOT_RESOLVER_URL?: string; SITES_DOMAIN?: string; CAMO_URL?: string; CAMO_SHARED_SECRET?: string; AVATAR_URL?: string; AVATAR_SHARED_SECRET?: string; }; export const resolveConfig = (values: WebConfigEnv): WebConfig => ({ bobbinUrl: cleanUrl(values.BOBBIN_URL, "http://127.0.0.1:8090"), knotMirrorUrl: cleanUrl(values.KNOTMIRROR_URL, ""), apiUrl: cleanUrl(values.TANGLED_API_URL ?? values.API_URL, "http://127.0.0.1:8080"), sitesDomain: values.SITES_DOMAIN?.trim() || "tngl.io", camoUrl: cleanUrl(values.CAMO_URL, "https://camo.tangled.sh"), avatarUrl: cleanUrl(values.AVATAR_URL, "https://avatar.tangled.sh"), camoSecret: values.CAMO_SHARED_SECRET?.trim() ?? "", avatarSecret: values.AVATAR_SHARED_SECRET?.trim() ?? "" }); export const getConfig = (): WebConfig => resolveConfig(env as WebConfigEnv); // listed one by one, so anything new in WebConfig stays private until it is // named here export const getPublicConfig = (): PublicWebConfig => { const config = getConfig(); return { bobbinUrl: config.bobbinUrl, knotMirrorUrl: config.knotMirrorUrl, apiUrl: config.apiUrl, sitesDomain: config.sitesDomain, camoEnabled: config.camoSecret !== "" }; };