Initial commit

This commit is contained in:
2025-08-16 19:43:44 +02:00
commit ea9a2627b4
64 changed files with 9232 additions and 0 deletions

63
server/middleware/auth.js Normal file
View File

@@ -0,0 +1,63 @@
const jwt = require('jsonwebtoken');
const { User } = require('../models');
async function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({
success: false,
message: 'Access token required'
});
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findByPk(decoded.userId, {
attributes: ['id', 'username', 'email', 'role', 'is_active']
});
if (!user || !user.is_active) {
return res.status(401).json({
success: false,
message: 'Invalid or inactive user'
});
}
req.user = user;
next();
} catch (error) {
console.error('Token verification error:', error);
return res.status(403).json({
success: false,
message: 'Invalid or expired token'
});
}
}
function requireRole(roles) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
success: false,
message: 'Authentication required'
});
}
const userRoles = Array.isArray(roles) ? roles : [roles];
if (!userRoles.includes(req.user.role)) {
return res.status(403).json({
success: false,
message: 'Insufficient permissions'
});
}
next();
};
}
module.exports = {
authenticateToken,
requireRole
};

View File

@@ -0,0 +1,57 @@
function errorHandler(error, req, res, next) {
console.error('Error occurred:', error);
// Default error response
let statusCode = 500;
let message = 'Internal server error';
let details = null;
// Handle specific error types
if (error.name === 'ValidationError') {
statusCode = 400;
message = 'Validation error';
details = error.details || error.message;
} else if (error.name === 'UnauthorizedError') {
statusCode = 401;
message = 'Unauthorized';
} else if (error.name === 'SequelizeValidationError') {
statusCode = 400;
message = 'Database validation error';
details = error.errors.map(err => ({
field: err.path,
message: err.message
}));
} else if (error.name === 'SequelizeUniqueConstraintError') {
statusCode = 409;
message = 'Resource already exists';
details = error.errors.map(err => ({
field: err.path,
message: err.message
}));
} else if (error.name === 'SequelizeForeignKeyConstraintError') {
statusCode = 400;
message = 'Invalid reference';
} else if (error.status) {
statusCode = error.status;
message = error.message;
}
// Send error response
const response = {
success: false,
message,
timestamp: new Date().toISOString()
};
// Include error details in development
if (process.env.NODE_ENV === 'development') {
response.error = details || error.message;
response.stack = error.stack;
} else if (details) {
response.details = details;
}
res.status(statusCode).json(response);
}
module.exports = errorHandler;

View File

@@ -0,0 +1,30 @@
function validateRequest(schema) {
return (req, res, next) => {
const { error, value } = schema.validate(req.body, {
abortEarly: false,
stripUnknown: true
});
if (error) {
const errorDetails = error.details.map(detail => ({
field: detail.path.join('.'),
message: detail.message,
value: detail.context.value
}));
return res.status(400).json({
success: false,
message: 'Validation error',
errors: errorDetails
});
}
// Replace req.body with validated and sanitized data
req.body = value;
next();
};
}
module.exports = {
validateRequest
};