new features

This commit is contained in:
Matthias Hochmeister
2026-03-23 14:01:39 +01:00
parent d2dc64d54a
commit 3326156b15
35 changed files with 1341 additions and 257 deletions

View File

@@ -1,8 +1,9 @@
/**
* Admin API Routes — Audit Log
* Admin API Routes — Audit Log + Data Cleanup
*
* GET /api/admin/audit-log — paginated, filtered list
* GET /api/admin/audit-log/export — CSV download of filtered results
* DELETE /api/admin/cleanup/:target — preview / delete old data
*
* Both endpoints require authentication + admin:access permission.
*
@@ -18,6 +19,7 @@ import { authenticate } from '../middleware/auth.middleware';
import { requirePermission } from '../middleware/rbac.middleware';
import { auditExport } from '../middleware/audit.middleware';
import auditService, { AuditAction, AuditResourceType, AuditFilters } from '../services/audit.service';
import cleanupService from '../services/cleanup.service';
import logger from '../utils/logger';
const router = Router();
@@ -213,4 +215,53 @@ router.post(
}
);
// ---------------------------------------------------------------------------
// Cleanup / Data Management endpoints
// ---------------------------------------------------------------------------
const cleanupBodySchema = z.object({
olderThanDays: z.number().int().min(1).max(3650),
confirm: z.boolean().optional().default(false),
});
type CleanupTarget = 'notifications' | 'audit-log' | 'events' | 'bookings' | 'orders' | 'vehicle-history' | 'equipment-history';
const CLEANUP_TARGETS: Record<CleanupTarget, (days: number, confirm: boolean) => Promise<{ count: number; deleted: boolean }>> = {
'notifications': (d, c) => cleanupService.cleanupNotifications(d, c),
'audit-log': (d, c) => cleanupService.cleanupAuditLog(d, c),
'events': (d, c) => cleanupService.cleanupEvents(d, c),
'bookings': (d, c) => cleanupService.cleanupBookings(d, c),
'orders': (d, c) => cleanupService.cleanupOrders(d, c),
'vehicle-history': (d, c) => cleanupService.cleanupVehicleHistory(d, c),
'equipment-history': (d, c) => cleanupService.cleanupEquipmentHistory(d, c),
};
router.delete(
'/cleanup/:target',
authenticate,
requirePermission('admin:write'),
async (req: Request, res: Response): Promise<void> => {
try {
const target = req.params.target as CleanupTarget;
const handler = CLEANUP_TARGETS[target];
if (!handler) {
res.status(400).json({ success: false, message: `Unknown cleanup target: ${target}` });
return;
}
const { olderThanDays, confirm } = cleanupBodySchema.parse(req.body);
const result = await handler(olderThanDays, confirm);
res.json({ success: true, data: result });
} catch (error) {
if (error instanceof z.ZodError) {
res.status(400).json({ success: false, message: 'Invalid parameters', errors: error.issues });
return;
}
logger.error('Cleanup failed', { error, target: req.params.target });
res.status(500).json({ success: false, message: 'Cleanup failed' });
}
}
);
export default router;

View File

@@ -2,18 +2,20 @@ import { Router } from 'express';
import equipmentController from '../controllers/equipment.controller';
import { authenticate } from '../middleware/auth.middleware';
import { requirePermission } from '../middleware/rbac.middleware';
import { uploadWartung } from '../middleware/upload';
const router = Router();
// ── Read-only (any authenticated user) ───────────────────────────────────────
router.get('/', authenticate, equipmentController.listEquipment.bind(equipmentController));
router.get('/stats', authenticate, equipmentController.getStats.bind(equipmentController));
router.get('/alerts', authenticate, equipmentController.getAlerts.bind(equipmentController));
router.get('/categories', authenticate, equipmentController.getCategories.bind(equipmentController));
router.get('/', authenticate, requirePermission('ausruestung:view'), equipmentController.listEquipment.bind(equipmentController));
router.get('/stats', authenticate, requirePermission('ausruestung:view'), equipmentController.getStats.bind(equipmentController));
router.get('/alerts', authenticate, requirePermission('ausruestung:view'), equipmentController.getAlerts.bind(equipmentController));
router.get('/categories', authenticate, requirePermission('ausruestung:view'), equipmentController.getCategories.bind(equipmentController));
router.get('/vehicle-warnings', authenticate, equipmentController.getVehicleWarnings.bind(equipmentController));
router.get('/vehicle/:fahrzeugId', authenticate, equipmentController.getByVehicle.bind(equipmentController));
router.get('/:id', authenticate, equipmentController.getEquipment.bind(equipmentController));
router.get('/:id/status-history', authenticate, equipmentController.getStatusHistory.bind(equipmentController));
// ── Write — gruppenfuehrer+ ────────────────────────────────────────────────
@@ -21,6 +23,7 @@ router.post('/', authenticate, requirePermission('ausruestung:create')
router.patch('/:id', authenticate, requirePermission('ausruestung:create'), equipmentController.updateEquipment.bind(equipmentController));
router.patch('/:id/status', authenticate, requirePermission('ausruestung:create'), equipmentController.updateStatus.bind(equipmentController));
router.post('/:id/wartung', authenticate, requirePermission('ausruestung:create'), equipmentController.addWartung.bind(equipmentController));
router.post('/wartung/:wartungId/upload', authenticate, requirePermission('ausruestung:create'), uploadWartung.single('datei'), equipmentController.uploadWartungFile.bind(equipmentController));
// ── Delete — admin only ──────────────────────────────────────────────────────

View File

@@ -2,17 +2,19 @@ import { Router } from 'express';
import vehicleController from '../controllers/vehicle.controller';
import { authenticate } from '../middleware/auth.middleware';
import { requirePermission } from '../middleware/rbac.middleware';
import { uploadWartung } from '../middleware/upload';
const router = Router();
// ── Read-only (any authenticated user) ───────────────────────────────────────
router.get('/', authenticate, vehicleController.listVehicles.bind(vehicleController));
router.get('/stats', authenticate, vehicleController.getStats.bind(vehicleController));
router.get('/alerts', authenticate, vehicleController.getAlerts.bind(vehicleController));
router.get('/', authenticate, requirePermission('fahrzeuge:view'), vehicleController.listVehicles.bind(vehicleController));
router.get('/stats', authenticate, requirePermission('fahrzeuge:view'), vehicleController.getStats.bind(vehicleController));
router.get('/alerts', authenticate, requirePermission('fahrzeuge:view'), vehicleController.getAlerts.bind(vehicleController));
router.get('/alerts/export', authenticate, requirePermission('fahrzeuge:view'), vehicleController.exportAlerts.bind(vehicleController));
router.get('/:id', authenticate, vehicleController.getVehicle.bind(vehicleController));
router.get('/:id/wartung', authenticate, vehicleController.getWartung.bind(vehicleController));
router.get('/:id/status-history', authenticate, vehicleController.getStatusHistory.bind(vehicleController));
// ── Write — kommandant+ ──────────────────────────────────────────────────────
@@ -24,5 +26,6 @@ router.delete('/:id', authenticate, requirePermission('fahrzeuge:delete'), vehic
router.patch('/:id/status', authenticate, requirePermission('fahrzeuge:change_status'), vehicleController.updateVehicleStatus.bind(vehicleController));
router.post('/:id/wartung', authenticate, requirePermission('fahrzeuge:change_status'), vehicleController.addWartung.bind(vehicleController));
router.post('/wartung/:wartungId/upload', authenticate, requirePermission('fahrzeuge:change_status'), uploadWartung.single('datei'), vehicleController.uploadWartungFile.bind(vehicleController));
export default router;