import { serve } from 'bun'; import { join } from 'path'; import { existsSync } from 'fs'; import { handleApiRequest } from './api'; const PORT = parseInt(process.env.PORT || '3456'); const STATIC_DIR = join(import.meta.dir, '../../dist'); export async function startServer() { console.log(`\nšŸš€ Starting worklog server on http://localhost:${PORT}\n`); serve({ port: PORT, async fetch(req) { const url = new URL(req.url); // API routes if (url.pathname.startsWith('/api/')) { return handleApiRequest(req, url); } // Static files return serveStatic(url.pathname); }, error(error) { console.error('Server error:', error); return new Response('Internal Server Error', { status: 500 }); }, }); console.log('Server running. Press Ctrl+C to stop.\n'); } async function serveStatic(pathname: string): Promise { // Map pathname to file let filePath = pathname === '/' ? '/index.html' : pathname; filePath = join(STATIC_DIR, filePath); // Check if file exists if (existsSync(filePath)) { const file = Bun.file(filePath); return new Response(file, { headers: { 'Content-Type': getContentType(filePath), }, }); } // SPA fallback - serve index.html for all routes const indexPath = join(STATIC_DIR, 'index.html'); if (existsSync(indexPath)) { const file = Bun.file(indexPath); return new Response(file, { headers: { 'Content-Type': 'text/html', }, }); } // No static files yet - serve a placeholder return new Response( ` Worklog

Worklog

Frontend not built yet. Run:

bun run build

Or for development:

bun run dev

API is available at /api/stats

`, { headers: { 'Content-Type': 'text/html' }, } ); } function getContentType(filePath: string): string { const ext = filePath.split('.').pop()?.toLowerCase(); const types: Record = { html: 'text/html', css: 'text/css', js: 'application/javascript', json: 'application/json', png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', svg: 'image/svg+xml', ico: 'image/x-icon', woff: 'font/woff', woff2: 'font/woff2', }; return types[ext || ''] || 'application/octet-stream'; }