From dee86aaa863c75f92cf7e49b9d2da8477e5a4e97 Mon Sep 17 00:00:00 2001 From: diced Date: Tue, 10 Mar 2026 21:37:26 -0700 Subject: [PATCH] fix: once and for all fix #907 --- src/server/index.ts | 13 ---------- src/server/routes/favicon.ts | 47 ++++++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/server/index.ts b/src/server/index.ts index a309c9d2..70fa2b6c 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -86,19 +86,6 @@ async function main() { trustProxy: config.core.trustProxy, }).withTypeProvider(); - if (process.env.DEBUG_EVENT_EMITTER) { - server.addHook('onSend', async (req, res) => { - const counts = { - listeners: res.raw.eventNames(), - close: res.raw.listenerCount('close'), - data: res.raw.listenerCount('data'), - end: res.raw.listenerCount('end'), - error: res.raw.listenerCount('error'), - }; - - logger.debug('event emitter counts', { path: req.url, ...counts }); - }); - } server.setValidatorCompiler(validatorCompiler); server.setSerializerCompiler(serializerCompiler); diff --git a/src/server/routes/favicon.ts b/src/server/routes/favicon.ts index 8109c5ae..e9840a0c 100644 --- a/src/server/routes/favicon.ts +++ b/src/server/routes/favicon.ts @@ -1,25 +1,46 @@ import { config } from '@/lib/config'; +import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; import typedPlugin from '../typedPlugin'; +import { sanitizeFilename } from '@/lib/fs'; export const FAVICON_SIZES = [16, 32, 64, 128, 512]; +export const PUBLIC_DIR = join(process.cwd(), 'public'); -export const PATH = '/favicon.ico'; +function loadFavicon(file: string): Buffer | null { + const path = join(PUBLIC_DIR, file); + if (!existsSync(path)) return null; + + return readFileSync(path); +} + +const FAVICONS: Record = { + 'favicon.ico': loadFavicon('favicon.ico'), + ...Object.fromEntries( + FAVICON_SIZES.map((size) => { + const name = `favicon-${size}x${size}.png`; + return [name, loadFavicon(name)]; + }), + ), +}; + +export const PATH = '/favicon*'; export default typedPlugin( async (server) => { - server.get(PATH, (_, res) => { - return res.sendFile('favicon.ico', join(process.cwd(), 'public')); + server.get(PATH, (req, res) => { + const filename = sanitizeFilename(req.url.replace('/', '')); + if (!filename) return res.callNotFound(); + + const buffer = FAVICONS[filename]; + + if (!buffer) return res.callNotFound(); + if (filename.startsWith('favicon-') && !config.pwa.enabled) return res.callNotFound(); + + return res + .type(filename.endsWith('.ico') ? 'image/x-icon' : 'image/png') + .header('Cache-Control', 'public, max-age=86400') + .send(buffer); }); - - // different sizes of favicon for PWA, if they exist then serve them - for (const size of FAVICON_SIZES) { - const str = `${size}x${size}`; - server.get(`/favicon-${str}.png`, async (_, res) => { - if (!config.pwa.enabled) return res.callNotFound(); - - return res.sendFile(`favicon-${str}.png`, join(process.cwd(), 'public')); - }); - } }, { name: PATH }, );