Initial commit: TimeClock v3 - Node.js/Vue.js Zeiterfassung
Features: - Backend: Node.js/Express mit MySQL/MariaDB - Frontend: Vue.js 3 mit Composition API - UTC-Zeithandling für korrekte Zeiterfassung - Timewish-basierte Überstundenberechnung - Wochenübersicht mit Urlaubs-/Krankheits-/Feiertagshandling - Bereinigtes Arbeitsende (Generell/Woche) - Überstunden-Offset für historische Daten - Fixed Layout mit scrollbarem Content - Kompakte UI mit grünem Theme
This commit is contained in:
66
backend/src/middleware/auth.js
Normal file
66
backend/src/middleware/auth.js
Normal file
@@ -0,0 +1,66 @@
|
||||
const authService = require('../services/AuthService');
|
||||
|
||||
/**
|
||||
* Authentication Middleware
|
||||
* Validiert JWT-Token und fügt Benutzer-Info zu req hinzu
|
||||
*/
|
||||
const authenticateToken = async (req, res, next) => {
|
||||
try {
|
||||
// Token aus Authorization-Header extrahieren
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
error: 'Kein Token vorhanden',
|
||||
code: 'NO_TOKEN'
|
||||
});
|
||||
}
|
||||
|
||||
// Token validieren
|
||||
const decoded = await authService.validateToken(token);
|
||||
|
||||
// Benutzer-Info zu Request hinzufügen
|
||||
req.user = decoded;
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
console.error('Auth-Middleware-Fehler:', error.message);
|
||||
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
error: 'Ungültiger oder abgelaufener Token',
|
||||
code: 'INVALID_TOKEN'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Optional Authentication Middleware
|
||||
* Validiert Token falls vorhanden, erlaubt aber auch Requests ohne Token
|
||||
*/
|
||||
const optionalAuth = async (req, res, next) => {
|
||||
try {
|
||||
const authHeader = req.headers['authorization'];
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
|
||||
if (token) {
|
||||
const decoded = await authService.validateToken(token);
|
||||
req.user = decoded;
|
||||
}
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
// Bei optionalem Auth ignorieren wir Fehler
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
authenticateToken,
|
||||
optionalAuth
|
||||
};
|
||||
|
||||
|
||||
|
||||
104
backend/src/middleware/hashResponse.js
Normal file
104
backend/src/middleware/hashResponse.js
Normal file
@@ -0,0 +1,104 @@
|
||||
const hashId = require('../utils/hashId');
|
||||
|
||||
/**
|
||||
* Middleware zum automatischen Hashen von IDs in Response-Daten
|
||||
*/
|
||||
const hashResponseIds = (req, res, next) => {
|
||||
// Originale json-Methode speichern
|
||||
const originalJson = res.json.bind(res);
|
||||
|
||||
// json-Methode überschreiben
|
||||
res.json = function(data) {
|
||||
// Wenn Daten vorhanden sind, IDs hashen
|
||||
if (data) {
|
||||
data = hashResponseData(data);
|
||||
}
|
||||
|
||||
// Originale Methode aufrufen
|
||||
return originalJson(data);
|
||||
};
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* Rekursiv IDs in Datenstruktur hashen
|
||||
* @param {*} data - Zu verarbeitende Daten
|
||||
* @returns {*} Daten mit gehashten IDs
|
||||
*/
|
||||
function hashResponseData(data) {
|
||||
// Primitive Typen direkt zurückgeben
|
||||
if (data === null || data === undefined) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// Array: Jedes Element verarbeiten
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(item => hashResponseData(item));
|
||||
}
|
||||
|
||||
// Object: ID-Felder hashen
|
||||
if (typeof data === 'object') {
|
||||
const result = {};
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
// ID-Felder identifizieren und hashen
|
||||
if (isIdField(key) && typeof value === 'number') {
|
||||
result[key] = hashId.encode(value);
|
||||
}
|
||||
// Verschachtelte Objekte/Arrays rekursiv verarbeiten
|
||||
else if (typeof value === 'object') {
|
||||
result[key] = hashResponseData(value);
|
||||
}
|
||||
// Andere Werte unverändert übernehmen
|
||||
else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Andere Typen unverändert
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob ein Feldname eine ID repräsentiert
|
||||
* @param {string} fieldName - Name des Feldes
|
||||
* @returns {boolean} True wenn ID-Feld
|
||||
*/
|
||||
function isIdField(fieldName) {
|
||||
// Felder die als ID erkannt werden sollen
|
||||
const idPatterns = [
|
||||
'id',
|
||||
'_id',
|
||||
'user_id',
|
||||
'userId',
|
||||
'auth_info_id',
|
||||
'authInfoId',
|
||||
'auth_token_id',
|
||||
'authTokenId',
|
||||
'worklog_id',
|
||||
'worklogId',
|
||||
'vacation_id',
|
||||
'vacationId',
|
||||
'sick_id',
|
||||
'sickId',
|
||||
'holiday_id',
|
||||
'holidayId',
|
||||
'state_id',
|
||||
'stateId',
|
||||
'sick_type_id',
|
||||
'sickTypeId',
|
||||
'weekly_worktime_id',
|
||||
'weeklyWorktimeId'
|
||||
];
|
||||
|
||||
return idPatterns.includes(fieldName);
|
||||
}
|
||||
|
||||
module.exports = hashResponseIds;
|
||||
|
||||
|
||||
|
||||
116
backend/src/middleware/unhashRequest.js
Normal file
116
backend/src/middleware/unhashRequest.js
Normal file
@@ -0,0 +1,116 @@
|
||||
const hashId = require('../utils/hashId');
|
||||
|
||||
/**
|
||||
* Middleware zum automatischen Enthashen von IDs in Request-Daten
|
||||
*/
|
||||
const unhashRequestIds = (req, res, next) => {
|
||||
// Body-Parameter verarbeiten
|
||||
if (req.body && typeof req.body === 'object') {
|
||||
req.body = unhashRequestData(req.body);
|
||||
}
|
||||
|
||||
// Query-Parameter verarbeiten
|
||||
if (req.query && typeof req.query === 'object') {
|
||||
req.query = unhashRequestData(req.query);
|
||||
}
|
||||
|
||||
// Route-Parameter verarbeiten
|
||||
if (req.params && typeof req.params === 'object') {
|
||||
req.params = unhashRequestData(req.params);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
/**
|
||||
* Rekursiv Hash-IDs in Datenstruktur entschlüsseln
|
||||
* @param {*} data - Zu verarbeitende Daten
|
||||
* @returns {*} Daten mit entschlüsselten IDs
|
||||
*/
|
||||
function unhashRequestData(data) {
|
||||
// Primitive Typen direkt zurückgeben
|
||||
if (data === null || data === undefined) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// String: Könnte ein Hash sein
|
||||
if (typeof data === 'string') {
|
||||
// Versuche als Hash zu dekodieren
|
||||
const decoded = hashId.decode(data);
|
||||
if (decoded !== null) {
|
||||
return decoded;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// Array: Jedes Element verarbeiten
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(item => unhashRequestData(item));
|
||||
}
|
||||
|
||||
// Object: Rekursiv verarbeiten
|
||||
if (typeof data === 'object') {
|
||||
const result = {};
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
// ID-Felder identifizieren und enthashen
|
||||
if (isIdField(key) && typeof value === 'string') {
|
||||
const decoded = hashId.decode(value);
|
||||
result[key] = decoded !== null ? decoded : value;
|
||||
}
|
||||
// Verschachtelte Objekte/Arrays rekursiv verarbeiten
|
||||
else if (typeof value === 'object') {
|
||||
result[key] = unhashRequestData(value);
|
||||
}
|
||||
// Andere Werte unverändert übernehmen
|
||||
else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Andere Typen unverändert
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob ein Feldname eine ID repräsentiert
|
||||
* @param {string} fieldName - Name des Feldes
|
||||
* @returns {boolean} True wenn ID-Feld
|
||||
*/
|
||||
function isIdField(fieldName) {
|
||||
// Felder die als ID erkannt werden sollen
|
||||
const idPatterns = [
|
||||
'id',
|
||||
'_id',
|
||||
'user_id',
|
||||
'userId',
|
||||
'auth_info_id',
|
||||
'authInfoId',
|
||||
'auth_token_id',
|
||||
'authTokenId',
|
||||
'worklog_id',
|
||||
'worklogId',
|
||||
'vacation_id',
|
||||
'vacationId',
|
||||
'sick_id',
|
||||
'sickId',
|
||||
'holiday_id',
|
||||
'holidayId',
|
||||
'state_id',
|
||||
'stateId',
|
||||
'sick_type_id',
|
||||
'sickTypeId',
|
||||
'weekly_worktime_id',
|
||||
'weeklyWorktimeId'
|
||||
];
|
||||
|
||||
return idPatterns.includes(fieldName);
|
||||
}
|
||||
|
||||
module.exports = unhashRequestIds;
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user