diff --git a/create-a-container/server.js b/create-a-container/server.js index 5b97cc62..ad37bf78 100644 --- a/create-a-container/server.js +++ b/create-a-container/server.js @@ -1,29 +1,47 @@ +require('dotenv').config(); + const express = require('express'); const bodyParser = require('body-parser'); const session = require('express-session'); const { spawn, exec } = require('child_process'); const path = require('path'); const crypto = require('crypto'); -const fs = require('fs'); // Added fs module +const fs = require('fs'); +const rateLimit = require('express-rate-limit'); // <-- ADDED const app = express(); +app.use(express.json()); + +app.set('trust proxy', 1); -// A simple in-memory object to store job status and output const jobs = {}; // --- Middleware Setup --- -app.use(bodyParser.urlencoded({ extended: true })); -app.use(express.json()); -app.use(express.static('public')); +if (!process.env.SESSION_SECRET) { + throw new Error("SESSION_SECRET is not set in environment!"); +} + app.use(session({ - secret: 'A7d#9Lm!qW2z%Xf8@Rj3&bK6^Yp$0Nc', - resave: false, - saveUninitialized: true, - cookie: { secure: false } // Set to true if using HTTPS + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: true, + cookie: { secure: true } })); +app.use(express.static('public')); + +// --- Rate Limiter for Login --- +const loginLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 5, // limit each IP to 5 requests per windowMs + message: { error: "Too many login attempts. Please try again later." } +}); + // --- Route Handlers --- +const PORT = 3000; +app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`)); + // Serves the main container creation form, protected by login app.get('/form.html', (req, res) => { if (!req.session.user) { @@ -32,17 +50,30 @@ app.get('/form.html', (req, res) => { res.sendFile(path.join(__dirname, 'views', 'form.html')); }); -// Handles user login -app.post('/login', (req, res) => { +// Handles user login with rate limiting +app.post('/login', loginLimiter, (req, res) => { const { username, password } = req.body; - exec(`node /root/bin/js/runner.js authenticateUser ${username} ${password}`, (err, stdout) => { - if (err) { - console.error("Login script execution error:", err); + const runner = spawn('node', ['/root/bin/js/runner.js', 'authenticateUser', username, password]); + + let stdoutData = ''; + let stderrData = ''; + + runner.stdout.on('data', (data) => { + stdoutData += data.toString(); + }); + + runner.stderr.on('data', (data) => { + stderrData += data.toString(); + }); + + runner.on('close', (code) => { + if (code !== 0) { + console.error("Login script execution error:", stderrData); return res.status(500).json({ error: "Server error during authentication." }); } - if (stdout.trim() === 'true') { + if (stdoutData.trim() === 'true') { req.session.user = username; req.session.proxmoxUsername = username; req.session.proxmoxPassword = password; @@ -53,15 +84,12 @@ app.post('/login', (req, res) => { }); }); -// ✨ UPDATED: API endpoint to get user's containers +// API endpoint to get user's containers app.get('/api/my-containers', (req, res) => { if (!req.session.user) { return res.status(401).json({ error: "Unauthorized" }); } - // The username in port_map.json doesn't have the @pve suffix const username = req.session.user.split('@')[0]; - - // Command to read the remote JSON file const command = "ssh root@10.15.20.69 'cat /etc/nginx/port_map.json'"; exec(command, (err, stdout, stderr) => { @@ -72,7 +100,6 @@ app.get('/api/my-containers', (req, res) => { try { const portMap = JSON.parse(stdout); const userContainers = Object.entries(portMap) - // This check now ensures 'details' exists and has a 'user' property before comparing .filter(([_, details]) => details && details.user === username) .map(([name, details]) => ({ name, ...details })); @@ -155,8 +182,4 @@ app.get('/api/stream/:jobId', (req, res) => { clearInterval(intervalId); res.end(); }); -}); - -// --- Server Initialization --- -const PORT = 3000; -app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`)); \ No newline at end of file +}); \ No newline at end of file