← back to Billy Website

server.js

113 lines

const http = require('http');
const fs = require('fs');
const path = require('path');

const PORT = process.env.PORT || 9100;

// MIME types for different file extensions
const mimeTypes = {
    '.html': 'text/html',
    '.css': 'text/css',
    '.js': 'text/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',
    '.eot': 'application/vnd.ms-fontobject'
};

const server = http.createServer((req, res) => {
    console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);

    // Default to index.html for root path
    let filePath = req.url === '/' ? '/index.html' : req.url;

    // Remove query string if present
    filePath = filePath.split('?')[0];

    // Decode percent-encoding so encoded traversal sequences are caught
    try {
        filePath = decodeURIComponent(filePath);
    } catch (e) {
        res.writeHead(400, { 'Content-Type': 'text/html' });
        res.end('<h1>400 - Bad Request</h1>', 'utf-8');
        return;
    }

    // Construct full file path and confirm it stays inside the web root
    filePath = path.join(__dirname, path.normalize(filePath));
    if (filePath !== __dirname && !filePath.startsWith(__dirname + path.sep)) {
        res.writeHead(403, { 'Content-Type': 'text/html' });
        res.end('<h1>403 - Forbidden</h1>', 'utf-8');
        return;
    }

    // Get file extension
    const extname = String(path.extname(filePath)).toLowerCase();
    const contentType = mimeTypes[extname] || 'application/octet-stream';

    // Read and serve the file
    fs.readFile(filePath, (error, content) => {
        if (error) {
            if (error.code === 'ENOENT') {
                // File not found
                res.writeHead(404, { 'Content-Type': 'text/html' });
                res.end('<h1>404 - File Not Found</h1>', 'utf-8');
            } else {
                // Server error
                res.writeHead(500);
                res.end(`Server Error: ${error.code}`, 'utf-8');
            }
        } else {
            // Success
            res.writeHead(200, { 'Content-Type': contentType });
            res.end(content, 'utf-8');
        }
    });
});

server.listen(PORT, '0.0.0.0', () => {
    console.log('='.repeat(60));
    console.log('🏖️  Bill Fox San Diego Rental - 3306 Lloyd Street');
    console.log('='.repeat(60));
    console.log(`🚀 Server running at http://0.0.0.0:${PORT}/`);
    console.log(`📅 Started: ${new Date().toLocaleString()}`);
    console.log('='.repeat(60));
    console.log('\nPress Ctrl+C to stop the server\n');
});

// Handle server errors
server.on('error', (error) => {
    if (error.code === 'EADDRINUSE') {
        console.error(`\n❌ Error: Port ${PORT} is already in use.`);
        console.error(`Please close the other application or choose a different port.\n`);
        process.exit(1);
    } else {
        console.error(`\n❌ Server Error: ${error.message}\n`);
        process.exit(1);
    }
});

// Graceful shutdown
process.on('SIGTERM', () => {
    console.log('\n\n👋 Shutting down gracefully...');
    server.close(() => {
        console.log('✅ Server closed successfully\n');
        process.exit(0);
    });
});

process.on('SIGINT', () => {
    console.log('\n\n👋 Shutting down gracefully...');
    server.close(() => {
        console.log('✅ Server closed successfully\n');
        process.exit(0);
    });
});