fix(content-length): remove Bun.file, use c.html(plain text) for dynamic routes

Bun.file() returns Response without Content-Length, causing 12s Nginx
wait and console warnings. serveStatic({ root }) is correct for
/admin/*.html. c.html(await Bun.file(...).text()) is correct for
SPA fallback routes where path != filename.

Refs: production server
This commit is contained in:
APAW Agent Sync
2026-05-15 21:19:59 +01:00
parent bbe9a42691
commit ae01d42191

View File

@@ -1794,29 +1794,27 @@ const adminHtmlAuthDisabled = async (c: any, next: any) => {
}
// Serve static files and SPA routes (clean URLs without .html)
// Admin component files - using Bun.file() directly to avoid serveStatic content-length bug
app.get('/admin/sidebar.html', async (c) => new Response(Bun.file('./public/admin/sidebar.html')))
app.get('/admin/topbar.html', async (c) => new Response(Bun.file('./public/admin/topbar.html')))
app.get('/admin/dashboard.html', async (c) => new Response(Bun.file('./public/admin/dashboard.html')))
app.get('/admin/properties.html', async (c) => new Response(Bun.file('./public/admin/properties.html')))
app.get('/admin/leads.html', async (c) => new Response(Bun.file('./public/admin/leads.html')))
app.get('/admin/testimonials.html', async (c) => new Response(Bun.file('./public/admin/testimonials.html')))
app.get('/admin/faq.html', async (c) => new Response(Bun.file('./public/admin/faq.html')))
app.get('/admin/services.html', async (c) => new Response(Bun.file('./public/admin/services.html')))
app.get('/admin/settings.html', async (c) => new Response(Bun.file('./public/admin/settings.html')))
app.get('/admin/users.html', async (c) => new Response(Bun.file('./public/admin/users.html')))
app.get('/admin/analytics.html', async (c) => new Response(Bun.file('./public/admin/analytics.html')))
app.get('/admin/traffic.html', async (c) => new Response(Bun.file('./public/admin/traffic.html')))
// Static assets (URL path matches file path under ./public)
app.get('/css/*', serveStatic({ root: './public' }))
app.get('/js/*', serveStatic({ root: './public' }))
app.get('/images/*', serveStatic({ root: './public' }))
app.get('/uploads/*', serveStatic({ root: './public' }))
app.get('/src/i18n/*', serveStatic({ root: '.' }))
// SPA routes - using Bun.file() for static files to avoid content-length bugs
app.get('/property/*', async (c) => new Response(Bun.file('./public/property.html')))
app.get('/catalog', async (c) => new Response(Bun.file('./public/catalog.html')))
app.get('/catalog.html', async (c) => new Response(Bun.file('./public/catalog.html')))
app.get('/admin', async (c) => new Response(Bun.file('./public/admin.html')))
app.get('/login', async (c) => new Response(Bun.file('./public/login.html')))
// Admin HTML components - all served under /admin/* from ./public/admin/
app.get('/admin/*', serveStatic({ root: './public' }))
// Static HTML pages where URL path directly matches filename
app.get('/catalog.html', serveStatic({ root: './public' }))
// SPA fallback routes (dynamic paths) - use c.html to ensure correct Content-Length
app.get('/property/*', async (c) => c.html(await Bun.file('./public/property.html').text()))
app.get('/catalog', async (c) => c.html(await Bun.file('./public/catalog.html').text()))
app.get('/admin', async (c) => c.html(await Bun.file('./public/admin.html').text()))
app.get('/login', async (c) => c.html(await Bun.file('./public/login.html').text()))
// Fallback to index.html for all other routes
app.get('*', async (c) => new Response(Bun.file('./public/index.html')))
app.get('*', async (c) => c.html(await Bun.file('./public/index.html').text()))
// Start server
const port = parseInt(process.env.PORT || '8080')