Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ffcfd9766d | |||
| 3501a52f0e |
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.59",
|
"version": "1.0.60",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.59",
|
"version": "1.0.60",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"archiver": "^5.3.1",
|
"archiver": "^5.3.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "picpeak-backend",
|
"name": "picpeak-backend",
|
||||||
"version": "1.0.59",
|
"version": "1.0.60",
|
||||||
"description": "Backend for PicPeak event photo sharing platform",
|
"description": "Backend for PicPeak event photo sharing platform",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+72
-1
@@ -97,6 +97,38 @@ app.use(cors(corsOptions));
|
|||||||
const limiter = rateLimit({
|
const limiter = rateLimit({
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
max: process.env.NODE_ENV === 'development' ? 1000 : 100, // More lenient in development
|
max: process.env.NODE_ENV === 'development' ? 1000 : 100, // More lenient in development
|
||||||
|
// Use correct client IP when behind proxy
|
||||||
|
keyGenerator: (req) => {
|
||||||
|
// Get the real client IP from proxy headers
|
||||||
|
const clientIp = req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
||||||
|
req.headers['x-real-ip'] ||
|
||||||
|
req.connection.remoteAddress ||
|
||||||
|
req.ip;
|
||||||
|
|
||||||
|
// Log rate limit key for debugging (only in development)
|
||||||
|
if (process.env.NODE_ENV === 'development' && req.path.includes('/api/')) {
|
||||||
|
logger.debug('Rate limit key generated', {
|
||||||
|
path: req.path,
|
||||||
|
clientIp,
|
||||||
|
headers: {
|
||||||
|
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||||
|
'x-real-ip': req.headers['x-real-ip']
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientIp;
|
||||||
|
},
|
||||||
|
handler: (req, res) => {
|
||||||
|
logger.warn('Rate limit exceeded', {
|
||||||
|
ip: req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip,
|
||||||
|
path: req.path,
|
||||||
|
method: req.method
|
||||||
|
});
|
||||||
|
res.status(429).json({
|
||||||
|
error: 'Too many requests, please try again later.'
|
||||||
|
});
|
||||||
|
},
|
||||||
skip: (req) => {
|
skip: (req) => {
|
||||||
// Skip rate limiting for authenticated admin users
|
// Skip rate limiting for authenticated admin users
|
||||||
if (req.path.startsWith('/api/admin/') && req.headers.authorization) {
|
if (req.path.startsWith('/api/admin/') && req.headers.authorization) {
|
||||||
@@ -118,7 +150,24 @@ const limiter = rateLimit({
|
|||||||
|
|
||||||
const authLimiter = rateLimit({
|
const authLimiter = rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 5 // limit auth attempts
|
max: 5, // limit auth attempts
|
||||||
|
// Use correct client IP when behind proxy
|
||||||
|
keyGenerator: (req) => {
|
||||||
|
// Get the real client IP from proxy headers
|
||||||
|
return req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
||||||
|
req.headers['x-real-ip'] ||
|
||||||
|
req.connection.remoteAddress ||
|
||||||
|
req.ip;
|
||||||
|
},
|
||||||
|
handler: (req, res) => {
|
||||||
|
logger.warn('Auth rate limit exceeded', {
|
||||||
|
ip: req.headers['x-forwarded-for']?.split(',')[0]?.trim() || req.ip,
|
||||||
|
path: req.path
|
||||||
|
});
|
||||||
|
res.status(429).json({
|
||||||
|
error: 'Too many authentication attempts, please try again later.'
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply rate limiting - admin routes check will skip for valid admin tokens
|
// Apply rate limiting - admin routes check will skip for valid admin tokens
|
||||||
@@ -158,6 +207,28 @@ app.use('/thumbnails', require('./src/middleware/photoAuth'), setCorsHeaders, se
|
|||||||
// Static file serving for uploads (public - logos, favicons)
|
// Static file serving for uploads (public - logos, favicons)
|
||||||
app.use('/uploads', setCorsHeaders, secureStatic(path.join(storagePath, 'uploads')));
|
app.use('/uploads', setCorsHeaders, secureStatic(path.join(storagePath, 'uploads')));
|
||||||
|
|
||||||
|
// Debug endpoint to check IP detection (only in development)
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
app.get('/api/debug/ip', (req, res) => {
|
||||||
|
const clientIp = req.headers['x-forwarded-for']?.split(',')[0]?.trim() ||
|
||||||
|
req.headers['x-real-ip'] ||
|
||||||
|
req.connection.remoteAddress ||
|
||||||
|
req.ip;
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
detectedIp: clientIp,
|
||||||
|
reqIp: req.ip,
|
||||||
|
headers: {
|
||||||
|
'x-forwarded-for': req.headers['x-forwarded-for'],
|
||||||
|
'x-real-ip': req.headers['x-real-ip'],
|
||||||
|
'x-forwarded-proto': req.headers['x-forwarded-proto'],
|
||||||
|
'x-forwarded-host': req.headers['x-forwarded-host']
|
||||||
|
},
|
||||||
|
trustProxy: app.get('trust proxy')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Health check endpoint
|
// Health check endpoint
|
||||||
app.get('/health', async (req, res) => {
|
app.get('/health', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user