feat: notifications in the app and, optionally, by e-mail

Derived from events and kept per person: whoever a ticket is assigned to,
whoever reports or works on it when it moves, fails QA or gets a comment,
and whoever joins a project. Never about what you did yourself, once per
event. They arrive live through a subscription to a bell in the header,
and are copied by e-mail to whoever turns it on when SMTP_URL is set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 09:19:59 +02:00
parent 0b65c1c912
commit efa06a0723
60 changed files with 3406 additions and 28 deletions

View File

@@ -16,6 +16,27 @@ import { ReadAccess } from './queries/read-access';
import { ReleasesService } from './releases.service';
import { SAGAS } from './sagas/sagas';
import { WorkflowsService } from './workflows.service';
import { NotificationsService } from './notifications.service';
import { Notifier } from './notifications/notifier';
import { NotificationsEventHandler } from './notifications/notifications.event-handler';
import {
GetMyNotificationSettingsQueryHandler,
GetMyNotificationsQueryHandler,
} from './notifications/notifications.queries';
import {
MarkNotificationsReadCommandHandler,
UpdateNotificationSettingsCommandHandler,
} from './notifications/notifications.commands';
/** Notifications: derived from events, kept and dismissed per person. */
const NOTIFICATIONS = [
Notifier,
NotificationsEventHandler,
GetMyNotificationsQueryHandler,
GetMyNotificationSettingsQueryHandler,
MarkNotificationsReadCommandHandler,
UpdateNotificationSettingsCommandHandler,
];
const SERVICES = [
ProjectsService,
@@ -24,6 +45,7 @@ const SERVICES = [
ReleasesService,
ChangesService,
WorkflowsService,
NotificationsService,
];
/**
@@ -43,6 +65,7 @@ const SERVICES = [
...QUERY_HANDLERS,
...EVENT_HANDLERS,
...SAGAS,
...NOTIFICATIONS,
],
exports: SERVICES,
})