const express = require('express'); const { spawn } = require('child_process'); const { MongoClient } = require('mongodb'); const path = require('path'); const app = express(); const http = require('http').createServer(app); const io = require('socket.io')(http, { cors: { origin: "*", methods: ["GET", "POST"] } }); const crypto = require('crypto'); const fs = require('fs'); // MongoDB Connection const MONGO_URI = process.env.MONGO_URI || "mongodb://localhost:27017"; let db; async function connectDB() { try { const client = new MongoClient(MONGO_URI); await client.connect(); db = client.db('whatsapp-bots'); console.log("✅ Connected to MongoDB"); // Initialize collections const collections = ['users', 'bots', 'sessions']; for (const colName of collections) { if (!(await db.listCollections({ name: colName }).hasNext())) { await db.createCollection(colName); console.log(`Created collection: ${colName}`); } } // Create admin user if not exists const adminExists = await db.collection('users').findOne({ username: 'admin' }); if (!adminExists) { await db.collection('users').insertOne({ username: 'admin', password: hashPassword('admin123'), isAdmin: true, createdAt: new Date() }); console.log("👑 Created admin user (password: admin123)"); } } catch (err) { console.error("❌ MongoDB connection error:", err); process.exit(1); } } function hashPassword(password) { return crypto .createHash('sha256') .update(password + (process.env.PEPPER || 'defaultPepper')) .digest('hex'); } // Socket.IO io.on('connection', (socket) => { console.log(`New connection: ${socket.id}`); // Force terminal initialization socket.emit('terminal-init', { status: 'ready', timestamp: Date.now() }); // Terminal command handler socket.on('terminal-command', async (data) => { try { const { command, userId } = data; console.log(`Command from ${userId}: ${command}`); // Execute command in user's directory const userDir = `/persistent/storage/${userId}`; const child = spawn(command.split(' ')[0], command.split(' ').slice(1), { cwd: userDir }); child.stdout.on('data', (data) => { socket.emit('terminal-output', data.toString()); }); child.stderr.on('data', (data) => { socket.emit('terminal-output', `ERROR: ${data.toString()}`); }); child.on('close', (code) => { socket.emit('terminal-output', `Process exited with code ${code}\n`); }); } catch (err) { socket.emit('terminal-output', `ERROR: ${err.message}\n`); } }); // Authentication handlers socket.on('login', async (data) => { try { const { username, password } = data; const user = await db.collection('users').findOne({ username, password: hashPassword(password) }); if (user) { currentUser = user._id.toString(); socket.emit('login-success', { userId: currentUser, isAdmin: user.isAdmin }); // Ensure user directory exists const userDir = `/persistent/storage/${currentUser}`; if (!fs.existsSync(userDir)) { fs.mkdirSync(userDir, { recursive: true }); } } else { socket.emit('login-error', 'Invalid credentials'); } } catch (err) { socket.emit('login-error', 'Authentication failed'); } }); }); // Express middleware app.use(express.static('public')); app.use(express.json()); // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'healthy', timestamp: Date.now() }); }); // Error handling app.use((err, req, res, next) => { console.error('Global error:', err); io.emit('terminal-output', `SYSTEM ERROR: ${err.message}\n`); res.status(500).json({ error: err.message }); }); // Start server const PORT = process.env.PORT || 7860; connectDB().then(() => { http.listen(PORT, () => { console.log(`🚀 Server running on port ${PORT}`); // Restart any previously running bots db.collection('bots').find({ status: 'running' }).forEach(bot => { console.log(`Restarting bot for user ${bot.userId}`); startBotProcess(bot.userId, bot.repoUrl, bot.entryFile); }); }); }); // Helper function to start bot processes function startBotProcess(userId, repoUrl, entryFile) { const botDir = `/persistent/storage/${userId}`; // Clone repo if needed if (!fs.existsSync(botDir)) { const clone = spawn('git', ['clone', repoUrl, botDir]); clone.on('close', (code) => { if (code === 0) installDependencies(botDir, userId, repoUrl, entryFile); }); } else { installDependencies(botDir, userId, repoUrl, entryFile); } } function installDependencies(botDir, userId, repoUrl, entryFile) { const install = spawn('npm', ['install'], { cwd: botDir }); install.on('close', (code) => { if (code === 0) runBot(botDir, userId, repoUrl, entryFile); }); } function runBot(botDir, userId, repoUrl, entryFile) { const botProcess = spawn('node', [entryFile], { cwd: botDir }); // Save to database db.collection('bots').updateOne( { userId }, { $set: { status: 'running', repoUrl, entryFile, pid: botProcess.pid, lastStarted: new Date() }}, { upsert: true } ); // Handle process events botProcess.on('exit', (code) => { db.collection('bots').updateOne( { userId }, { $set: { status: 'stopped', exitCode: code } } ); }); }