mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-11-30 22:45:46 +01:00
fixed blowup code
This commit is contained in:
@@ -22,7 +22,7 @@ export interface LoginRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface JWTPayload {
|
export interface JWTPayload {
|
||||||
id: number;
|
id: string; // ← VON number ZU string ÄNDERN
|
||||||
email: string;
|
email: string;
|
||||||
role: string;
|
role: string;
|
||||||
iat?: number;
|
iat?: number;
|
||||||
@@ -64,7 +64,7 @@ export const login = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// Create token payload
|
// Create token payload
|
||||||
const tokenPayload: JWTPayload = {
|
const tokenPayload: JWTPayload = {
|
||||||
id: user.id,
|
id: user.id.toString(), // ← Sicherstellen dass es string ist
|
||||||
email: user.email,
|
email: user.email,
|
||||||
role: user.role
|
role: user.role
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,25 +5,6 @@ import bcrypt from 'bcryptjs';
|
|||||||
import { db } from '../services/databaseService.js';
|
import { db } from '../services/databaseService.js';
|
||||||
import { AuthRequest } from '../middleware/auth.js';
|
import { AuthRequest } from '../middleware/auth.js';
|
||||||
|
|
||||||
export const getEmployees = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
||||||
try {
|
|
||||||
const employees = await db.all<any>(`
|
|
||||||
SELECT
|
|
||||||
id, email, name, role, is_active as isActive,
|
|
||||||
phone, department, created_at as createdAt,
|
|
||||||
last_login as lastLogin
|
|
||||||
FROM users
|
|
||||||
WHERE is_active = 1
|
|
||||||
ORDER BY name
|
|
||||||
`);
|
|
||||||
|
|
||||||
res.json(employees);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching employees:', error);
|
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
export const getEmployee = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
@@ -56,7 +37,14 @@ export const createEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
password: '***hidden***'
|
password: '***hidden***'
|
||||||
});
|
});
|
||||||
|
|
||||||
const { email, password, name, role, phone, department } = req.body;
|
const { email, password, name, role, phone, department } = req.body as {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
phone?: string;
|
||||||
|
department?: string;
|
||||||
|
};
|
||||||
|
|
||||||
// Validierung
|
// Validierung
|
||||||
if (!email || !password || !name || !role) {
|
if (!email || !password || !name || !role) {
|
||||||
@@ -158,7 +146,7 @@ export const deleteEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
console.log('🗑️ Starting deletion process for employee ID:', id);
|
console.log('🗑️ Starting deletion process for employee ID:', id);
|
||||||
|
|
||||||
// Get full employee details first
|
// Check if employee exists
|
||||||
const existingEmployee = await db.get<any>(`
|
const existingEmployee = await db.get<any>(`
|
||||||
SELECT id, email, name, is_active, role
|
SELECT id, email, name, is_active, role
|
||||||
FROM users
|
FROM users
|
||||||
@@ -173,137 +161,60 @@ export const deleteEmployee = async (req: AuthRequest, res: Response): Promise<v
|
|||||||
|
|
||||||
console.log('📝 Found employee to delete:', existingEmployee);
|
console.log('📝 Found employee to delete:', existingEmployee);
|
||||||
|
|
||||||
try {
|
|
||||||
// Start transaction
|
// Start transaction
|
||||||
await db.run('BEGIN TRANSACTION');
|
await db.run('BEGIN TRANSACTION');
|
||||||
|
|
||||||
// Remove all references first
|
try {
|
||||||
const queries = [
|
|
||||||
// 1. Remove availabilities
|
// 1. Remove availabilities
|
||||||
'DELETE FROM employee_availabilities WHERE employee_id = ?',
|
await db.run('DELETE FROM employee_availabilities WHERE employee_id = ?', [id]);
|
||||||
|
|
||||||
// 2. Remove from assigned shifts
|
// 2. Remove from assigned_shifts (JSON field cleanup)
|
||||||
`UPDATE assigned_shifts
|
interface AssignedShift {
|
||||||
SET assigned_employees = json_remove(
|
id: string;
|
||||||
assigned_employees,
|
assigned_employees: string;
|
||||||
'$[' || (
|
}
|
||||||
SELECT key
|
|
||||||
FROM json_each(assigned_employees)
|
|
||||||
WHERE value = ?
|
|
||||||
LIMIT 1
|
|
||||||
) || ']'
|
|
||||||
)
|
|
||||||
WHERE json_extract(assigned_employees, '$') LIKE ?`,
|
|
||||||
|
|
||||||
// 3. Nullify references
|
const assignedShifts = await db.all<AssignedShift>(
|
||||||
'UPDATE shift_plans SET created_by = NULL WHERE created_by = ?',
|
'SELECT id, assigned_employees FROM assigned_shifts WHERE json_extract(assigned_employees, "$") LIKE ?',
|
||||||
'UPDATE shift_templates SET created_by = NULL WHERE created_by = ?',
|
[`%${id}%`]
|
||||||
|
);
|
||||||
|
|
||||||
// 4. Delete the user
|
for (const shift of assignedShifts) {
|
||||||
'DELETE FROM users WHERE id = ?'
|
try {
|
||||||
];
|
const employeesArray: string[] = JSON.parse(shift.assigned_employees || '[]');
|
||||||
|
const filteredEmployees = employeesArray.filter((empId: string) => empId !== id);
|
||||||
// Execute all cleanup queries
|
await db.run(
|
||||||
for (const query of queries) {
|
'UPDATE assigned_shifts SET assigned_employees = ? WHERE id = ?',
|
||||||
if (query.includes('json_extract')) {
|
[JSON.stringify(filteredEmployees), shift.id]
|
||||||
await db.run(query, [id, `%${id}%`]);
|
);
|
||||||
} else {
|
} catch (parseError) {
|
||||||
await db.run(query, [id]);
|
console.warn(`Could not parse assigned_employees for shift ${shift.id}:`, shift.assigned_employees);
|
||||||
|
// Falls JSON parsing fehlschlägt, setze leeres Array
|
||||||
|
await db.run(
|
||||||
|
'UPDATE assigned_shifts SET assigned_employees = ? WHERE id = ?',
|
||||||
|
[JSON.stringify([]), shift.id]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the deletion
|
// 3. Nullify created_by references
|
||||||
const verifyDeletion = await db.get<{count: number}>(`
|
await db.run('UPDATE shift_plans SET created_by = NULL WHERE created_by = ?', [id]);
|
||||||
SELECT
|
await db.run('UPDATE shift_templates SET created_by = NULL WHERE created_by = ?', [id]);
|
||||||
(SELECT COUNT(*) FROM users WHERE id = ?) +
|
|
||||||
(SELECT COUNT(*) FROM employee_availabilities WHERE employee_id = ?) +
|
|
||||||
(SELECT COUNT(*) FROM assigned_shifts WHERE json_extract(assigned_employees, '$') LIKE ?) as count
|
|
||||||
`, [id, id, `%${id}%`]);
|
|
||||||
|
|
||||||
if ((verifyDeletion?.count ?? 0) > 0) {
|
// 4. Finally delete the user
|
||||||
throw new Error('Failed to remove all references to the employee');
|
await db.run('DELETE FROM users WHERE id = ?', [id]);
|
||||||
}
|
|
||||||
|
|
||||||
// If we got here, everything worked
|
|
||||||
await db.run('COMMIT');
|
await db.run('COMMIT');
|
||||||
console.log('✅ Successfully deleted employee:', existingEmployee.email);
|
console.log('✅ Successfully deleted employee:', existingEmployee.email);
|
||||||
|
|
||||||
res.status(204).send();
|
res.status(204).send();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during deletion, rolling back:', error);
|
|
||||||
await db.run('ROLLBACK');
|
await db.run('ROLLBACK');
|
||||||
|
console.error('Error during deletion transaction:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Attempting to delete employee:', { id, email: existingEmployee.email });
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Start a transaction to ensure all deletes succeed or none do
|
|
||||||
await db.run('BEGIN TRANSACTION');
|
|
||||||
|
|
||||||
console.log('Starting transaction for deletion of employee:', id);
|
|
||||||
|
|
||||||
// First verify the current state
|
|
||||||
const beforeState = await db.all(`
|
|
||||||
SELECT
|
|
||||||
(SELECT COUNT(*) FROM employee_availabilities WHERE employee_id = ?) as avail_count,
|
|
||||||
(SELECT COUNT(*) FROM shift_templates WHERE created_by = ?) as template_count,
|
|
||||||
(SELECT COUNT(*) FROM shift_plans WHERE created_by = ?) as plan_count,
|
|
||||||
(SELECT COUNT(*) FROM users WHERE id = ?) as user_count
|
|
||||||
`, [id, id, id, id]);
|
|
||||||
console.log('Before deletion state:', beforeState[0]);
|
|
||||||
|
|
||||||
// Clear all references first
|
|
||||||
await db.run(`DELETE FROM employee_availabilities WHERE employee_id = ?`, [id]);
|
|
||||||
await db.run(`UPDATE shift_plans SET created_by = NULL WHERE created_by = ?`, [id]);
|
|
||||||
await db.run(`UPDATE shift_templates SET created_by = NULL WHERE created_by = ?`, [id]);
|
|
||||||
await db.run(`UPDATE assigned_shifts
|
|
||||||
SET assigned_employees = json_remove(assigned_employees, '$[' || json_each.key || ']')
|
|
||||||
FROM json_each(assigned_shifts.assigned_employees)
|
|
||||||
WHERE json_each.value = ?`, [id]);
|
|
||||||
|
|
||||||
// Now delete the user
|
|
||||||
await db.run('DELETE FROM users WHERE id = ?', [id]);
|
|
||||||
|
|
||||||
// Verify the deletion
|
|
||||||
const verifyAfterDelete = await db.all(`
|
|
||||||
SELECT
|
|
||||||
(SELECT COUNT(*) FROM users WHERE id = ?) as user_exists,
|
|
||||||
(SELECT COUNT(*) FROM users WHERE email = ?) as email_exists,
|
|
||||||
(SELECT COUNT(*) FROM users WHERE email = ? AND is_active = 1) as active_email_exists
|
|
||||||
`, [id, existingEmployee.email, existingEmployee.email]);
|
|
||||||
|
|
||||||
console.log('🔍 Verification after delete:', verifyAfterDelete[0]);
|
|
||||||
|
|
||||||
// Verify the deletion worked
|
|
||||||
const verifyDeletion = await db.all<{user_count: number, avail_count: number, shift_refs: number}>(`
|
|
||||||
SELECT
|
|
||||||
(SELECT COUNT(*) FROM users WHERE id = ?) as user_count,
|
|
||||||
(SELECT COUNT(*) FROM employee_availabilities WHERE employee_id = ?) as avail_count,
|
|
||||||
(SELECT COUNT(*) FROM assigned_shifts WHERE json_extract(assigned_employees, '$') LIKE ?) as shift_refs
|
|
||||||
`, [id, id, `%${id}%`]);
|
|
||||||
|
|
||||||
const counts = verifyDeletion[0];
|
|
||||||
if (!counts || counts.user_count > 0 || counts.avail_count > 0 || counts.shift_refs > 0) {
|
|
||||||
console.error('Deletion verification failed:', counts);
|
|
||||||
await db.run('ROLLBACK');
|
|
||||||
throw new Error('Failed to delete all user data');
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got here, the deletion was successful
|
|
||||||
await db.run('COMMIT');
|
|
||||||
console.log('✅ Deletion committed successfully');
|
|
||||||
|
|
||||||
// Final verification after commit
|
|
||||||
const finalCheck = await db.get('SELECT * FROM users WHERE email = ?', [existingEmployee.email]);
|
|
||||||
console.log('🔍 Final verification - any user with this email:', finalCheck);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error during deletion transaction:', err);
|
|
||||||
await db.run('ROLLBACK');
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(204).send();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting employee:', error);
|
console.error('Error deleting employee:', error);
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
@@ -344,7 +255,14 @@ export const getAvailabilities = async (req: AuthRequest, res: Response): Promis
|
|||||||
export const updateAvailabilities = async (req: AuthRequest, res: Response): Promise<void> => {
|
export const updateAvailabilities = async (req: AuthRequest, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { employeeId } = req.params;
|
const { employeeId } = req.params;
|
||||||
const availabilities = req.body;
|
const availabilities = req.body as Array<{
|
||||||
|
id?: string;
|
||||||
|
employeeId: string;
|
||||||
|
dayOfWeek: number;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
isAvailable: boolean;
|
||||||
|
}>;
|
||||||
|
|
||||||
// Check if employee exists
|
// Check if employee exists
|
||||||
const existingEmployee = await db.get('SELECT id FROM users WHERE id = ?', [employeeId]);
|
const existingEmployee = await db.get('SELECT id FROM users WHERE id = ?', [employeeId]);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Request, Response } from 'express';
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { db } from '../services/databaseService.js';
|
import { db } from '../services/databaseService.js';
|
||||||
import { ShiftTemplate, CreateShiftTemplateRequest, UpdateShiftTemplateRequest } from '../models/ShiftTemplate.js';
|
import { ShiftTemplate, CreateShiftTemplateRequest, UpdateShiftTemplateRequest } from '../models/ShiftTemplate.js';
|
||||||
|
import { AuthRequest } from '../middleware/auth.js';
|
||||||
|
|
||||||
export const getTemplates = async (req: Request, res: Response): Promise<void> => {
|
export const getTemplates = async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
@@ -140,7 +141,7 @@ export const createDefaultTemplate = async (userId: string): Promise<string> =>
|
|||||||
export const createTemplate = async (req: Request, res: Response): Promise<void> => {
|
export const createTemplate = async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { name, description, isDefault, shifts }: CreateShiftTemplateRequest = req.body;
|
const { name, description, isDefault, shifts }: CreateShiftTemplateRequest = req.body;
|
||||||
const userId = (req as any).user?.userId; // From auth middleware
|
const userId = (req as AuthRequest).user?.userId;
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
res.status(401).json({ error: 'Unauthorized' });
|
res.status(401).json({ error: 'Unauthorized' });
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// backend/src/middleware/auth.ts
|
// backend/src/middleware/auth.ts
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { JWTPayload } from '../controllers/authController.js';
|
||||||
|
|
||||||
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
|
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
|
||||||
|
|
||||||
@@ -21,8 +22,12 @@ export const authMiddleware = (req: AuthRequest, res: Response, next: NextFuncti
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(token, JWT_SECRET) as any;
|
const decoded = jwt.verify(token, JWT_SECRET) as JWTPayload;
|
||||||
req.user = decoded;
|
req.user = {
|
||||||
|
userId: decoded.id,
|
||||||
|
email: decoded.email,
|
||||||
|
role: decoded.role
|
||||||
|
};
|
||||||
next();
|
next();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({ error: 'Invalid token.' });
|
res.status(400).json({ error: 'Invalid token.' });
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { authService } from './authService';
|
|||||||
|
|
||||||
const API_BASE = 'http://localhost:3002/api/employees';
|
const API_BASE = 'http://localhost:3002/api/employees';
|
||||||
|
|
||||||
export const employeeService = {
|
export class EmployeeService {
|
||||||
// Alle Mitarbeiter abrufen
|
// Alle Mitarbeiter abrufen
|
||||||
async getEmployees(): Promise<Employee[]> {
|
async getEmployees(): Promise<Employee[]> {
|
||||||
const response = await fetch(`${API_BASE}?_=${Date.now()}`, {
|
const response = await fetch(`${API_BASE}?_=${Date.now()}`, {
|
||||||
@@ -21,7 +21,7 @@ export const employeeService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json();
|
||||||
},
|
}
|
||||||
|
|
||||||
// Einzelnen Mitarbeiter abrufen
|
// Einzelnen Mitarbeiter abrufen
|
||||||
async getEmployee(id: string): Promise<Employee> {
|
async getEmployee(id: string): Promise<Employee> {
|
||||||
@@ -37,7 +37,7 @@ export const employeeService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json();
|
||||||
},
|
}
|
||||||
|
|
||||||
// Neuen Mitarbeiter erstellen
|
// Neuen Mitarbeiter erstellen
|
||||||
async createEmployee(employeeData: CreateEmployeeRequest): Promise<Employee> {
|
async createEmployee(employeeData: CreateEmployeeRequest): Promise<Employee> {
|
||||||
@@ -56,7 +56,7 @@ export const employeeService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json();
|
||||||
},
|
}
|
||||||
|
|
||||||
// Mitarbeiter aktualisieren
|
// Mitarbeiter aktualisieren
|
||||||
async updateEmployee(id: string, updates: UpdateEmployeeRequest): Promise<Employee> {
|
async updateEmployee(id: string, updates: UpdateEmployeeRequest): Promise<Employee> {
|
||||||
@@ -74,7 +74,7 @@ export const employeeService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json();
|
||||||
},
|
}
|
||||||
|
|
||||||
// Mitarbeiter permanent löschen
|
// Mitarbeiter permanent löschen
|
||||||
async deleteEmployee(id: string): Promise<void> {
|
async deleteEmployee(id: string): Promise<void> {
|
||||||
@@ -89,7 +89,7 @@ export const employeeService = {
|
|||||||
const error = await response.json().catch(() => ({ error: 'Fehler beim Löschen des Mitarbeiters' }));
|
const error = await response.json().catch(() => ({ error: 'Fehler beim Löschen des Mitarbeiters' }));
|
||||||
throw new Error(error.error || 'Fehler beim Löschen des Mitarbeiters');
|
throw new Error(error.error || 'Fehler beim Löschen des Mitarbeiters');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// Verfügbarkeiten abrufen
|
// Verfügbarkeiten abrufen
|
||||||
async getAvailabilities(employeeId: string): Promise<Availability[]> {
|
async getAvailabilities(employeeId: string): Promise<Availability[]> {
|
||||||
@@ -105,7 +105,7 @@ export const employeeService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return response.json();
|
return response.json();
|
||||||
},
|
}
|
||||||
|
|
||||||
// Verfügbarkeiten aktualisieren
|
// Verfügbarkeiten aktualisieren
|
||||||
async updateAvailabilities(employeeId: string, availabilities: Availability[]): Promise<Availability[]> {
|
async updateAvailabilities(employeeId: string, availabilities: Availability[]): Promise<Availability[]> {
|
||||||
|
|||||||
Reference in New Issue
Block a user