Set up Node.js with advanced security deps

This commit is contained in:
2025-08-31 23:41:50 +02:00
parent 72273246b1
commit 5f13158ac7
12 changed files with 370 additions and 4 deletions

View File

View File

View File

View File

View File

218
package-lock.json generated
View File

@@ -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",

View File

@@ -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"

View File

View File

View File

View File

137
server.js Normal file
View File

@@ -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}`));