175 lines
5.8 KiB
TypeScript
175 lines
5.8 KiB
TypeScript
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<typeof authUserSchema>;
|
|
|
|
/** 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<typeof loginSchema>;
|
|
|
|
/** 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<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>;
|
|
|
|
/** 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(),
|
|
configuration: z.array(z.object({
|
|
key: z.string(), label: z.string(), description: z.string(),
|
|
type: z.enum(['text', 'url', 'boolean']), secret: z.boolean(), required: z.boolean(),
|
|
defaultValue: z.string().optional(), services: z.array(z.string()),
|
|
})),
|
|
configurationReady: z.boolean(),
|
|
});
|
|
export type Module = z.infer<typeof moduleSchema>;
|
|
|
|
export const moduleConfigurationSchema = z.object({
|
|
ready: z.boolean(),
|
|
fields: z.array(z.object({
|
|
key: z.string(), label: z.string(), description: z.string(),
|
|
type: z.enum(['text', 'url', 'boolean']), secret: z.boolean(), required: z.boolean(),
|
|
services: z.array(z.string()), isSet: z.boolean(), value: z.string().optional(),
|
|
})),
|
|
});
|
|
export type ModuleConfiguration = z.infer<typeof moduleConfigurationSchema>;
|
|
|
|
/** Modul-Kachel für das Dashboard (/api/v1/profile/modules). */
|
|
export const accessibleModuleSchema = z.object({
|
|
id: z.string(),
|
|
moduleId: z.string(),
|
|
name: z.string(),
|
|
slug: z.string(),
|
|
description: z.string(),
|
|
status: z.enum(MODULE_STATUSES),
|
|
});
|
|
export type AccessibleModule = z.infer<typeof accessibleModuleSchema>;
|
|
|
|
/** Modul-Berechtigung eines Benutzers. */
|
|
export const modulePermissionSchema = z.object({
|
|
moduleId: z.string(),
|
|
moduleSlug: z.string(),
|
|
moduleName: z.string(),
|
|
permission: z.enum(['GRANTED', 'DENIED']),
|
|
});
|
|
export type ModulePermission = z.infer<typeof modulePermissionSchema>;
|