refine vehicle freatures
This commit is contained in:
318
backend/src/controllers/equipment.controller.ts
Normal file
318
backend/src/controllers/equipment.controller.ts
Normal file
@@ -0,0 +1,318 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { z } from 'zod';
|
||||
import equipmentService from '../services/equipment.service';
|
||||
import { AusruestungStatus } from '../models/equipment.model';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
// ── UUID validation ───────────────────────────────────────────────────────────
|
||||
|
||||
function isValidUUID(s: string): boolean {
|
||||
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);
|
||||
}
|
||||
|
||||
// ── Zod Validation Schemas ────────────────────────────────────────────────────
|
||||
|
||||
const AusruestungStatusEnum = z.enum([
|
||||
AusruestungStatus.Einsatzbereit,
|
||||
AusruestungStatus.Beschaedigt,
|
||||
AusruestungStatus.InWartung,
|
||||
AusruestungStatus.AusserDienst,
|
||||
]);
|
||||
|
||||
const isoDate = z.string().regex(
|
||||
/^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/,
|
||||
'Erwartet ISO-Datum im Format YYYY-MM-DD'
|
||||
);
|
||||
|
||||
const uuidString = z.string().regex(
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
|
||||
'Ungültige UUID'
|
||||
);
|
||||
|
||||
const CreateAusruestungSchema = z.object({
|
||||
bezeichnung: z.string().min(1).max(200),
|
||||
kategorie_id: uuidString,
|
||||
seriennummer: z.string().max(100).optional(),
|
||||
inventarnummer: z.string().max(50).optional(),
|
||||
hersteller: z.string().max(150).optional(),
|
||||
baujahr: z.number().int().min(1950).max(2100).optional(),
|
||||
status: AusruestungStatusEnum.optional(),
|
||||
status_bemerkung: z.string().max(2000).optional(),
|
||||
ist_wichtig: z.boolean().optional(),
|
||||
fahrzeug_id: uuidString.optional(),
|
||||
standort: z.string().min(1).max(150).optional(),
|
||||
pruef_intervall_monate: z.number().int().min(1).optional(),
|
||||
letzte_pruefung_am: isoDate.optional(),
|
||||
naechste_pruefung_am: isoDate.optional(),
|
||||
bemerkung: z.string().max(2000).optional(),
|
||||
});
|
||||
|
||||
const UpdateAusruestungSchema = z.object({
|
||||
bezeichnung: z.string().min(1).max(200).optional(),
|
||||
kategorie_id: uuidString.optional(),
|
||||
seriennummer: z.string().max(100).nullable().optional(),
|
||||
inventarnummer: z.string().max(50).nullable().optional(),
|
||||
hersteller: z.string().max(150).nullable().optional(),
|
||||
baujahr: z.number().int().min(1950).max(2100).nullable().optional(),
|
||||
status: AusruestungStatusEnum.optional(),
|
||||
status_bemerkung: z.string().max(2000).nullable().optional(),
|
||||
ist_wichtig: z.boolean().optional(),
|
||||
fahrzeug_id: uuidString.nullable().optional(),
|
||||
standort: z.string().min(1).max(150).optional(),
|
||||
pruef_intervall_monate: z.number().int().min(1).nullable().optional(),
|
||||
letzte_pruefung_am: isoDate.nullable().optional(),
|
||||
naechste_pruefung_am: isoDate.nullable().optional(),
|
||||
bemerkung: z.string().max(2000).nullable().optional(),
|
||||
});
|
||||
|
||||
const UpdateStatusSchema = z.object({
|
||||
status: AusruestungStatusEnum,
|
||||
bemerkung: z.string().max(2000).optional().default(''),
|
||||
});
|
||||
|
||||
const CreateWartungslogSchema = z.object({
|
||||
datum: isoDate,
|
||||
art: z.enum(['Prüfung', 'Reparatur', 'Sonstiges']),
|
||||
beschreibung: z.string().min(1).max(2000),
|
||||
ergebnis: z.enum(['bestanden', 'bestanden_mit_maengeln', 'nicht_bestanden']).optional(),
|
||||
kosten: z.number().min(0).optional(),
|
||||
pruefende_stelle: z.string().max(150).optional(),
|
||||
dokument_url: z.string().url().max(500).refine(
|
||||
(url) => /^https?:\/\//i.test(url),
|
||||
'Nur http/https URLs erlaubt'
|
||||
).optional(),
|
||||
});
|
||||
|
||||
// ── Helper ────────────────────────────────────────────────────────────────────
|
||||
|
||||
function getUserId(req: Request): string {
|
||||
return req.user!.id;
|
||||
}
|
||||
|
||||
// ── Controller ────────────────────────────────────────────────────────────────
|
||||
|
||||
class EquipmentController {
|
||||
async listEquipment(_req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const equipment = await equipmentService.getAllEquipment();
|
||||
res.status(200).json({ success: true, data: equipment });
|
||||
} catch (error) {
|
||||
logger.error('listEquipment error', { error });
|
||||
res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async getEquipment(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { id } = req.params as Record<string, string>;
|
||||
if (!isValidUUID(id)) {
|
||||
res.status(400).json({ success: false, message: 'Ungültige Ausrüstungs-ID' });
|
||||
return;
|
||||
}
|
||||
const equipment = await equipmentService.getEquipmentById(id);
|
||||
if (!equipment) {
|
||||
res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ success: true, data: equipment });
|
||||
} catch (error) {
|
||||
logger.error('getEquipment error', { error, id: req.params.id });
|
||||
res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async getByVehicle(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { fahrzeugId } = req.params as Record<string, string>;
|
||||
if (!isValidUUID(fahrzeugId)) {
|
||||
res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
|
||||
return;
|
||||
}
|
||||
const equipment = await equipmentService.getEquipmentByVehicle(fahrzeugId);
|
||||
res.status(200).json({ success: true, data: equipment });
|
||||
} catch (error) {
|
||||
logger.error('getByVehicle error', { error, fahrzeugId: req.params.fahrzeugId });
|
||||
res.status(500).json({ success: false, message: 'Ausrüstung für Fahrzeug konnte nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async getCategories(_req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const categories = await equipmentService.getCategories();
|
||||
res.status(200).json({ success: true, data: categories });
|
||||
} catch (error) {
|
||||
logger.error('getCategories error', { error });
|
||||
res.status(500).json({ success: false, message: 'Kategorien konnten nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async getStats(_req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const stats = await equipmentService.getEquipmentStats();
|
||||
res.status(200).json({ success: true, data: stats });
|
||||
} catch (error) {
|
||||
logger.error('getStats error', { error });
|
||||
res.status(500).json({ success: false, message: 'Statistiken konnten nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async getAlerts(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const raw = parseInt((req.query.daysAhead as string) || '30', 10);
|
||||
if (isNaN(raw) || raw < 0) {
|
||||
res.status(400).json({ success: false, message: 'Ungültiger daysAhead-Wert' });
|
||||
return;
|
||||
}
|
||||
const daysAhead = Math.min(raw, 365);
|
||||
const alerts = await equipmentService.getUpcomingInspections(daysAhead);
|
||||
res.status(200).json({ success: true, data: alerts });
|
||||
} catch (error) {
|
||||
logger.error('getAlerts error', { error });
|
||||
res.status(500).json({ success: false, message: 'Prüfungshinweise konnten nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async getVehicleWarnings(_req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const warnings = await equipmentService.getVehicleWarnings();
|
||||
res.status(200).json({ success: true, data: warnings });
|
||||
} catch (error) {
|
||||
logger.error('getVehicleWarnings error', { error });
|
||||
res.status(500).json({ success: false, message: 'Fahrzeug-Warnungen konnten nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async createEquipment(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const parsed = CreateAusruestungSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: 'Validierungsfehler',
|
||||
errors: parsed.error.flatten().fieldErrors,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const equipment = await equipmentService.createEquipment(parsed.data, getUserId(req));
|
||||
res.status(201).json({ success: true, data: equipment });
|
||||
} catch (error) {
|
||||
logger.error('createEquipment error', { error });
|
||||
res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht erstellt werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateEquipment(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { id } = req.params as Record<string, string>;
|
||||
if (!isValidUUID(id)) {
|
||||
res.status(400).json({ success: false, message: 'Ungültige Ausrüstungs-ID' });
|
||||
return;
|
||||
}
|
||||
const parsed = UpdateAusruestungSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: 'Validierungsfehler',
|
||||
errors: parsed.error.flatten().fieldErrors,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (Object.keys(parsed.data).length === 0) {
|
||||
res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
|
||||
return;
|
||||
}
|
||||
const equipment = await equipmentService.updateEquipment(id, parsed.data, getUserId(req));
|
||||
if (!equipment) {
|
||||
res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ success: true, data: equipment });
|
||||
} catch (error: any) {
|
||||
if (error?.message === 'No fields to update') {
|
||||
res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
|
||||
return;
|
||||
}
|
||||
logger.error('updateEquipment error', { error, id: req.params.id });
|
||||
res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht aktualisiert werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async updateStatus(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { id } = req.params as Record<string, string>;
|
||||
if (!isValidUUID(id)) {
|
||||
res.status(400).json({ success: false, message: 'Ungültige Ausrüstungs-ID' });
|
||||
return;
|
||||
}
|
||||
const parsed = UpdateStatusSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: 'Validierungsfehler',
|
||||
errors: parsed.error.flatten().fieldErrors,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await equipmentService.updateStatus(
|
||||
id, parsed.data.status, parsed.data.bemerkung, getUserId(req)
|
||||
);
|
||||
res.status(200).json({ success: true, message: 'Status aktualisiert' });
|
||||
} catch (error: any) {
|
||||
if (error?.message === 'Equipment not found') {
|
||||
res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
logger.error('updateStatus error', { error, id: req.params.id });
|
||||
res.status(500).json({ success: false, message: 'Status konnte nicht aktualisiert werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async deleteEquipment(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { id } = req.params as Record<string, string>;
|
||||
if (!isValidUUID(id)) {
|
||||
res.status(400).json({ success: false, message: 'Ungültige Ausrüstungs-ID' });
|
||||
return;
|
||||
}
|
||||
const deleted = await equipmentService.deleteEquipment(id, getUserId(req));
|
||||
if (!deleted) {
|
||||
res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ success: true, message: 'Ausrüstung gelöscht' });
|
||||
} catch (error) {
|
||||
logger.error('deleteEquipment error', { error, id: req.params.id });
|
||||
res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht gelöscht werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async addWartung(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { id } = req.params as Record<string, string>;
|
||||
if (!isValidUUID(id)) {
|
||||
res.status(400).json({ success: false, message: 'Ungültige Ausrüstungs-ID' });
|
||||
return;
|
||||
}
|
||||
const parsed = CreateWartungslogSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: 'Validierungsfehler',
|
||||
errors: parsed.error.flatten().fieldErrors,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const entry = await equipmentService.addWartungslog(id, parsed.data, getUserId(req));
|
||||
res.status(201).json({ success: true, data: entry });
|
||||
} catch (error: any) {
|
||||
if (error?.message === 'Equipment not found') {
|
||||
res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
logger.error('addWartung error', { error, id: req.params.id });
|
||||
res.status(500).json({ success: false, message: 'Wartungseintrag konnte nicht gespeichert werden' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new EquipmentController();
|
||||
Reference in New Issue
Block a user