this repo has no description
at experiment/session-classification 104 lines 2.7 kB view raw
1import { serve } from 'bun'; 2import { join } from 'path'; 3import { existsSync } from 'fs'; 4import { handleApiRequest } from './api'; 5 6const PORT = parseInt(process.env.PORT || '3456'); 7const STATIC_DIR = join(import.meta.dir, '../../dist'); 8 9export async function startServer() { 10 console.log(`\n🚀 Starting worklog server on http://localhost:${PORT}\n`); 11 12 serve({ 13 port: PORT, 14 async fetch(req) { 15 const url = new URL(req.url); 16 17 // API routes 18 if (url.pathname.startsWith('/api/')) { 19 return handleApiRequest(req, url); 20 } 21 22 // Static files 23 return serveStatic(url.pathname); 24 }, 25 error(error) { 26 console.error('Server error:', error); 27 return new Response('Internal Server Error', { status: 500 }); 28 }, 29 }); 30 31 console.log('Server running. Press Ctrl+C to stop.\n'); 32} 33 34async function serveStatic(pathname: string): Promise<Response> { 35 // Map pathname to file 36 let filePath = pathname === '/' ? '/index.html' : pathname; 37 filePath = join(STATIC_DIR, filePath); 38 39 // Check if file exists 40 if (existsSync(filePath)) { 41 const file = Bun.file(filePath); 42 return new Response(file, { 43 headers: { 44 'Content-Type': getContentType(filePath), 45 }, 46 }); 47 } 48 49 // SPA fallback - serve index.html for all routes 50 const indexPath = join(STATIC_DIR, 'index.html'); 51 if (existsSync(indexPath)) { 52 const file = Bun.file(indexPath); 53 return new Response(file, { 54 headers: { 55 'Content-Type': 'text/html', 56 }, 57 }); 58 } 59 60 // No static files yet - serve a placeholder 61 return new Response( 62 `<!DOCTYPE html> 63<html> 64<head> 65 <title>Worklog</title> 66 <style> 67 body { font-family: system-ui; max-width: 600px; margin: 100px auto; padding: 20px; } 68 code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; } 69 </style> 70</head> 71<body> 72 <h1>Worklog</h1> 73 <p>Frontend not built yet. Run:</p> 74 <pre><code>bun run build</code></pre> 75 <p>Or for development:</p> 76 <pre><code>bun run dev</code></pre> 77 <hr> 78 <p>API is available at <a href="/api/stats">/api/stats</a></p> 79</body> 80</html>`, 81 { 82 headers: { 'Content-Type': 'text/html' }, 83 } 84 ); 85} 86 87function getContentType(filePath: string): string { 88 const ext = filePath.split('.').pop()?.toLowerCase(); 89 const types: Record<string, string> = { 90 html: 'text/html', 91 css: 'text/css', 92 js: 'application/javascript', 93 json: 'application/json', 94 png: 'image/png', 95 jpg: 'image/jpeg', 96 jpeg: 'image/jpeg', 97 gif: 'image/gif', 98 svg: 'image/svg+xml', 99 ico: 'image/x-icon', 100 woff: 'font/woff', 101 woff2: 'font/woff2', 102 }; 103 return types[ext || ''] || 'application/octet-stream'; 104}