diff --git a/LICENSE b/LICENSE index 74ca971..20c9bb8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Serverlele30 +Copyright (c) 2025 Aaron K. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/package.json b/package.json new file mode 100644 index 0000000..d9dab1a --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "vbb-netz-status", + "version": "29.0.0", + "description": "Live-Nahverkehr fΓΌr Berlin & Brandenburg - Progressive Web App (PWA) fΓΌr Echtzeit-Abfahrten, Routenplanung und Live-Fahrzeugverfolgung im VBB-Gebiet", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "node server.js" + }, + "keywords": [ + "vbb", + "bvg", + "berlin", + "brandenburg", + "transit", + "public-transport", + "pwa", + "real-time", + "departures", + "route-planner", + "live-map" + ], + "author": "Aaron K. & Claude (Anthropic)", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Serverlele30/VBB-Status-Web-App.git" + }, + "bugs": { + "url": "https://github.com/Serverlele30/VBB-Status-Web-App/issues" + }, + "homepage": "https://github.com/Serverlele30/VBB-Status-Web-App#readme", + "dependencies": { + "mime-types": "^2.1.35" + }, + "devDependencies": {}, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..2ba742c --- /dev/null +++ b/server.js @@ -0,0 +1,116 @@ +const https = require('https'); +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); +const mimeTypes = require('mime-types'); + +const PORT = 3000; +const CERT_DIR = path.join(__dirname, 'certs'); +const CERT_FILE = path.join(CERT_DIR, 'cert.pem'); +const KEY_FILE = path.join(CERT_DIR, 'key.pem'); + +// Create certs directory if it doesn't exist +if (!fs.existsSync(CERT_DIR)) { + fs.mkdirSync(CERT_DIR); +} + +// Generate self-signed certificate if it doesn't exist +if (!fs.existsSync(CERT_FILE) || !fs.existsSync(KEY_FILE)) { + console.log('πŸ” Generating self-signed SSL certificate...'); + try { + execSync(`openssl req -x509 -newkey rsa:4096 -keyout "${KEY_FILE}" -out "${CERT_FILE}" -days 365 -nodes -subj "/CN=localhost"`, { + stdio: 'inherit' + }); + console.log('βœ… Certificate generated successfully!'); + } catch (error) { + console.error('❌ Error generating certificate:', error.message); + console.log('πŸ“ Please install OpenSSL or create certificates manually.'); + process.exit(1); + } +} + +// HTTPS server options +const options = { + key: fs.readFileSync(KEY_FILE), + cert: fs.readFileSync(CERT_FILE) +}; + +// Create HTTPS server +const server = https.createServer(options, (req, res) => { + // Remove query parameters and get clean path + let filePath = req.url.split('?')[0]; + + // Default to index.html for root + if (filePath === '/') { + filePath = '/index.html'; + } + + // Construct full file path + const fullPath = path.join(__dirname, filePath); + + // Security check: prevent directory traversal + const normalizedPath = path.normalize(fullPath); + if (!normalizedPath.startsWith(__dirname)) { + res.writeHead(403); + res.end('Forbidden'); + return; + } + + // Check if file exists + fs.access(fullPath, fs.constants.F_OK, (err) => { + if (err) { + res.writeHead(404); + res.end('File not found'); + return; + } + + // Get MIME type + const mimeType = mimeTypes.lookup(fullPath) || 'application/octet-stream'; + + // Read and serve file + fs.readFile(fullPath, (err, data) => { + if (err) { + res.writeHead(500); + res.end('Error reading file'); + return; + } + + // Set appropriate headers + res.writeHead(200, { + 'Content-Type': mimeType, + 'Cache-Control': 'no-cache' + }); + res.end(data); + }); + }); +}); + +// Start server +server.listen(PORT, () => { + console.log(''); + console.log('πŸš€ VBB Netz Status - HTTPS Development Server'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log(`βœ… Server running at: https://localhost:${PORT}`); + console.log(''); + console.log('πŸ“± To test on mobile:'); + console.log(' 1. Find your local IP: ipconfig (Windows) or ifconfig (Mac/Linux)'); + console.log(' 2. Open https://YOUR_IP:3000 on your mobile device'); + console.log(' 3. Accept the self-signed certificate warning'); + console.log(''); + console.log('⚠️ Note: Your browser will show a security warning.'); + console.log(' This is normal for self-signed certificates in development.'); + console.log(' Click "Advanced" β†’ "Proceed to localhost" to continue.'); + console.log(''); + console.log('πŸ›‘ Press Ctrl+C to stop the server'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log(''); +}); + +// Graceful shutdown +process.on('SIGINT', () => { + console.log('\n\nπŸ‘‹ Shutting down server...'); + server.close(() => { + console.log('βœ… Server stopped'); + process.exit(0); + }); +});