/** * Development Server - Simple HTTP server for local development * Handles static files, CORS, and development features */ import { createServer } from 'http'; import { readFile, stat } from 'fs/promises'; import { join, extname } from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const PORT = process.env.PORT || 3000; const HOST = process.env.HOST || 'localhost'; // MIME types for different file extensions const MIME_TYPES = { '.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', '.ttf': 'font/ttf', '.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.mp4': 'video/mp4' }; const server = createServer(async (req, res) => { try { // Parse URL and remove query parameters const urlPath = new URL(req.url, `http://${req.headers.host}`).pathname; // Default to index.html for root requests const filePath = urlPath === '/' ? 'index.html' : urlPath.slice(1); const fullPath = join(__dirname, filePath); console.log(`${new Date().toISOString()} - ${req.method} ${urlPath}`); // Set CORS headers for all requests res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); // Handle preflight requests if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } // Check if file exists try { const stats = await stat(fullPath); if (stats.isDirectory()) { // Try to serve index.html from directory const indexPath = join(fullPath, 'index.html'); try { await stat(indexPath); return serveFile(indexPath, res); } catch { return send404(res, `Directory listing not allowed for ${urlPath}`); } } return serveFile(fullPath, res); } catch (error) { if (error.code === 'ENOENT') { return send404(res, `File not found: ${urlPath}`); } throw error; } } catch (error) { console.error('Server error:', error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Internal Server Error'); } }); async function serveFile(filePath, res) { try { const ext = extname(filePath).toLowerCase(); const mimeType = MIME_TYPES[ext] || 'application/octet-stream'; // Set content type res.setHeader('Content-Type', mimeType); // Set cache headers for static assets if (['.css', '.js', '.png', '.jpg', '.gif', '.svg', '.ico', '.woff', '.woff2'].includes(ext)) { res.setHeader('Cache-Control', 'public, max-age=3600'); // 1 hour } else { res.setHeader('Cache-Control', 'no-cache'); // No cache for HTML and other files } // Add security headers if (ext === '.js') { res.setHeader('X-Content-Type-Options', 'nosniff'); } // Read and serve file const content = await readFile(filePath); res.writeHead(200); res.end(content); console.log(` āœ… Served ${filePath} (${content.length} bytes, ${mimeType})`); } catch (error) { console.error(`Error serving file ${filePath}:`, error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error reading file'); } } function send404(res, message = 'Not Found') { const html404 = ` 404 - Not Found

404

${message}

← Back to Class Generator

`; res.writeHead(404, { 'Content-Type': 'text/html' }); res.end(html404); console.log(` āŒ 404: ${message}`); } // Start server server.listen(PORT, HOST, () => { console.log('\nšŸš€ Class Generator Development Server'); console.log('====================================='); console.log(`šŸ“ Local: http://${HOST}:${PORT}/`); console.log(`🌐 Network: http://localhost:${PORT}/`); console.log('šŸ“ Serving files from:', __dirname); console.log('\n✨ Features:'); console.log(' • ES6 modules support'); console.log(' • CORS enabled'); console.log(' • Static file serving'); console.log(' • Development-friendly caching'); console.log('\nšŸ”„ Ready for development!'); console.log('Press Ctrl+C to stop\n'); }); // Graceful shutdown process.on('SIGINT', () => { console.log('\nšŸ‘‹ Shutting down server...'); server.close(() => { console.log('āœ… Server stopped'); process.exit(0); }); }); process.on('SIGTERM', () => { console.log('\nšŸ‘‹ Received SIGTERM, shutting down...'); server.close(() => { console.log('āœ… Server stopped'); process.exit(0); }); });