const express = require('express'); const pg = require('pg'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const socketIo = require('socket.io'); const http = require('http'); const cors = require('cors'); const bodyParser = require('body-parser'); const dotenv = require('dotenv'); dotenv.config(); const app = express(); const server = http.createServer(app); const io = socketIo(server); app.use(cors()); app.use(bodyParser.json()); app.use(express.static('public')); const pool = new pg.Pool({ user: process.env.POSTGRES_USER, host: process.env.POSTGRES_HOST, database: process.env.POSTGRES_DB, password: process.env.POSTGRES_PASSWORD, port: 5432, }); // Auth middleware const authenticate = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) return res.status(401).json({ error: 'Unauthorized' }); try { const decoded = jwt.verify(token, process.env.SECRET_KEY); req.user = decoded; next(); } catch (err) { res.status(401).json({ error: 'Invalid token' }); } }; const isAdmin = (req, res, next) => { if (req.user.role !== 'admin') return res.status(403).json({ error: 'Forbidden' }); next(); }; // Routes: Auth app.post('/api/signup', async (req, res) => { const { username, password, email, promoCode } = req.body; const hashedPw = await bcrypt.hash(password, 10); try { const result = await pool.query('INSERT INTO users (username, password, email) VALUES ($1, $2, $3) RETURNING id', [username, hashedPw, email]); let bonus = 0; if (promoCode) { const promo = await pool.query('SELECT * FROM promo_codes WHERE code = $1 AND used = FALSE', [promoCode]); if (promo.rows[0]) { bonus = promo.rows[0].value; await pool.query('UPDATE promo_codes SET used = TRUE, user_id = $1 WHERE id = $2', [result.rows[0].id, promo.rows[0].id]); await pool.query('UPDATE users SET balance = balance + $1 WHERE id = $2', [bonus, result.rows[0].id]); } } res.json({ message: 'User created', bonus }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/login', async (req, res) => { const { username, password } = req.body; try { const user = await pool.query('SELECT * FROM users WHERE username = $1', [username]); if (!user.rows[0] || !(await bcrypt.compare(password, user.rows[0].password))) { return res.status(401).json({ error: 'Invalid credentials' }); } const token = jwt.sign({ id: user.rows[0].id, role: user.rows[0].role }, process.env.SECRET_KEY, { expiresIn: '1h' }); res.json({ token, balance: user.rows[0].balance }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Games API (example for bet/result) app.post('/api/bet', authenticate, async (req, res) => { const { gameId, amount } = req.body; try { const game = await pool.query('SELECT * FROM games WHERE id = $1 AND enabled = TRUE', [gameId]); if (!game.rows[0] || amount < game.rows[0].min_bet || amount > game.rows[0].max_bet) { return res.status(400).json({ error: 'Invalid bet' }); } const user = await pool.query('SELECT balance FROM users WHERE id = $1', [req.user.id]); if (user.rows[0].balance < amount) return res.status(400).json({ error: 'Insufficient balance' }); // Simulate result (fake, based on win_probability) const win = Math.random() * 100 < game.rows[0].win_probability; const payout = win ? amount * 2 : -amount; // Simple 2x win example; customize per game await pool.query('UPDATE users SET balance = balance + $1 WHERE id = $2', [payout, req.user.id]); await pool.query('INSERT INTO transactions (user_id, type, amount) VALUES ($1, $2, $3)', [req.user.id, win ? 'win' : 'loss', Math.abs(payout)]); res.json({ win, payout, newBalance: user.rows[0].balance + payout }); } catch (err) { res.status(500).json({ error: err.message }); } }); // Admin Routes app.get('/api/admin/users', authenticate, isAdmin, async (req, res) => { const users = await pool.query('SELECT id, username, email, balance, role FROM users'); res.json(users.rows); }); app.put('/api/admin/user/:id', authenticate, isAdmin, async (req, res) => { const { balance, role } = req.body; await pool.query('UPDATE users SET balance = $1, role = $2 WHERE id = $3', [balance, role, req.params.id]); res.json({ message: 'User updated' }); }); // Similar routes for promo codes, games, stats (e.g., sum wins/losses) app.get('/api/admin/stats', authenticate, isAdmin, async (req, res) => { const wins = await pool.query("SELECT SUM(amount) FROM transactions WHERE type = 'win'"); const losses = await pool.query("SELECT SUM(amount) FROM transactions WHERE type = 'loss'"); res.json({ totalWins: wins.rows[0].sum || 0, totalLosses: losses.rows[0].sum || 0 }); }); // Socket.io for multiplayer/chat io.on('connection', (socket) => { console.log('User connected'); socket.on('joinGame', (gameId) => { socket.join(gameId); // Broadcast messages/bets; integrate with Diablo-Lounge multiplayer logic }); socket.on('chatMessage', (msg) => { io.to(msg.gameId).emit('message', msg); }); // Add more for real-time game updates (e.g., card deals) }); server.listen(process.env.PORT, () => console.log(`Server running on port ${process.env.PORT}`));