feat(platform): real email domains, mailboxes & member lifecycle
Wire the mail/identity stack to real Stalwart/Authentik/OCIS provisioning, replacing the mocked Domains and Users pages. Domains (customer-admin): - StalwartClient: real JMAP management (v0.16 dropped REST) — create/list/delete email domains via x:Domain at the internal http://stalwart:8080 listener; DKIM auto-generated; the records to publish are read from the domain's dnsZoneFile. Gated by STALWART_PROVISIONING_ENABLED. - New Domain collection + DomainsModule: add/list/recheck/set-DMARC/remove, tenant-membership-gated and audited. - DnsVerifierService: verifies MX/SPF/DKIM/DMARC/ownership against a public resolver (1.1.1.1/8.8.8.8) and diffs them against the expected records. - Remove is guarded: refuses while accounts/aliases/mailing lists still use the domain (via Stalwart referential integrity). - Domains page + add wizard on real data; sidebar badge counts domains needing attention. Users & groups (customer-admin): - Create a member provisioned across Authentik SSO, a Stalwart mailbox on the tenant's primary domain, and OCIS — returning a one-time password. - Lifecycle: suspend/resume (Authentik is_active + freeze the mailbox via account permissions, original password preserved), force-logout (terminate sessions, filtered client-side so it can never end other users' sessions), reset password (new one-time password on SSO + mailbox), and remove (tear down mailbox + SSO identity + OCIS + doc; mailbox-in-use aware for multi-tenant users). Self-suspend / self-force-logout are blocked. Infra: point platform-api at the internal Stalwart listener; document the new STALWART_/provisioning vars in .env.example.
This commit is contained in:
@@ -50,45 +50,6 @@ export const sampleGroups = [
|
||||
{ id: 'g-5', name: 'Sales', description: 'Outbound + customer success', members: 3, owner: 'Bo Christensen', resources: ['sales@', 'Drev/Sales', '#sales'] },
|
||||
]
|
||||
|
||||
export const sampleDomains = [
|
||||
{
|
||||
id: 'd-1',
|
||||
domain: 'baslund.dk',
|
||||
primary: true,
|
||||
status: 'partial',
|
||||
records: [
|
||||
{ type: 'MX', status: 'ok', value: '10 mail.dezky.com' },
|
||||
{ type: 'SPF', status: 'warn', value: 'v=spf1 ~all', expected: 'v=spf1 include:_spf.dezky.com ~all' },
|
||||
{ type: 'DKIM', status: 'ok', value: 'k=rsa; p=MIGfMA0GCSq...' },
|
||||
{ type: 'DMARC', status: 'bad', value: '— not found —', expected: 'v=DMARC1; p=quarantine; rua=mailto:dmarc@baslund.dk' },
|
||||
],
|
||||
addedOn: '2025-12-04',
|
||||
},
|
||||
{
|
||||
id: 'd-2',
|
||||
domain: 'baslund.shop',
|
||||
primary: false,
|
||||
status: 'healthy',
|
||||
records: [
|
||||
{ type: 'MX', status: 'ok', value: '10 mail.dezky.com' },
|
||||
{ type: 'SPF', status: 'ok', value: 'v=spf1 include:_spf.dezky.com ~all' },
|
||||
{ type: 'DKIM', status: 'ok', value: 'k=rsa; p=MIGfMA0G...' },
|
||||
{ type: 'DMARC', status: 'ok', value: 'v=DMARC1; p=quarantine' },
|
||||
],
|
||||
addedOn: '2026-02-11',
|
||||
},
|
||||
{
|
||||
id: 'd-3',
|
||||
domain: 'baslund.io',
|
||||
primary: false,
|
||||
status: 'verifying',
|
||||
records: [
|
||||
{ type: 'TXT', status: 'warn', value: 'dezky-site-verification=…', hint: 'Awaiting propagation · ~10 min remaining' },
|
||||
],
|
||||
addedOn: '2026-05-22',
|
||||
},
|
||||
]
|
||||
|
||||
export const sampleInvoices = [
|
||||
{ id: 'inv-2026-001247', date: '2026-05-01', period: '2026-05', amount: 1940, status: 'paid', method: 'MobilePay' },
|
||||
{ id: 'inv-2026-001112', date: '2026-04-01', period: '2026-04', amount: 1940, status: 'paid', method: 'MobilePay' },
|
||||
@@ -173,14 +134,6 @@ export const sampleUsersFlat = [
|
||||
{ id: 'u_tt55', name: 'Clara Bjerre', email: 'clara@dezky.com', role: 'Member', status: 'active', last: '5 d ago', group: 'Sales', storage: 2.0 },
|
||||
]
|
||||
|
||||
// Source-fidelity domains (platform-screens.jsx SAMPLE_DOMAINS line 23) — flat
|
||||
// shape with per-record-type status used by DomainsScreen / DomainCard.
|
||||
export const sampleDomainsFlat = [
|
||||
{ domain: 'dezky.com', status: 'ok' as const, mx: 'ok' as const, spf: 'ok' as const, dkim: 'ok' as const, dmarc: 'ok' as const, users: 11 },
|
||||
{ domain: 'dezky.io', status: 'ok' as const, mx: 'ok' as const, spf: 'ok' as const, dkim: 'ok' as const, dmarc: 'warn' as const, users: 0 },
|
||||
{ domain: 'baslund.dk', status: 'warn' as const, mx: 'ok' as const, spf: 'warn' as const, dkim: 'ok' as const, dmarc: 'bad' as const, users: 2 },
|
||||
]
|
||||
|
||||
// Meeting rooms — strict port of platform-collab.jsx MEETING_ROOMS (line 8)
|
||||
export const meetingRooms = [
|
||||
{ id: 'r_eng', name: 'Engineering standup', alias: 'eng-standup', type: 'recurring' as const, when: 'Daily · 09:30', owner: 'Mikkel Nørgaard', members: 4, recording: 'auto' as const, protected: false },
|
||||
|
||||
Reference in New Issue
Block a user