Beheer (Backend)
Technisch Ontwerp
De Django-app features/beheer is klein en doelgericht: het verzorgt de team-brede broadcasts (e-mail en push) die niet in auth/ (gebruikersbeheer, uitnodigingen) of rbac/ (rollen en permissies) thuishoren. De overige beheerfunctionaliteit die het scherm BeheerScreen toont, leeft in andere modules. Beheer roept die endpoints aan maar implementeert ze niet:
auth(admin-endpoints onder/api/auth/admin/...): gebruikers, uitnodigingen, admin-TOTP-reset en de audit-log.rbac: functies (rollen) en de permissiecatalogus.features.tenant: apotheekgegevens, logo, billing-samenvatting en de afmeldflow.email_domains(onder/api/settings/email/...): e-mailafzenderdomeinen en DNS.billing: abonnement, verbruik en facturen.
De broadcast-endpoints zelf zijn drie APIView's. De e-mailcomposer rendert eerst een server-side voorbeeld (MailEmployeesPreviewView) dat byte-identiek is aan de verzonden mail, behalve dat het logo als data-URL wordt ingebed in plaats van als cid:-referentie. Verzenden gebeurt via MailEmployeesSendView en PushEmployeesSendView. Ontvangers worden in beide gevallen via resolve_recipients opgelost; verzending loopt via de notifications-laag (Celery).
flowchart TD
UI[BeheerScreen / Mail- en PushModal] -->|POST + step-up| PV[MailEmployeesPreviewView]
UI -->|POST + step-up| MV[MailEmployeesSendView]
UI -->|POST + step-up| PUV[PushEmployeesSendView]
PV --> PC0{PermCheck: settings.manage_users + RequiresFreshStepUp}
PV --> ER[email_render.render_employee_email_content preview=True]
MV --> PC1{PermCheck: settings.manage_users + RequiresFreshStepUp}
MV --> RL1{ratelimit.is_rate_limited mail_employees?}
RL1 -->|Ja| R429[429 Too Many Requests]
RL1 -->|Nee| RR1[resolve_recipients require_email=True]
RR1 -->|RecipientResolutionError| R400[400 Bad Request]
RR1 --> SB[email_render.send_employee_broadcast]
SB --> EE[notifications.enqueue_email per adres] --> Celery --> SES
SB --> AL1[audit_logger: settings.mail_employees.sent]
PUV --> RR2[resolve_recipients require_email=False]
RR2 --> PE[notifications.push_enabled_users]
PE --> EN[notifications.enqueue_notification send_email=False] --> Celery --> Push
PUV --> AL2[audit_logger: settings.push_employees.sent]
Datamodel (ERD)
Beheer kent zelf geen modellen. De broadcasts en de overige beheeracties leunen op bestaande modellen uit tenants, auth en rbac. Het diagram toont uitsluitend de samenhang die voor Beheer relevant is.
erDiagram
APOTHEEK ||--o{ ROLE : "heeft"
APOTHEEK ||--o{ USER : "heeft via tenant"
APOTHEEK ||--o{ USERINVITATION : "heeft"
ROLE }o--o{ PERMISSION : "ROLE_PERMISSIONS"
USER }o--o{ ROLE : "users m2m"
APOTHEEK {
int id PK
string schema_name
string name
string logo_s3_key
string billing_email
string general_email
string straat_adres
string postcode
string stad
boolean is_active
datetime pending_unregister_at
datetime deleted_at
}
USER {
int id PK
string email
string first_name
string last_name
date birth_date
string phone_number "versleuteld"
boolean is_active
datetime date_joined
}
USERINVITATION {
uuid id PK
int tenant_id FK
string email
string first_name
string last_name
datetime created_at
datetime expires_at
datetime cancelled_at
}
ROLE {
int id PK
int tenant_id FK
string name
string code
}
PERMISSION {
int id PK
string code
}
API & Communicatie
Eigen endpoints (/api/beheer/)
POST /api/beheer/mail-employees/preview/: server-side render van een team-e-mail. Bodysubject(max 200 tekens),body(max 20000 tekens). Antwoord bevatsubject,text,html,tenant_name,preview_logo_srcenfrom_label. Het voorbeeld is byte-identiek aan de verzonden mail, alleen het logo verschilt (data-URL hier,cid:bij verzenden).POST /api/beheer/mail-employees/: verstuurt de e-mail. Bodysubject,body,participants_mode(everyoneofselection),roles(codes uitROLE_OPTION_CODES),people(gebruiker-id's, integers). Antwoord{ "sent_to": <aantal> }. Eénenqueue_emailper adres, zodat ontvangers elkaar niet zien.POST /api/beheer/push-employees/: verstuurt een pushbericht. Bodytitle(max 50 tekens),body(max 150 tekens) plus dezelfde ontvangerselectie. Antwoord{ "sent_to": <aantal> }. Alleen ontvangers met push aangezet en een actief native device-token worden bereikt; web push wordt bewust overgeslagen (gereserveerd voor Atlas-antwoorden).
De ontvangerlogica zit in services.resolve_recipients(tenant, data, require_email). Bij everyone levert dit alle actieve tenant-gebruikers; bij selection de gededuceerde unie van opgegeven personen en rol-gematchte gebruikers. Een opgegeven people-id dat niet tot een actieve gebruiker van deze tenant herleidt, levert een RecipientResolutionError op (in tegenstelling tot de agenda, die zulke id's stil laat vallen). Voor e-mail worden adresloze gebruikers eruit gefilterd; voor push is dat niet nodig.
Verbruikte endpoints (in andere modules)
- Gebruikers en uitnodigingen (
/api/auth/admin/...):GET|POST /users/,PATCH|DELETE /users/<id>/,POST /users/<id>/totp-reset/,GET /pending-invitations/,POST /invitations/<uuid>/resend/,DELETE /invitations/<uuid>/. - Audit-log:
GET /api/auth/admin/audit-log/(gepagineerd, met filters op gebeurtenis, ernst, actor, resource en periode). - RBAC:
GET /api/rbac/catalog/,GET|POST /api/rbac/roles/,PUT|DELETE /api/rbac/roles/<id>/. - Tenant:
GET|PATCH /api/tenant/info/,POST /api/tenant/logo/(multipart, veldnaamimage),GET /api/tenant/billing/,POST /api/tenant/unregister/(TOTP vereist). - E-mailafzender:
GET|POST /api/settings/email/sending-domains/, plus per domeinrefresh,enable,disable,send-testenDELETE.
Publieke (niet-ingelogde) endpoints die door Beheer worden aangeroepen:
GET /api/auth/invite/<token>/validate/enPOST /api/auth/invite/<token>/accept/.GET /api/auth/totp-admin-reset/<token>/validate/enPOST /api/auth/totp-admin-reset/<token>/confirm/.
Foutafhandeling & Statuscodes
200 OK: succesvolle preview of verzending.400 Bad Request: validatiefout (te lang onderwerp of bericht, lege selectie), ontbrekende tenant-context, ofRecipientResolutionError(lege of ongeldige selectie).401 Unauthorized: ongeldige of verlopen sessie. De frontend stuurt naar/login.403 Forbidden: gebruiker mistsettings.manage_users, of de step-up is niet recent genoeg (de frontend start dan de step-up-flow).429 Too Many Requests: de verzendlimiet voor deze apotheek is bereikt (apart geteld voor mail en push).
De rate limiter is een per-tenant, per-actie Redis-teller (ratelimit.py, sleutel {action}:{tenant.id}). De limieten staan in settings: MAIL_EMPLOYEES_RATE_LIMIT = 10 per MAIL_EMPLOYEES_RATE_WINDOW_SECONDS = 3600, en PUSH_EMPLOYEES_RATE_LIMIT = 10 per PUSH_EMPLOYEES_RATE_WINDOW_SECONDS = 3600. De teller wordt alleen verhoogd na een geslaagde enqueue, nooit na een 400, zodat een mislukte poging geen budget verbruikt. Het is een zachte rem: onder zware gelijktijdigheid is één extra verzending mogelijk.
Voor specifieke foutcodes binnen auth, billing, rbac of email_domains verwijst dit naar de respectieve module-documentatie.
Autorisatie & Beveiliging
De drie eigen endpoints vereisen HasPermissionCode met required_permission = "settings.manage_users" en RequiresFreshStepUp (een recente step-up via TOTP of biometrie). Tenant-context komt uit common.tenants.current_tenant(request); ontbreekt die, dan volgt een 400.
De permissiecodes voor de overige secties (afgehandeld door de onderliggende modules) zijn:
settings.view: toegang tot/beheerenGET /api/tenant/info/.settings.manage_users: gebruikers- en uitnodigingsbeheer, plus de team-broadcasts.settings.manage_roles: permissiecatalogus en rollen.settings.manage_tenant: apotheekgegevens, logo en e-mailafzender.settings.manage_billing: billing-samenvatting, billing-mutaties en het afmelden van de apotheek.settings.view_audit_log: de audit-log inzien.
Alle codes staan in backend/rbac/permissions_catalog.py. Tenant-scoping verloopt via de django-tenants-middleware en de User.tenant-FK. Destructieve admin-acties (gebruiker verwijderen, TOTP-reset, uitnodiging intrekken of opnieuw versturen, apotheek afmelden) vereisen aanvullend een geldige TOTP-code in de request body. Elke verzending wordt vastgelegd via audit_logger met de gebeurtenissen settings.mail_employees.sent en settings.push_employees.sent, inclusief de gekozen modus en de aantallen.
Bestandsstructuur & Verantwoordelijkheden
backend/features/beheer/views.py:MailEmployeesPreviewView,MailEmployeesSendView,PushEmployeesSendView.backend/features/beheer/services.py:resolve_recipients,ROLE_OPTION_CODES,RecipientResolutionError.backend/features/beheer/email_render.py:render_employee_email_content(parity preview/verzending) ensend_employee_broadcast.backend/features/beheer/ratelimit.py: per-tenant, per-actie verzendlimiet.backend/features/beheer/serializers.py: request- en response-serializers, met de tekenlimieten uit settings.backend/features/beheer/urls.py: de drie URL-patronen.backend/auth/views/admin_users.py,backend/auth/views/audit_log.py,backend/auth/views/totp_admin_reset.py,backend/auth/views/invite_accept.py: gebruikers-, audit- en uitnodiging-endpoints.backend/rbac/views.pyenbackend/rbac/permissions_catalog.py: rollen, catalogus en permissiecodes.backend/features/tenant/views.py:TenantInfoView,TenantLogoUploadView,BillingInfoView,ApotheekUnregisterView.backend/tenants/models.py: modelApotheek.
Belangrijke bestanden
backend/features/beheer/views.pybackend/features/beheer/services.pybackend/features/beheer/email_render.pybackend/features/beheer/ratelimit.pybackend/rbac/permissions_catalog.pybackend/features/tenant/views.py
API & Communicatie (Swagger)
De openapi.yaml hieronder dekt uitsluitend de eigen /api/beheer/-endpoints (de drie broadcast-acties). De gebruikers-, RBAC-, tenant-, audit-log- en e-mailafzender-endpoints die Beheer verbruikt, staan in de Swagger-specificaties van hun eigen modules (auth, rbac, tenant, email_domains).