Systeem-mailboxen (API en datamodel)
Feiten over de systeem-mailbox-registry: collection, velden, endpoints, scripts, environment.
Voor begrip: zie de explanation. Voor stappen: zie de how-to.
Datamodel
Collection: systemmailboxes op de admin-connectie (mongooseConnectionManager.adminConnection). Niet tenant-gescopt.
Index: unieke index op key.
Velden (zie apps/api/src/api/system-mailbox/SystemMailbox.interface.ts):
| Veld | Type | Default | Toelichting |
|---|---|---|---|
key |
string | (verplicht) | Unieke sleutel, bv. 'monitor' |
displayName |
string | '' |
Afzender-naam in From header |
azureTenantId |
string | '' |
Azure AD tenant-id van de mailbox |
mailboxAddress |
string | '' |
E-mailadres, fungeert als from |
saveToSentItems |
boolean | false |
Of Graph een kopie in Verzonden plaatst |
rateLimitPerMinute |
number | leeg | Override op default 25 calls/min, zie mail rate limit |
rateLimitRecipientsPerDay |
number | leeg | Override op default 9000 ontvangers/dag, zie mail rate limit |
consentStatus.granted |
boolean | false |
Admin-consent voltooid |
consentStatus.lastConsentAt |
date | optioneel | Tijdstip laatste succesvolle consent |
consentStatus.lastConsentBy |
string | optioneel | User-uuid of e-mail van consent-verlener |
consentStatus.lastErrorAt |
date | optioneel | Tijdstip laatste mailfout |
consentStatus.lastErrorMessage |
string | optioneel | Foutboodschap bij laatste mailfout |
Bekende keys:
| Key | Doel |
|---|---|
monitor |
Alle monitor-campagne-mails (uitnodiging, 1e en 2e reminder, rapportages) |
Velden op andere modellen
MessageTemplate.systemMailboxKey?: string: als gezet, erven nieuwe Messages deze key.
Message.systemMailboxKey?: string: bepaalt of de Message via systeem- of tenant-mailbox gaat. Expliciete waarde overschrijft template-waarde tijdens messageService.create.
Beide additief, default leeg, backward-compatible.
Admin-API
Mount: apps/api/src/api/protectedEndpointRouter.ts, prefix /admin/system-mailboxes. Auth: authenticationService.validateBearerAuth (admin-token).
| Method | Path | Body | Response |
|---|---|---|---|
GET |
/admin/system-mailboxes |
- | Array van bekende records (vandaag alleen monitor) |
GET |
/admin/system-mailboxes/:key |
- | Record of 404 { message: 'Niet gevonden' } |
PUT |
/admin/system-mailboxes/:key |
{ displayName, azureTenantId, mailboxAddress, saveToSentItems, rateLimitPerMinute?, rateLimitRecipientsPerDay? } |
Bijgewerkt record, of 400 { errors: [...] } |
GET |
/admin/system-mailboxes/:key/consent-url |
- | { consentUrl, redirectUri } of 400 als AZURE_CLIENT_ID ontbreekt |
POST |
/admin/system-mailboxes/:key/test |
{ to: string } |
{ ok: true, messageId? } of { ok: false, error } |
Validatie op PUT:
displayName: niet-lege stringazureTenantId: niet-lege stringmailboxAddress: regex^[^@\s]+@[^@\s]+\.[^@\s]+$rateLimitPerMinute(optioneel): positief geheel getalrateLimitRecipientsPerDay(optioneel): positief geheel getal
Validatie op POST /test:
to: zelfde e-mail-regex. Bij ongeldig:400 { message: 'Geldigtoadres vereist' }.
Public consent-callback
Mount: apps/api/src/api/publicEndpointRouter.ts, geen auth (Microsoft redirect).
| Method | Path | Query |
|---|---|---|
GET |
/system/mailboxes/azure/callback |
admin_consent, tenant, state, error?, error_description? |
State-formaat: JSON string { "systemMailboxKey": "<key>" }.
Gedrag:
erroraanwezig →400, response geeft de Microsoft-foutmelding terug.admin_consent !== 'true'→400, “Admin consent niet verleend”.- State niet parseerbaar of zonder
systemMailboxKey→400. - Onbekende
key→400, “Onbekende system-mailbox”. - Tenant uit callback wijkt af van opgeslagen
azureTenantId→ record wordt bijgewerkt naar de Microsoft-waarde. - Succes →
consentStatus.granted = true,lastConsentAt = now, factory-cache geïnvalideerd.
Mailer-routing
apps/api/src/common/services/mail-provider-service/MailProviderService.ts:
const mailer = message.systemMailboxKey
? await mailerFactory.getMailerForSystemMailbox(message.systemMailboxKey)
: await mailerFactory.getMailer();
mailerFactory.getMailerForSystemMailbox(key) (zie MailerFactory.ts):
- Cache-key:
key. Aparte cache (systemCache) naast tenant-cache. - Throwt
Error('System mailbox "X" is not configured')als record ontbreekt ofazureTenantIdofmailboxAddressleeg is. - Throwt
Error('System mailbox "X" consent not granted')alsconsentStatus.granted !== true. - Bij succes:
MicrosoftGraphMailer({ tenantId, fromEmail, fromDisplayName, saveToSentItems }), gewrapt metRateLimitedGraphMailer(zie mail rate limit). - Cache-hash bevat ook
rateLimitPerMinuteenrateLimitRecipientsPerDay, dus een wijziging via de admin-UI invalideert de cache zonder pod-restart.
mailerFactory.invalidateSystemMailbox(key?) leegt cache voor één key, of alle systeem-cache als key leeg is.
Foutlogging op het record
MailProviderService.send catch-pad (alleen als systemMailboxKey op de message stond):
- Foutboodschap matcht
\b(401|403)\b→systemMailboxService.logAuthFailure(key, msg). Dit zetgranted: false, pluslastErrorAt/Message. - Anders →
systemMailboxService.logError(key, msg). AlleenlastErrorAt/Message.
Original error bubble-t door zodat messageService.send de message-status op ERROR zet.
Scripts
Seed: apps/api/scripts/seed-system-mailbox-monitor/script.ts
npm run seed:system-mailbox-monitor -w apps/api
Idempotent. Maakt het record monitor aan met lege configuratie, of laat het bestaande record ongemoeid.
Templates taggen: apps/api/scripts/tag-monitor-templates/script.ts
npm run tag:monitor-templates -w apps/api
Itereert alle tenants. Per tenant: verzamelt template-uuids uit ParticipationMonitorCampaign.messageTemplate, messageTemplateFirstReminder en messageTemplateSecondReminder. Update via MessageTemplate.updateMany alleen templates zonder bestaande systemMailboxKey. Idempotent. Geeft per tenant { uniqueTemplateUuids, modified, skipped } terug.
Environment
| Variabele | Doel |
|---|---|
AZURE_CLIENT_ID |
App-registratie voor admin-consent. Verplicht voor GET /:key/consent-url. Gedeeld met tenant-flow. |
AZURE_CLIENT_SECRET |
App-secret voor app-only token-acquisitie via MicrosoftGraphTokenProvider. |
Beide bestaan al voor de tenant-flow, geen nieuwe variabelen geïntroduceerd.
Frontend
Feature: apps/frontend/src/app/features/admin/mailboxes/.
Route: /admin/systeem-mailboxen (list) en /admin/systeem-mailboxen/:key (detail). Geregistreerd in admin.routes.ts onder path: 'systeem-mailboxen'. Sidebar-menu in tapster-sidebar.component.ts. Beschermd door adminGuard op de parent-route.
HTTP-client: SystemMailboxService in data-access/system-mailbox.service.ts. Base-URL: ${apiV7Url}/admin/system-mailboxes.
Bron-bestanden
- Backend module:
apps/api/src/api/system-mailbox/ - Mailer-routing:
apps/api/src/common/services/mail-provider-service/{MailerFactory.ts,MailProviderService.ts} - Message-velden:
apps/api/src/api/message/messageModel.ts,apps/api/src/api/message-template/messageTemplateModel.ts - Message-erving:
apps/api/src/api/message/messageService.ts(zoek opsystemMailboxKey) - Public callback:
apps/api/src/api/publicEndpointRouter.ts(/system/mailboxes/azure/callback) - Frontend:
apps/frontend/src/app/features/admin/mailboxes/