import { z } from 'zod'; /** Globale Plattform-Rollen (muss zum Backend passen). */ export const ROLE_NAMES = ['ADMIN', 'USER'] as const; export type RoleName = (typeof ROLE_NAMES)[number]; /** Öffentliche Benutzerdaten (API-Vertrag /api/v1/auth/me). */ export const authUserSchema = z.object({ id: z.string(), username: z.string(), email: z.string(), displayName: z.string(), role: z.enum(ROLE_NAMES), }); export type AuthUser = z.infer; /** Login-Anfrage (Client-Validierung ist UX; Server validiert erneut). */ export const loginSchema = z.object({ username: z.string().trim().min(1, 'Benutzername ist erforderlich'), password: z.string().min(1, 'Passwort ist erforderlich'), }); export type LoginDto = z.infer; /** Health-Antwort (API-Vertrag /api/v1/health). */ export const healthSchema = z.object({ status: z.enum(['healthy', 'unhealthy']), version: z.string(), uptimeSeconds: z.number(), components: z.object({ backend: z.enum(['healthy']), database: z.object({ status: z.enum(['healthy', 'unhealthy']), latencyMs: z.number(), }), }), }); export type Health = z.infer; /** 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; /** 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; /** 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; /** 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; /** 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; /** 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; /** Modul-Lifecycle-Zustände (muss zum Backend passen). */ export const MODULE_STATUSES = [ 'INSTALLED', 'STARTING', 'RUNNING', 'STOPPING', 'STOPPED', 'ERROR', 'DISABLED', ] as const; export type ModuleStatus = (typeof MODULE_STATUSES)[number]; /** Modul-Datensatz der Admin-API (/api/v1/modules). */ export const moduleSchema = z.object({ id: z.string(), moduleId: z.string(), name: z.string(), slug: z.string(), version: z.string(), description: z.string(), author: z.string(), status: z.enum(MODULE_STATUSES), internalPort: z.number(), healthcheckUrl: z.string(), enabled: z.boolean(), createdAt: z.string(), }); export type Module = z.infer;