Files
Schichtenplaner/backend/src/controllers/authController.ts
2025-10-20 01:50:12 +02:00

235 lines
7.2 KiB
TypeScript

// backend/src/controllers/authController.ts
import { v4 as uuidv4 } from 'uuid';
import { Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import { db } from '../services/databaseService.js';
import { AuthRequest } from '../middleware/auth.js';
import { Employee, EmployeeWithPassword } from '../models/Employee.js';
export interface User {
id: number;
email: string;
name: string;
role: string;
}
export interface UserWithPassword extends User {
password: string;
}
export interface LoginRequest {
email: string;
password: string;
}
export interface JWTPayload {
id: string; // ← VON number ZU string ÄNDERN
email: string;
role: string;
iat?: number;
exp?: number;
}
export interface RegisterRequest {
// REMOVED: email - will be auto-generated
password: string;
firstname: string;
lastname: string;
role?: string;
}
function generateEmail(firstname: string, lastname: string): string {
const cleanFirstname = firstname.toLowerCase().replace(/[^a-z0-9]/g, '');
const cleanLastname = lastname.toLowerCase().replace(/[^a-z0-9]/g, '');
return `${cleanFirstname}.${cleanLastname}@sp.de`;
}
export const login = async (req: Request, res: Response) => {
try {
const { email, password } = req.body as LoginRequest;
console.log('🔐 Login attempt for email:', email);
if (!email || !password) {
console.log('❌ Missing email or password');
return res.status(400).json({ error: 'E-Mail und Passwort sind erforderlich' });
}
// Get user from database
const user = await db.get<EmployeeWithPassword>(
'SELECT id, email, password, firstname, lastname, role, employee_type as employeeType, contract_type as contractType, can_work_alone as canWorkAlone, is_active as isActive FROM employees WHERE email = ? AND is_active = 1',
[email]
);
console.log('🔍 User found:', user ? 'Yes' : 'No');
if (!user) {
console.log('❌ No user found with email:', email);
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
}
// Verify password
const validPassword = await bcrypt.compare(password, user.password);
console.log('🔑 Password valid:', validPassword);
if (!validPassword) {
console.log('❌ Invalid password for user:', email);
return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
}
// Create token payload - KORREKT: id field verwenden
const tokenPayload = {
id: user.id.toString(), // ← WICHTIG: Dies wird als 'id' im JWT gespeichert
email: user.email,
role: user.role
};
console.log('🎫 Creating JWT with payload:', tokenPayload);
// Create token
const token = jwt.sign(
tokenPayload,
process.env.JWT_SECRET || 'your-secret-key',
{ expiresIn: '24h' }
);
// Remove password from user object
const { password: _, ...userWithoutPassword } = user;
console.log('✅ Login successful for:', user.email);
res.json({
user: userWithoutPassword,
token
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: 'Ein Fehler ist beim Login aufgetreten' });
}
};
export const getCurrentUser = async (req: Request, res: Response) => {
try {
const authReq = req as AuthRequest;
const jwtUser = authReq.user;
console.log('🔍 Getting current user for ID:', jwtUser?.userId);
if (!jwtUser?.userId) {
console.log('❌ No user ID in JWT');
return res.status(401).json({ error: 'Nicht authentifiziert' });
}
const user = await db.get<Employee>(
'SELECT id, email, firstname, lastname, role, employee_type as employeeType, contract_type as contractType, can_work_alone as canWorkAlone, is_active as isActive FROM employees WHERE id = ? AND is_active = 1',
[jwtUser.userId]
);
console.log('🔍 User found in database:', user ? 'Yes' : 'No');
if (!user) {
console.log('❌ User not found in database for ID:', jwtUser.userId);
return res.status(404).json({ error: 'Benutzer nicht gefunden' });
}
console.log('✅ Returning user:', user.email);
res.json({ user });
} catch (error) {
console.error('Get current user error:', error);
res.status(500).json({ error: 'Ein Fehler ist beim Abrufen des Benutzers aufgetreten' });
}
};
export const validateToken = async (req: Request, res: Response) => {
try {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Kein Token vorhanden' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key') as JWTPayload;
// Verify that the decoded token has the required fields
if (!decoded.id || !decoded.email || !decoded.role) {
throw new Error('Invalid token structure');
}
res.json({ valid: true, user: decoded });
} catch (jwtError) {
return res.status(401).json({ valid: false, error: 'Ungültiger Token' });
}
} catch (error) {
console.error('Token validation error:', error);
res.status(500).json({ valid: false, error: 'Fehler bei der Token-Validierung' });
}
};
export const register = async (req: Request, res: Response) => {
try {
const { password, firstname, lastname, role = 'user' } = req.body as RegisterRequest;
// Validate required fields - REMOVED email
if (!password || !firstname || !lastname) {
return res.status(400).json({
error: 'Password, firstname und lastname sind erforderlich'
});
}
// Generate email automatically
const email = generateEmail(firstname, lastname);
// Check if generated email already exists
const existingUser = await db.get<Employee>(
'SELECT id FROM employees WHERE email = ?',
[email]
);
if (existingUser) {
return res.status(400).json({
error: `Ein Benutzer mit der E-Mail ${email} existiert bereits`
});
}
// Hash password and create user
const hashedPassword = await bcrypt.hash(password, 10);
// Insert user with generated email
const result = await db.run(
`INSERT INTO employees (id, email, password, firstname, lastname, role, employee_type, contract_type, can_work_alone, is_active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[uuidv4(), email, hashedPassword, firstname, lastname, role, 'experienced', 'small', false, 1]
);
if (!result.lastID) {
throw new Error('Benutzer konnte nicht erstellt werden');
}
// Get created user
const newUser = await db.get<Employee>(
'SELECT id, email, firstname, lastname, role FROM employees WHERE id = ?',
[result.lastID]
);
res.status(201).json({ user: newUser });
} catch (error) {
console.error('Registration error:', error);
res.status(500).json({
error: 'Fehler bei der Registrierung'
});
}
};
export const logout = async (req: Request, res: Response) => {
try {
// Note: Since we're using JWTs, we don't need to do anything server-side
// The client should remove the token from storage
res.json({ message: 'Erfolgreich abgemeldet' });
} catch (error) {
console.error('Logout error:', error);
res.status(500).json({
error: 'Fehler beim Abmelden'
});
}
};