import { Request, Response } from 'express'; import * as authService from '../services/auth.service.js'; import { AuthRequest, ApiResponse } from '../types/index.js'; import prisma from '../lib/prisma.js'; // Mitarbeiter-Login export async function login(req: Request, res: Response): Promise { try { const { email, password } = req.body; if (!email || !password) { res.status(400).json({ success: false, error: 'E-Mail und Passwort erforderlich', } as ApiResponse); return; } const result = await authService.login(email, password); res.json({ success: true, data: result } as ApiResponse); } catch (error) { res.status(401).json({ success: false, error: error instanceof Error ? error.message : 'Anmeldung fehlgeschlagen', } as ApiResponse); } } // Kundenportal-Login export async function customerLogin(req: Request, res: Response): Promise { try { const { email, password } = req.body; if (!email || !password) { res.status(400).json({ success: false, error: 'E-Mail und Passwort erforderlich', } as ApiResponse); return; } const result = await authService.customerLogin(email, password); res.json({ success: true, data: result } as ApiResponse); } catch (error) { res.status(401).json({ success: false, error: error instanceof Error ? error.message : 'Anmeldung fehlgeschlagen', } as ApiResponse); } } export async function me(req: AuthRequest, res: Response): Promise { try { if (!req.user) { res.status(401).json({ success: false, error: 'Nicht authentifiziert', } as ApiResponse); return; } // Kundenportal-Login if (req.user.isCustomerPortal && req.user.customerId) { const customer = await authService.getCustomerPortalUser(req.user.customerId); if (!customer) { res.status(404).json({ success: false, error: 'Kunde nicht gefunden', } as ApiResponse); return; } res.json({ success: true, data: customer } as ApiResponse); return; } // Mitarbeiter-Login if (!req.user.userId) { res.status(401).json({ success: false, error: 'Ungültige Authentifizierung', } as ApiResponse); return; } const user = await authService.getUserById(req.user.userId); if (!user) { res.status(404).json({ success: false, error: 'Benutzer nicht gefunden', } as ApiResponse); return; } res.json({ success: true, data: user } as ApiResponse); } catch (error) { res.status(500).json({ success: false, error: 'Fehler beim Laden der Benutzerdaten', } as ApiResponse); } } /** * Passwort-Reset anfordern (Email + Token per Mail). * Immer 200 OK zurückgeben um Email-Existenz nicht preiszugeben (User-Enumeration-Schutz). */ export async function requestPasswordReset(req: Request, res: Response): Promise { try { const { email, userType } = req.body; // userType: 'admin' | 'portal' if (!email) { res.status(400).json({ success: false, error: 'E-Mail erforderlich' } as ApiResponse); return; } await authService.requestPasswordReset(email, userType === 'portal' ? 'portal' : 'admin'); // IMMER success senden, damit Angreifer nicht herausfinden kann welche Emails existieren res.json({ success: true, message: 'Wenn ein Konto mit dieser E-Mail existiert, wurde ein Link zum Zurücksetzen gesendet.', } as ApiResponse); } catch (error) { console.error('Password reset request error:', error); // Auch bei Fehlern dieselbe Antwort res.json({ success: true, message: 'Wenn ein Konto mit dieser E-Mail existiert, wurde ein Link zum Zurücksetzen gesendet.', } as ApiResponse); } } /** * Passwort-Reset bestätigen (Token + neues Passwort). */ export async function confirmPasswordReset(req: Request, res: Response): Promise { try { const { token, password } = req.body; if (!token || !password) { res.status(400).json({ success: false, error: 'Token und neues Passwort erforderlich', } as ApiResponse); return; } if (password.length < 6) { res.status(400).json({ success: false, error: 'Das Passwort muss mindestens 6 Zeichen lang sein', } as ApiResponse); return; } await authService.confirmPasswordReset(token, password); res.json({ success: true, message: 'Passwort erfolgreich zurückgesetzt. Du kannst dich jetzt einloggen.', } as ApiResponse); } catch (error) { res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Passwort-Reset fehlgeschlagen', } as ApiResponse); } } /** * Logout: invalidiert den aktuellen JWT serverseitig durch Setzen von * tokenInvalidatedAt / portalTokenInvalidatedAt auf jetzt. Auth-Middleware * prüft dieses Feld und lehnt Tokens ab, deren `iat` davor liegt. * * Hinweis: Da JWTs stateless sind, gibt es keine echte Token-Revocation * ohne dieses Pattern. Logout invalidiert ALLE aktiven Sessions des Users * (auch andere Geräte) – akzeptabel für ein Sicherheits-Logout. */ export async function logout(req: AuthRequest, res: Response): Promise { try { const user = req.user as any; if (!user) { res.json({ success: true, message: 'Bereits abgemeldet' } as ApiResponse); return; } if (user.isCustomerPortal && user.customerId) { await prisma.customer.update({ where: { id: user.customerId }, data: { portalTokenInvalidatedAt: new Date() }, }); } else if (user.userId) { await prisma.user.update({ where: { id: user.userId }, data: { tokenInvalidatedAt: new Date() }, }); } res.json({ success: true, message: 'Abgemeldet' } as ApiResponse); } catch (error) { res.status(500).json({ success: false, error: 'Fehler beim Abmelden', } as ApiResponse); } } export async function register(req: Request, res: Response): Promise { try { const { email, password, firstName, lastName, roleIds } = req.body; if (!email || !password || !firstName || !lastName) { res.status(400).json({ success: false, error: 'Alle Pflichtfelder müssen ausgefüllt sein', } as ApiResponse); return; } const user = await authService.createUser({ email, password, firstName, lastName, roleIds: roleIds || [2], // Default to employee role }); res.status(201).json({ success: true, data: user } as ApiResponse); } catch (error) { res.status(400).json({ success: false, error: error instanceof Error ? error.message : 'Benutzer konnte nicht erstellt werden', } as ApiResponse); } }