feat: Phase 2 – Benutzerverwaltung (CRUD-API, Profil, UI)
This commit is contained in:
@@ -34,4 +34,76 @@ export const healthSchema = z.object({
|
||||
}),
|
||||
}),
|
||||
});
|
||||
export type Health = z.infer<typeof healthSchema>;
|
||||
export type Health = z.infer<typeof healthSchema>;
|
||||
|
||||
/** Benutzer-Datensatz der Admin-API (/api/v1/users). */
|
||||
export const userSchema = z.object({
|
||||
id: z.string(),
|
||||
username: z.string(),
|
||||
email: z.string(),
|
||||
displayName: z.string(),
|
||||
role: z.enum(ROLE_NAMES),
|
||||
isActive: z.boolean(),
|
||||
lastLoginAt: z.string().nullable(),
|
||||
createdAt: z.string(),
|
||||
});
|
||||
export type User = z.infer<typeof userSchema>;
|
||||
|
||||
/** Profil des angemeldeten Benutzers (/api/v1/profile). */
|
||||
export const profileSchema = z.object({
|
||||
id: z.string(),
|
||||
username: z.string(),
|
||||
email: z.string(),
|
||||
displayName: z.string(),
|
||||
role: z.enum(ROLE_NAMES),
|
||||
lastLoginAt: z.string().nullable(),
|
||||
createdAt: z.string(),
|
||||
});
|
||||
export type Profile = z.infer<typeof profileSchema>;
|
||||
|
||||
/** Benutzer anlegen (Client-Validierung ist UX; Server validiert erneut). */
|
||||
export const createUserSchema = z.object({
|
||||
username: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(3, 'Benutzername muss mindestens 3 Zeichen lang sein')
|
||||
.max(100)
|
||||
.regex(/^[A-Za-z0-9._-]+$/, 'Nur Buchstaben, Zahlen sowie . _ - erlaubt'),
|
||||
email: z.string().trim().email('Ungültige E-Mail-Adresse'),
|
||||
displayName: z.string().trim().min(1, 'Anzeigename ist erforderlich'),
|
||||
password: z.string().min(10, 'Passwort muss mindestens 10 Zeichen lang sein'),
|
||||
role: z.enum(ROLE_NAMES),
|
||||
});
|
||||
export type CreateUserDto = z.infer<typeof createUserSchema>;
|
||||
|
||||
/** Benutzer bearbeiten. */
|
||||
export const updateUserSchema = z
|
||||
.object({
|
||||
email: z.string().trim().email('Ungültige E-Mail-Adresse').optional(),
|
||||
displayName: z.string().trim().min(1, 'Anzeigename ist erforderlich').optional(),
|
||||
role: z.enum(ROLE_NAMES).optional(),
|
||||
isActive: z.boolean().optional(),
|
||||
})
|
||||
.refine((data) => Object.values(data).some((value) => value !== undefined), {
|
||||
message: 'Mindestens ein Feld ist erforderlich',
|
||||
});
|
||||
export type UpdateUserDto = z.infer<typeof updateUserSchema>;
|
||||
|
||||
/** Passwort zurücksetzen (Admin). */
|
||||
export const resetPasswordSchema = z.object({
|
||||
newPassword: z.string().min(10, 'Passwort muss mindestens 10 Zeichen lang sein'),
|
||||
});
|
||||
export type ResetPasswordDto = z.infer<typeof resetPasswordSchema>;
|
||||
|
||||
/** Eigenes Passwort ändern. */
|
||||
export const changePasswordSchema = z
|
||||
.object({
|
||||
currentPassword: z.string().min(1, 'Aktuelles Passwort ist erforderlich'),
|
||||
newPassword: z.string().min(10, 'Passwort muss mindestens 10 Zeichen lang sein'),
|
||||
confirmPassword: z.string().min(1, 'Passwortbestätigung ist erforderlich'),
|
||||
})
|
||||
.refine((data) => data.newPassword === data.confirmPassword, {
|
||||
message: 'Passwörter stimmen nicht überein',
|
||||
path: ['confirmPassword'],
|
||||
});
|
||||
export type ChangePasswordDto = z.infer<typeof changePasswordSchema>;
|
||||
Reference in New Issue
Block a user