diff --git a/config_example.json b/config_example.json index e205e48..21ad32c 100644 --- a/config_example.json +++ b/config_example.json @@ -53,6 +53,7 @@ "amoled" ], "theme": "amoled", + "default_font": "", "default_layout": "legacy", "custom_favicon": "/s/img/favicon.gif", "custom_brand_image": [], diff --git a/src/inc/config.mjs b/src/inc/config.mjs index e1b5393..6662acd 100644 --- a/src/inc/config.mjs +++ b/src/inc/config.mjs @@ -30,7 +30,7 @@ const resolvePath = (defaultRel) => { const absStorage = path.resolve(storage); if (defaultRel.startsWith('public/')) { const sub = defaultRel.replace('public/', ''); - if (sub === 's/emojis' || sub === 's/koepfe') { + if (sub === 's/emojis' || sub === 's/koepfe' || sub === 's/fonts') { const storagePath = path.join(absStorage, sub.split('/').pop()); if (fs.existsSync(storagePath)) return path.resolve(storagePath); return local; @@ -51,6 +51,7 @@ config.paths = { s: path.join(base, 'public/s'), emojis: resolvePath('public/s/emojis'), koepfe: resolvePath('public/s/koepfe'), + fonts: resolvePath('public/s/fonts'), memes: resolvePath('public/memes'), e: resolvePath('e'), pending: resolvePath('pending'), diff --git a/src/inc/routes/apiv2/settings.mjs b/src/inc/routes/apiv2/settings.mjs index 1a1445d..390acc2 100644 --- a/src/inc/routes/apiv2/settings.mjs +++ b/src/inc/routes/apiv2/settings.mjs @@ -486,7 +486,7 @@ export default router => { // F-023 Security: Validate font against actual files on disk // The font value is rendered into CSS url() in header.html, so it must be a real filename if (font) { - const fontsDir = path.join(path.resolve(), 'public/s/fonts'); + const fontsDir = cfg.paths.fonts; try { const available = (await fs.readdir(fontsDir)).filter(f => /\.(ttf|otf|woff2?)$/i.test(f)); if (!available.includes(font)) { diff --git a/src/inc/routes/static.mjs b/src/inc/routes/static.mjs index 510ee6a..b5d2a2a 100644 --- a/src/inc/routes/static.mjs +++ b/src/inc/routes/static.mjs @@ -23,6 +23,11 @@ export default (router, tpl) => { route: /^\/s\/koepfe\// }); + router.static({ + dir: cfg.paths.fonts, + route: /^\/s\/fonts\// + }); + router.static({ dir: path.join(path.resolve(), 'node_modules/@ruffle-rs/ruffle'), route: /^\/s\/ruffle\// diff --git a/src/index.mjs b/src/index.mjs index 4777840..6e05b89 100644 --- a/src/index.mjs +++ b/src/index.mjs @@ -1248,6 +1248,7 @@ process.on('uncaughtException', err => { custom_favicon: cfg.websrv.custom_favicon || "", custom_brand_image: Array.isArray(cfg.websrv.custom_brand_image) ? cfg.websrv.custom_brand_image[0] : (cfg.websrv.custom_brand_image || ""), custom_navbar_brand_text: cfg.websrv.custom_navbar_brand_text || "", + default_font: cfg.websrv.default_font || "", site_description: cfg.websrv.description || "The webs dumpster", enable_nsfl: !!cfg.enable_nsfl, nsfl_tag_id: cfg.nsfl_tag_id || 3, @@ -1303,7 +1304,7 @@ process.on('uncaughtException', err => { get fonts() { try { - const fontsDir = path.join(path.resolve(), 'public/s/fonts'); + const fontsDir = cfg.paths.fonts; if (!fs.existsSync(fontsDir)) return []; return fs.readdirSync(fontsDir).filter(f => /\.(ttf|otf|woff2?)$/i.test(f)).map(f => ({ name: f.split('.').shift(), diff --git a/views/snippets/header.html b/views/snippets/header.html index 8249ae6..14ea890 100644 --- a/views/snippets/header.html +++ b/views/snippets/header.html @@ -19,6 +19,8 @@ :root { @if(session && session.font) --font: 'CustomUserFont', monospace !important; + @elseif(typeof default_font !== 'undefined' && default_font && default_font.length > 0) + --font: {{ default_font }} !important; @endif } @if(session && session.font) @@ -30,6 +32,15 @@ .fas::before, .far::before, .fab::before, .fa::before { font-family: "Font Awesome 6 Free", "Font Awesome 6 Brands" !important; } + @elseif(typeof default_font !== 'undefined' && default_font && default_font.length > 0) + *:not(canvas):not(.meme-title):not(.force-impact):not(.fa-solid):not(.fa-regular):not(.fa-brands):not(.fa-light):not(.fa-thin):not(.fa-duotone):not([class*=" fa-"]):not([class^="fa-"]) { + font-family: {{ default_font }} !important; + } + /* Explicitly protect FA pseudo-elements */ + .fa-solid::before, .fa-regular::before, .fa-brands::before, + .fas::before, .far::before, .fab::before, .fa::before { + font-family: "Font Awesome 6 Free", "Font Awesome 6 Brands" !important; + } @endif