From 5f13158ac7fc687ce145f9dd7bd955cd4a9e0754 Mon Sep 17 00:00:00 2001 From: arriej Date: Sun, 31 Aug 2025 23:41:50 +0200 Subject: [PATCH] Set up Node.js with advanced security deps --- models/Game.js | 0 models/PromoCode.js | 0 models/Transaction.js | 0 models/User.js | 0 models/index.js | 0 package-lock.json | 218 ++++++++++++++++++++++++++++++++++++++++++ package.json | 19 +++- routes/admin.js | 0 routes/auth.js | 0 routes/games.js | 0 routes/payments.js | 0 server.js | 137 ++++++++++++++++++++++++++ 12 files changed, 370 insertions(+), 4 deletions(-) delete mode 100644 models/Game.js delete mode 100644 models/PromoCode.js delete mode 100644 models/Transaction.js delete mode 100644 models/User.js delete mode 100644 models/index.js delete mode 100644 routes/admin.js delete mode 100644 routes/auth.js delete mode 100644 routes/games.js delete mode 100644 routes/payments.js create mode 100644 server.js diff --git a/models/Game.js b/models/Game.js deleted file mode 100644 index e69de29..0000000 diff --git a/models/PromoCode.js b/models/PromoCode.js deleted file mode 100644 index e69de29..0000000 diff --git a/models/Transaction.js b/models/Transaction.js deleted file mode 100644 index e69de29..0000000 diff --git a/models/User.js b/models/User.js deleted file mode 100644 index e69de29..0000000 diff --git a/models/index.js b/models/index.js deleted file mode 100644 index e69de29..0000000 diff --git a/package-lock.json b/package-lock.json index 7764e7a..9fe94c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,14 @@ "dependencies": { "bcryptjs": "^3.0.2", "body-parser": "^2.2.0", + "cookie-parser": "^1.4.7", "cors": "^2.8.5", + "csurf": "^1.2.2", "dotenv": "^17.2.1", "express": "^5.1.0", + "express-rate-limit": "^8.0.1", + "express-validator": "^7.2.1", + "helmet": "^8.1.0", "jsonwebtoken": "^9.0.2", "pg": "^8.16.3", "socket.io": "^4.8.1" @@ -261,6 +266,25 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", @@ -283,6 +307,100 @@ "node": ">= 0.10" } }, + "node_modules/csrf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz", + "integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==", + "license": "MIT", + "dependencies": { + "rndm": "1.2.0", + "tsscmp": "1.0.6", + "uid-safe": "2.1.5" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/csurf": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.11.0.tgz", + "integrity": "sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==", + "deprecated": "This package is archived and no longer maintained. For support, visit https://github.com/expressjs/express/discussions", + "license": "MIT", + "dependencies": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "csrf": "3.1.0", + "http-errors": "~1.7.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/csurf/node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/csurf/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/csurf/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/csurf/node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/csurf/node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "license": "ISC" + }, + "node_modules/csurf/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/csurf/node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -535,6 +653,37 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.0.1.tgz", + "integrity": "sha512-aZVCnybn7TVmxO4BtlmnvX+nuz8qHW124KKJ8dumsBsmv5ZLxE0pYu7S2nwyRBGHHCAzdmnGyrc5U/rksSPO7Q==", + "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express-validator": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.2.1.tgz", + "integrity": "sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21", + "validator": "~13.12.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -703,6 +852,15 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -753,6 +911,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -857,6 +1024,12 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -1253,6 +1426,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -1290,6 +1472,12 @@ "node": ">=8.10.0" } }, + "node_modules/rndm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", + "integrity": "sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw==", + "license": "MIT" + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -1670,6 +1858,15 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } + }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", @@ -1684,6 +1881,18 @@ "node": ">= 0.6" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -1706,6 +1915,15 @@ "node": ">= 0.8" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 3152c1e..6145593 100644 --- a/package.json +++ b/package.json @@ -3,23 +3,34 @@ "version": "1.0.0", "description": "A casino web application built with Node.js and Express", "main": "server.js", - "scripts":{ - "start": "node server.js", - "dev": "nodemon server.js" + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js" }, "repository": { "type": "git", "url": "https://git.skunkhotel.com/arriej/Skunk-casino.git" }, - "keywords": ["casino", "nodejs", "express", "webapp", "socket.io"], + "keywords": [ + "casino", + "nodejs", + "express", + "webapp", + "socket.io" + ], "author": "Your Name or Organization", "license": "ISC", "dependencies": { "bcryptjs": "^3.0.2", "body-parser": "^2.2.0", + "cookie-parser": "^1.4.7", "cors": "^2.8.5", + "csurf": "^1.2.2", "dotenv": "^17.2.1", "express": "^5.1.0", + "express-rate-limit": "^8.0.1", + "express-validator": "^7.2.1", + "helmet": "^8.1.0", "jsonwebtoken": "^9.0.2", "pg": "^8.16.3", "socket.io": "^4.8.1" diff --git a/routes/admin.js b/routes/admin.js deleted file mode 100644 index e69de29..0000000 diff --git a/routes/auth.js b/routes/auth.js deleted file mode 100644 index e69de29..0000000 diff --git a/routes/games.js b/routes/games.js deleted file mode 100644 index e69de29..0000000 diff --git a/routes/payments.js b/routes/payments.js deleted file mode 100644 index e69de29..0000000 diff --git a/server.js b/server.js new file mode 100644 index 0000000..c590a9e --- /dev/null +++ b/server.js @@ -0,0 +1,137 @@ +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}`)); \ No newline at end of file