Passwort-Komplexität + Portal-Credentials-UX

validatePasswordComplexity (12 Zeichen, Groß/Klein/Zahl/Sonderzeichen)
zentral in passwordGenerator.ts; jetzt erzwungen in setPortalPassword,
confirmPasswordReset, register, createUser, updateUser.

Neue Endpoints:
- POST /customers/:id/portal/password/generate → 16-Zeichen Zufallspasswort
- POST /customers/:id/portal/send-credentials → Versand per Mail
  (nur wenn portalEnabled aktiv)

Frontend (CustomerDetail): Generate-Button vor Setzen, Send-Credentials
nach gesetztem Passwort, Live-Komplexitäts-Hint (✓/○) während Eingabe,
alert() durch Toast-Notifications ersetzt.

Live-verifiziert: schwaches Passwort → 400 mit Detail-Fehler, komplexes
Passwort → 200, Generator liefert 16-Zeichen-Passwort.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-16 18:26:11 +02:00
parent 6af1a4bbd4
commit 8a5ffbb563
8 changed files with 353 additions and 10 deletions
+8
View File
@@ -168,6 +168,14 @@ export const customerApi = {
const res = await api.get<ApiResponse<{ password: string | null }>>(`/customers/${customerId}/portal/password`);
return res.data;
},
generatePortalPassword: async (customerId: number) => {
const res = await api.post<ApiResponse<{ password: string }>>(`/customers/${customerId}/portal/password/generate`);
return res.data;
},
sendPortalCredentials: async (customerId: number) => {
const res = await api.post<ApiResponse<void>>(`/customers/${customerId}/portal/send-credentials`);
return res.data;
},
// Vertreter-Verwaltung
getRepresentatives: async (customerId: number) => {
const res = await api.get<ApiResponse<CustomerRepresentative[]>>(`/customers/${customerId}/representatives`);