Initial commit: IT Nexus Web-App

This commit is contained in:
2026-06-01 20:49:07 +02:00
commit 8023765e6c
387 changed files with 106900 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
const jwt = require('jsonwebtoken');
const JWT_CONFIG = require('../config/jwt');
/**
* Middleware to verify JWT token and attach user to request
*/
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({
status: 'error',
message: 'Access token required'
});
}
try {
const decoded = jwt.verify(token, JWT_CONFIG.secret);
req.user = decoded; // { id, username, email, role, roleId }
next();
} catch (error) {
if (error.name === 'TokenExpiredError') {
return res.status(401).json({
status: 'error',
message: 'Token expired'
});
}
return res.status(403).json({
status: 'error',
message: 'Invalid token'
});
}
}
/**
* Optional authentication - doesn't fail if no token provided
* But validates token if present
*/
function optionalAuth(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
req.user = null;
return next();
}
try {
const decoded = jwt.verify(token, JWT_CONFIG.secret);
req.user = decoded;
next();
} catch (error) {
req.user = null;
next();
}
}
module.exports = {
authenticateToken,
optionalAuth
};

View File

@@ -0,0 +1,67 @@
/**
* Custom error class for application errors
*/
class AppError extends Error {
constructor(message, statusCode = 500, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
Error.captureStackTrace(this, this.constructor);
}
}
/**
* Global error handling middleware
*/
function errorHandler(err, req, res, next) {
let { statusCode = 500, message } = err;
// Log error for debugging
if (process.env.NODE_ENV === 'development') {
console.error('❌ Error:', err);
}
// Don't leak error details in production for operational errors
if (process.env.NODE_ENV === 'production' && !err.isOperational) {
message = 'Something went wrong';
}
// Prepare response
const response = {
status: 'error',
message
};
// Add stack trace in development
if (process.env.NODE_ENV === 'development') {
response.stack = err.stack;
}
res.status(statusCode).json(response);
}
/**
* Handle 404 errors
*/
function notFoundHandler(req, res, next) {
res.status(404).json({
status: 'error',
message: 'Route not found'
});
}
/**
* Async wrapper to catch errors in async route handlers
*/
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
module.exports = {
AppError,
errorHandler,
notFoundHandler,
asyncHandler
};

View File

@@ -0,0 +1,124 @@
/**
* Role-based access control middleware
*
* Usage: requireRole(['super_admin', 'admin'])
*/
const ROLES = {
SUPER_ADMIN: 'super_admin',
ADMIN: 'admin',
BEARBEITER: 'bearbeiter',
BENUTZER: 'benutzer',
SUPPORT: 'support',
HR_PERSONAL: 'hr_personal',
BUCHHALTUNG: 'buchhaltung',
TECHNIKER: 'produktion',
};
/**
* Middleware to check if user has one of the required roles
* @param {Array<string>} allowedRoles - Array of role names that are allowed
*/
function requireRole(allowedRoles) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
status: 'error',
message: 'Authentication required'
});
}
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({
status: 'error',
message: 'Insufficient permissions'
});
}
next();
};
}
/**
* Check if user is Super Admin
*/
function requireSuperAdmin(req, res, next) {
return requireRole([ROLES.SUPER_ADMIN])(req, res, next);
}
/**
* Check if user is Super Admin or Admin
*/
function requireAdmin(req, res, next) {
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN])(req, res, next);
}
/**
* Check if user can modify FIDO keys (Super Admin, Admin, or Bearbeiter)
*/
function canModifyFidoKeys(req, res, next) {
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.BEARBEITER])(req, res, next);
}
/**
* Check if user can view FIDO keys (all authenticated users)
*/
function canViewFidoKeys(req, res, next) {
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.BEARBEITER, ROLES.BENUTZER])(req, res, next);
}
/**
* Check if user can view assets (Super Admin, Admin, Bearbeiter)
*/
function canViewAssets(req, res, next) {
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.TECHNIKER])(req, res, next);
}
/**
* Check if user can modify assets (Super Admin, Admin only)
*/
function canModifyAssets(req, res, next) {
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.TECHNIKER])(req, res, next);
}
/**
* Check if user can view tickets (all authenticated users)
*/
function canViewTickets(req, res, next) {
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.SUPPORT, ROLES.BEARBEITER, ROLES.BENUTZER, ROLES.HR_PERSONAL, ROLES.BUCHHALTUNG, ROLES.TECHNIKER])(req, res, next);
}
/**
* Check if user can modify tickets (Super Admin, Admin, Support)
*/
function canModifyTickets(req, res, next) {
return requireRole([ROLES.SUPER_ADMIN, ROLES.ADMIN, ROLES.SUPPORT])(req, res, next);
}
/**
* Can view onboarding: admins + dept roles (hr_personal, buchhaltung, support used for IT)
*/
function canViewOnboarding(req, res, next) {
return requireRole([
ROLES.SUPER_ADMIN, ROLES.ADMIN,
ROLES.HR_PERSONAL, ROLES.BUCHHALTUNG, ROLES.SUPPORT,
])(req, res, next);
}
// Departments visible per role
const TECHNIKER_DEPARTMENTS = ['Produktion', 'Techniker'];
module.exports = {
ROLES,
TECHNIKER_DEPARTMENTS,
requireRole,
requireSuperAdmin,
requireAdmin,
canModifyFidoKeys,
canViewFidoKeys,
canViewAssets,
canModifyAssets,
canViewTickets,
canModifyTickets,
canViewOnboarding,
};