openvibers/openvibe.games Commits

/shared serves a stylesheet as text/css: the home page's "Your stuff across OpenVibe" panel drew unstyled because its CSS went out as JavaScript (#156)

OP OpenVibers committed 17 days ago · f5b7ab629e · 2 files, +24 −1

Changes

ddiff --git a/apps/server/src/net/sharedAssets.test.ts b/apps/server/src/net/sharedAssets.test.ts
iindex c765083..2a8f992 100644
---- a/apps/server/src/net/sharedAssets.test.ts
++++ b/apps/server/src/net/sharedAssets.test.ts
@@@ -40,6 +40,14 @@ describe('/shared (D42)', () => {
expect(r.status).toBe(304)
})
++ it('serves a stylesheet as text/css (the profile panel drew unstyled when its CSS went out as JavaScript)', async () => {
++ const r = await fetch(`${base}/shared/profile-panel.css`)
++ expect(r.status).toBe(200)
++ expect(r.headers.get('content-type')).toMatch(/^text\/css/)
++ expect(r.headers.get('x-content-type-options')).toBe('nosniff')
++ expect((await fetch(`${base}/shared/profile-panel.js`)).headers.get('content-type')).toMatch(/javascript/)
++ })
++
it('serves the web app manifest and touch icon the shared page shell links (/steam, /updates, world pages)', async () => {
const shell = req('openvibe-shared/serve') as { manifestUrl(s: string): string | null }
const url = shell.manifestUrl('games')
ddiff --git a/apps/server/src/net/sharedAssets.ts b/apps/server/src/net/sharedAssets.ts
iindex d032cda..700ca3e 100644
---- a/apps/server/src/net/sharedAssets.ts
++++ b/apps/server/src/net/sharedAssets.ts
@@@ -19,6 +19,21 @@ interface SharedServe {
handler(): (req: IncomingMessage & { path: string }, res: ServerResponse, next: (err?: unknown) => void) => void
}
++/**
++ * The file's type, as openvibe-shared/serve gives it: a stylesheet must be text/css, or the browser refuses it under
++ * nosniff (the profile panel's stylesheet went out as JavaScript and its panel drew unstyled on the home page,
++ * 2026-10-27).
++ */
++export function contentTypeOf(name: string): string {
++ if (name.endsWith('.css')) return 'text/css; charset=utf-8'
++ if (name.endsWith('.json')) return 'application/json; charset=utf-8'
++ if (name.endsWith('.svg')) return 'image/svg+xml'
++ if (name.endsWith('.png')) return 'image/png'
++ if (name.endsWith('.jpg') || name.endsWith('.jpeg')) return 'image/jpeg'
++ if (name.endsWith('.webp')) return 'image/webp'
++ return 'application/javascript; charset=utf-8'
++}
++
export function sharedAssetsHandler(req: NodeRequire = createRequire(import.meta.url)) {
const files = req('openvibe-shared/files') as SharedFiles
const serve = req('openvibe-shared/serve') as SharedServe
@@@ -40,7 +55,7 @@ export function sharedAssetsHandler(req: NodeRequire = createRequire(import.meta
}
const hash = serve.hashOf(name)
const headers = {
-- 'content-type': 'application/javascript; charset=utf-8',
++ 'content-type': contentTypeOf(name),
'cache-control':
url.searchParams.get('v') === hash
? 'public, max-age=31536000, immutable'