Initial commit
This commit is contained in:
80
remote/src/middlewares/auth.js
Normal file
80
remote/src/middlewares/auth.js
Normal file
@@ -0,0 +1,80 @@
|
||||
const AuthService = require('../services/auth/AuthService');
|
||||
const TokenManager = require('../services/auth/TokenManager');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
function extractToken(req) {
|
||||
if (req.headers.authorization) {
|
||||
const parts = req.headers.authorization.split(' ');
|
||||
if (parts.length === 2 && parts[0] === 'Bearer') {
|
||||
return parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (req.cookies) {
|
||||
if (req.cookies.token) {
|
||||
return req.cookies.token;
|
||||
}
|
||||
if (req.cookies.auth) {
|
||||
return req.cookies.auth;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.query && req.query.token) {
|
||||
return req.query.token;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function authMiddleware(req, res, next) {
|
||||
const authService = AuthService.getInstance();
|
||||
const tokenManager = TokenManager.getInstance();
|
||||
|
||||
if (!authService.hasPassword()) {
|
||||
req.user = { userId: 'default-user' };
|
||||
res.locals.authenticated = true;
|
||||
return next();
|
||||
}
|
||||
|
||||
const token = extractToken(req);
|
||||
|
||||
if (token) {
|
||||
const decoded = tokenManager.verifyToken(token);
|
||||
if (decoded) {
|
||||
req.user = { userId: decoded.userId };
|
||||
res.locals.authenticated = true;
|
||||
logger.debug('Authentication successful via token', { userId: decoded.userId });
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
const password = req.query.password || req.body?.password;
|
||||
|
||||
if (password) {
|
||||
try {
|
||||
const isValid = await authService.authenticate(password);
|
||||
if (isValid) {
|
||||
req.user = { userId: 'default-user' };
|
||||
res.locals.authenticated = true;
|
||||
logger.debug('Authentication successful via password');
|
||||
return next();
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Authentication error', { error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn('Authentication failed', {
|
||||
ip: req.socket?.remoteAddress,
|
||||
path: req.path,
|
||||
hasToken: !!token,
|
||||
hasPassword: !!password
|
||||
});
|
||||
|
||||
res.status(401).json({
|
||||
error: 'Authentication required',
|
||||
code: 'AUTH_REQUIRED'
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = authMiddleware;
|
||||
78
remote/src/middlewares/error.js
Normal file
78
remote/src/middlewares/error.js
Normal file
@@ -0,0 +1,78 @@
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
function errorHandler(err, req, res, next) {
|
||||
const statusCode = err.statusCode || err.status || 500;
|
||||
const errorCode = err.code || 'INTERNAL_ERROR';
|
||||
|
||||
const errorResponse = {
|
||||
error: err.message || 'Internal Server Error',
|
||||
code: errorCode
|
||||
};
|
||||
|
||||
if (err.details) {
|
||||
errorResponse.details = err.details;
|
||||
}
|
||||
|
||||
if (statusCode >= 500) {
|
||||
logger.error('Server error', {
|
||||
error: err.message,
|
||||
code: errorCode,
|
||||
stack: err.stack,
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
ip: req.socket?.remoteAddress
|
||||
});
|
||||
} else {
|
||||
logger.warn('Client error', {
|
||||
error: err.message,
|
||||
code: errorCode,
|
||||
path: req.path,
|
||||
method: req.method,
|
||||
ip: req.socket?.remoteAddress
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development' && err.stack) {
|
||||
errorResponse.stack = err.stack;
|
||||
}
|
||||
|
||||
res.status(statusCode).json(errorResponse);
|
||||
}
|
||||
|
||||
class AppError extends Error {
|
||||
constructor(message, statusCode = 500, code = 'APP_ERROR') {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
this.code = code;
|
||||
this.status = statusCode;
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
|
||||
withDetails(details) {
|
||||
this.details = details;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
function createError(message, statusCode = 500, code = 'APP_ERROR') {
|
||||
return new AppError(message, statusCode, code);
|
||||
}
|
||||
|
||||
function notFoundHandler(req, res, next) {
|
||||
const error = new AppError(`Not Found - ${req.originalUrl}`, 404, 'NOT_FOUND');
|
||||
next(error);
|
||||
}
|
||||
|
||||
function asyncHandler(fn) {
|
||||
return (req, res, next) => {
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
errorHandler,
|
||||
AppError,
|
||||
createError,
|
||||
notFoundHandler,
|
||||
asyncHandler
|
||||
};
|
||||
87
remote/src/middlewares/rateLimit.js
Normal file
87
remote/src/middlewares/rateLimit.js
Normal file
@@ -0,0 +1,87 @@
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
class RateLimiter {
|
||||
constructor(options = {}) {
|
||||
this.windowMs = options.windowMs || 60 * 1000;
|
||||
this.maxRequests = options.maxRequests || 5;
|
||||
this.requests = new Map();
|
||||
this.cleanupInterval = setInterval(() => this.cleanup(), this.windowMs);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
const now = Date.now();
|
||||
for (const [key, data] of this.requests.entries()) {
|
||||
if (now - data.startTime > this.windowMs) {
|
||||
this.requests.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getKey(req) {
|
||||
return req.ip || req.socket?.remoteAddress || 'unknown';
|
||||
}
|
||||
|
||||
middleware() {
|
||||
return (req, res, next) => {
|
||||
const key = this.getKey(req);
|
||||
const now = Date.now();
|
||||
|
||||
let requestData = this.requests.get(key);
|
||||
|
||||
if (!requestData || now - requestData.startTime > this.windowMs) {
|
||||
requestData = {
|
||||
count: 0,
|
||||
startTime: now
|
||||
};
|
||||
this.requests.set(key, requestData);
|
||||
}
|
||||
|
||||
requestData.count++;
|
||||
|
||||
const remainingTime = Math.ceil((requestData.startTime + this.windowMs - now) / 1000);
|
||||
|
||||
res.setHeader('X-RateLimit-Limit', this.maxRequests);
|
||||
res.setHeader('X-RateLimit-Remaining', Math.max(0, this.maxRequests - requestData.count));
|
||||
res.setHeader('X-RateLimit-Reset', remainingTime);
|
||||
|
||||
if (requestData.count > this.maxRequests) {
|
||||
logger.warn('Rate limit exceeded', {
|
||||
ip: key,
|
||||
path: req.path,
|
||||
count: requestData.count,
|
||||
maxRequests: this.maxRequests
|
||||
});
|
||||
|
||||
return res.status(429).json({
|
||||
error: 'Too many requests, please try again later',
|
||||
code: 'RATE_LIMIT_EXCEEDED',
|
||||
retryAfter: remainingTime
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.cleanupInterval) {
|
||||
clearInterval(this.cleanupInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createRateLimiter(options = {}) {
|
||||
const limiter = new RateLimiter(options);
|
||||
return limiter.middleware();
|
||||
}
|
||||
|
||||
const defaultRateLimiter = createRateLimiter({
|
||||
windowMs: 60 * 1000,
|
||||
maxRequests: 5
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
RateLimiter,
|
||||
createRateLimiter,
|
||||
defaultRateLimiter
|
||||
};
|
||||
Reference in New Issue
Block a user