diff --git a/client/vite.config.js b/client/vite.config.js index 69f52d5..a52eda4 100644 --- a/client/vite.config.js +++ b/client/vite.config.js @@ -39,6 +39,7 @@ export default defineConfig(({ mode }) => { changeOrigin: true, ws: true, secure: false, + xfwd: true, }, }, }, diff --git a/server/app.js b/server/app.js index 8c591ed..db52ad7 100644 --- a/server/app.js +++ b/server/app.js @@ -2,6 +2,8 @@ const express = require("express"); const staticRoutes = require("./routes/staticRoutes"); const apiRoutes = require("./routes/apiRoutes"); const cookieParser = require("cookie-parser"); +const cors = require('cors'); +const corsOptions = require('./utils/corsOptions'); const helmet = require("helmet"); const rateLimit = require("express-rate-limit"); const slowDown = require("express-slow-down"); @@ -29,6 +31,8 @@ const speedLimiter = slowDown({ if (process.env.NODE_ENV === "production") { // In production, trust the reverse proxy app.set("trust proxy", 1); +} else { + app.use(cors(corsOptions)); } app.use(express.json({ limit: "10kb" })); // Limit body size diff --git a/server/server.js b/server/server.js index 97afc2a..cdb91cc 100644 --- a/server/server.js +++ b/server/server.js @@ -46,7 +46,12 @@ const leaderboard = new LeaderboardManager(io); const socketLimiter = new SocketRateLimiter(); io.use(async (socket, next) => { // rate limiting - if(!socketLimiter.canConnect(socket.handshake.address)){ + socket.ip = + socket.handshake.headers['x-forwarded-for']?.split(',')[0] || + socket.handshake.address; //fallback + + if(!socketLimiter.canConnect(socket.ip)){ + console.log("Rate Limited:", socketLimiter.getRateLimitInfo(socket.ip)); return next(new Error('Too many connections, please try again later')); } @@ -88,7 +93,8 @@ io.on('connection', async (socket) => { socket.on('chat message', async (msg) => { // rate limit - if(!socketLimiter.canSendMessage(socket.handshake.address)){ + if(!socketLimiter.canSendMessage(socket.ip)){ + console.log("Rate Limited:", socketLimiter.getRateLimitInfo(socket.ip)); return; // TODO: send info back to client } diff --git a/server/utils/socketOptions.js b/server/utils/socketOptions.js index eb3d12f..c5d6dc2 100644 --- a/server/utils/socketOptions.js +++ b/server/utils/socketOptions.js @@ -2,6 +2,8 @@ const socketOptions = { pingTimeout: 5000, // How long to wait for a ping response before considering connection lost pingInterval: 60000, // How often to send ping to check connection maxHttpBufferSize: 1e4, // Limits message size to 10KB - prevents memory issues + proxy: true, + transports: ['websocket', 'polling'] }; module.exports = socketOptions; diff --git a/server/utils/socketRateLimiter.js b/server/utils/socketRateLimiter.js index f465dda..bad625a 100644 --- a/server/utils/socketRateLimiter.js +++ b/server/utils/socketRateLimiter.js @@ -90,6 +90,7 @@ class IPRateLimiter { if (!data) return null; return { + ip: ip, messagesRemaining: Math.max(0, this.MESSAGE_LIMIT - data.messages.count), connectionsRemaining: Math.max( 0,