mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-21 07:36:40 +00:00
feat(notifications): add structured category enum to dispatcher and history (#774)
Introduce a NotificationCategory string-literal union (11 values) and thread it through dispatchAlert as a required second argument. All callers (DockerEventService, AutoHealService, ImageUpdateService, MonitorService, PolicyEnforcement, policyGate, SchedulerService, imageUpdates route) pass an explicit category at every call site, giving TypeScript compile-time enforcement that no new emit site can be added without choosing a category. DatabaseService gains an idempotent migration that adds a nullable category TEXT column to notification_history; existing rows keep category=NULL (displayed as Uncategorized in the UI). The getNotificationHistory method accepts an optional category filter that is forwarded from the GET /api/notifications/history route via a ?category= query param. NotificationPanel gains a category Select dropdown so users can filter history by category. The frontend types mirror the backend union so API responses are type-safe end-to-end. All 75 test files (1410 tests) updated to the new 4-arg dispatchAlert signature and passing.
This commit is contained in:
@@ -284,7 +284,7 @@ autoUpdateRouter.post('/execute', authMiddleware, async (req: Request, res: Resp
|
||||
);
|
||||
if (!autoUpdateGate.ok) {
|
||||
const blockedMsg = `Policy "${autoUpdateGate.policy?.name}" blocked auto-update: ${autoUpdateGate.violations.length} image(s) exceed ${autoUpdateGate.policy?.max_severity}`;
|
||||
NotificationService.getInstance().dispatchAlert('warning', blockedMsg, stackName);
|
||||
NotificationService.getInstance().dispatchAlert('warning', 'scan_finding', blockedMsg, { stackName });
|
||||
results.push(`Stack "${stackName}": ${blockedMsg}`);
|
||||
continue;
|
||||
}
|
||||
@@ -294,8 +294,9 @@ autoUpdateRouter.post('/execute', authMiddleware, async (req: Request, res: Resp
|
||||
|
||||
NotificationService.getInstance().dispatchAlert(
|
||||
'info',
|
||||
'image_update_applied',
|
||||
`Auto-update: stack "${stackName}" updated with new images`,
|
||||
stackName,
|
||||
{ stackName },
|
||||
);
|
||||
|
||||
results.push(`Stack "${stackName}": updated (${updatedImages.join(', ')}).`);
|
||||
|
||||
@@ -25,7 +25,8 @@ export const notificationsRouter = Router();
|
||||
notificationsRouter.get('/', authMiddleware, async (req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
const nodeId = req.nodeId ?? 0;
|
||||
const history = DatabaseService.getInstance().getNotificationHistory(nodeId);
|
||||
const category = typeof req.query.category === 'string' ? req.query.category : undefined;
|
||||
const history = DatabaseService.getInstance().getNotificationHistory(nodeId, 50, category);
|
||||
res.json(history);
|
||||
} catch {
|
||||
res.status(500).json({ error: 'Failed to fetch notifications' });
|
||||
|
||||
Reference in New Issue
Block a user