Class_generator/server.js
StillHammer 38920cc858 Complete architectural rewrite with ultra-modular system
Major Changes:
- Moved legacy system to Legacy/ folder for archival
- Built new modular architecture with strict separation of concerns
- Created core system: Module, EventBus, ModuleLoader, Router
- Added Application bootstrap with auto-start functionality
- Implemented development server with ES6 modules support
- Created comprehensive documentation and project context
- Converted SBS-7-8 content to JSON format
- Copied all legacy games and content to new structure

New Architecture Features:
- Sealed modules with WeakMap private data
- Strict dependency injection system
- Event-driven communication only
- Inviolable responsibility patterns
- Auto-initialization without commands
- Component-based UI foundation ready

Technical Stack:
- Vanilla JS/HTML/CSS only
- ES6 modules with proper imports/exports
- HTTP development server (no file:// protocol)
- Modular CSS with component scoping
- Comprehensive error handling and debugging

Ready for Phase 2: Converting legacy modules to new architecture

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-22 07:08:39 +08:00

197 lines
6.1 KiB
JavaScript

/**
* 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 = `
<!DOCTYPE html>
<html>
<head>
<title>404 - Not Found</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
}
h1 { font-size: 3rem; margin-bottom: 1rem; }
p { font-size: 1.2rem; opacity: 0.8; }
a { color: white; text-decoration: underline; }
</style>
</head>
<body>
<h1>404</h1>
<p>${message}</p>
<p><a href="/">← Back to Class Generator</a></p>
</body>
</html>
`;
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);
});
});