From 83214eb3792e9483152dc8a694782e6a5157a3a4 Mon Sep 17 00:00:00 2001 From: Ronni Baslund Date: Wed, 10 Jun 2026 21:47:27 +0200 Subject: [PATCH] feat(tenants): isPlatformTenant flag replaces PLATFORM_TENANT_SLUG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Identifying the company tenant by slug in env was fragile — every purge/recreate changed the slug (or id) and the apex guard chased reality through three config flips in one day. The identity now lives ON the tenant document: isPlatformTenant, operator-set from the tenant page (single holder — setting it clears the flag everywhere else), guarded so tenant admins can't set it on themselves through the shared PATCH route. The dezky.eu apex guard reads the flag; PLATFORM_TENANT_SLUG is gone. Dev seed flags its seeded tenant. config-rev 5 rolls platform-api. --- apps/operator/pages/tenants/[slug].vue | 35 +++++++++++++++++++ apps/operator/types/tenant.ts | 2 ++ .../fleet/apps/platform-api-config.yaml | 7 ++-- .../production/fleet/apps/platform-api.yaml | 2 +- .../src/domains/domains.controller.ts | 2 +- .../domains/domains.service.guards.spec.ts | 31 +++++++--------- .../src/domains/domains.service.ts | 15 ++++---- .../platform-api/src/schemas/tenant.schema.ts | 9 +++++ .../platform-api/src/seed/seed.service.ts | 1 + .../src/tenants/dto/update-tenant.dto.ts | 6 ++++ .../src/tenants/tenants.controller.ts | 5 +++ .../src/tenants/tenants.service.ts | 9 +++++ 12 files changed, 93 insertions(+), 31 deletions(-) diff --git a/apps/operator/pages/tenants/[slug].vue b/apps/operator/pages/tenants/[slug].vue index a35c35d..2ff3dd3 100644 --- a/apps/operator/pages/tenants/[slug].vue +++ b/apps/operator/pages/tenants/[slug].vue @@ -85,6 +85,25 @@ type IntegrationKey = (typeof INTEGRATIONS)[number] // runs; reusable whenever the first invite failed or someone new takes over. const inviteAdminOpen = ref(false) +// Platform-tenant flag toggle. Single holder — the API clears it from every +// other tenant when set. Grants exactly one thing: claiming the dezky.eu +// apex as this tenant's customer mail domain. +const flagBusy = ref(false) +const flagError = ref(null) +async function setPlatformTenant(value: boolean) { + flagBusy.value = true + flagError.value = null + try { + await $fetch(`/api/tenants/${slug.value}`, { method: 'PATCH', body: { isPlatformTenant: value } }) + await refreshTenant() + } catch (err: unknown) { + const e = err as { data?: { data?: { message?: string }; message?: string } } + flagError.value = e.data?.data?.message ?? e.data?.message ?? String(err) + } finally { + flagBusy.value = false + } +} + // ── Danger-zone state ───────────────────────────────────────────────────── const dangerAction = ref<'suspend' | 'resume' | 'delete' | 'purge' | null>(null) const dangerBusy = ref(false) @@ -145,6 +164,7 @@ async function reconcile() {