From 89691626f4d3588e804eff84edb2440df2c26125 Mon Sep 17 00:00:00 2001 From: Ronni Baslund Date: Sat, 30 May 2026 08:03:07 +0200 Subject: [PATCH] feat: partner enrichment, mutations, settings & branding + operator quick-wins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend (platform-api): computed tenant health plus industry/brandColor; partner-scoped tenant update/suspend/resume guarded by assertPartnerOwnsTenant; enriched partner users (MFA + access level) with invite/remove; partner settings and whitelabel branding persistence; Authentik authenticator counting and group removal. Audit on every mutation. Frontend (portal): all five partner pages on real data — dashboard alerts, customers edit/suspend, team MFA/access with invite/remove, editable settings, branding fetch/save. Operator: dashboard and infrastructure service health driven by real liveness probes; fabricated uptime/p95/error-rate removed. --- apps/operator/pages/index.vue | 35 +- apps/operator/pages/infrastructure.vue | 9 +- .../components/partner/EditIdentityModal.vue | 53 ++- .../partner/InviteTeammateModal.vue | 19 +- .../components/partner/TeammatePanel.vue | 2 + apps/portal/pages/partner/branding.vue | 147 ++++++-- apps/portal/pages/partner/customers.vue | 182 +++++++--- apps/portal/pages/partner/index.vue | 81 +++-- apps/portal/pages/partner/settings.vue | 141 ++++++-- apps/portal/pages/partner/team.vue | 61 +++- .../portal/server/api/partner/branding.get.ts | 20 ++ .../portal/server/api/partner/branding.put.ts | 24 ++ .../portal/server/api/partner/settings.get.ts | 20 ++ .../server/api/partner/settings.patch.ts | 24 ++ .../api/partner/tenants/[slug].patch.ts | 25 ++ .../api/partner/tenants/[slug]/resume.post.ts | 23 ++ .../partner/tenants/[slug]/suspend.post.ts | 23 ++ apps/portal/server/api/partner/users.post.ts | 24 ++ .../api/partner/users/[subject].delete.ts | 23 ++ .../src/integrations/authentik.client.ts | 34 ++ .../src/me/dto/partner-branding.dto.ts | 48 +++ .../src/me/dto/partner-settings.dto.ts | 34 ++ .../src/me/dto/partner-update-tenant.dto.ts | 25 ++ services/platform-api/src/me/me.module.ts | 18 +- .../src/me/partner-branding.service.ts | 72 ++++ .../src/me/partner-me.controller.ts | 198 ++++++++++- .../src/partners/partners.service.ts | 44 +++ .../src/schemas/partner-branding.schema.ts | 54 +++ .../src/schemas/partner.schema.ts | 62 ++++ .../platform-api/src/schemas/tenant.schema.ts | 10 + .../platform-api/src/schemas/user.schema.ts | 11 + .../src/tenants/tenants.service.ts | 85 ++++- .../platform-api/src/users/users.service.ts | 320 +++++++++++++++++- 33 files changed, 1753 insertions(+), 198 deletions(-) create mode 100644 apps/portal/server/api/partner/branding.get.ts create mode 100644 apps/portal/server/api/partner/branding.put.ts create mode 100644 apps/portal/server/api/partner/settings.get.ts create mode 100644 apps/portal/server/api/partner/settings.patch.ts create mode 100644 apps/portal/server/api/partner/tenants/[slug].patch.ts create mode 100644 apps/portal/server/api/partner/tenants/[slug]/resume.post.ts create mode 100644 apps/portal/server/api/partner/tenants/[slug]/suspend.post.ts create mode 100644 apps/portal/server/api/partner/users.post.ts create mode 100644 apps/portal/server/api/partner/users/[subject].delete.ts create mode 100644 services/platform-api/src/me/dto/partner-branding.dto.ts create mode 100644 services/platform-api/src/me/dto/partner-settings.dto.ts create mode 100644 services/platform-api/src/me/dto/partner-update-tenant.dto.ts create mode 100644 services/platform-api/src/me/partner-branding.service.ts create mode 100644 services/platform-api/src/schemas/partner-branding.schema.ts diff --git a/apps/operator/pages/index.vue b/apps/operator/pages/index.vue index 65b6701..00d7b79 100644 --- a/apps/operator/pages/index.vue +++ b/apps/operator/pages/index.vue @@ -3,7 +3,6 @@ import type { Tenant } from '~/types/tenant' import type { Partner } from '~/types/partner' import type { PlatformUser } from '~/types/user' import type { AuditEvent } from '~/types/audit' -import { SERVICES, INCIDENT } from '~/data/fixtures' const { data: tenants, pending: tp, refresh: rT } = await useFetch('/api/tenants', { default: () => [] }) const { data: partners, pending: pp, refresh: rP } = await useFetch('/api/partners', { default: () => [] }) @@ -13,19 +12,30 @@ const { data: auditEvents, refresh: rA } = await useFetch('/api/au query: { limit: 8 }, }) +// Real service health from platform-api probes (same source as the +// infrastructure page) — replaces the old SERVICES/INCIDENT fixtures. +interface ProbeResult { + id: string + name: string + status: 'ok' | 'warn' | 'bad' +} +const { data: probes, refresh: rH } = await useFetch('/api/health/platform', { + default: () => [], +}) + function fmtClock(iso: string) { return new Date(iso).toLocaleTimeString('da-DK', { hour: '2-digit', minute: '2-digit' }) } -const { open: openIncident } = useIncidentModal() - const pending = computed(() => tp.value || pp.value || up.value) async function refresh() { - await Promise.all([rT(), rP(), rU(), rA()]) + await Promise.all([rT(), rP(), rU(), rA(), rH()]) } -const degradedCount = computed(() => SERVICES.filter((s) => s.status !== 'ok').length) +const totalServices = computed(() => (probes.value ?? []).length) +const degradedServices = computed(() => (probes.value ?? []).filter((p) => p.status !== 'ok')) +const degradedCount = computed(() => degradedServices.value.length) const incidentActive = computed(() => degradedCount.value > 0) const stats = computed(() => ({ @@ -74,18 +84,18 @@ function fmtDate(d: string) {
- +
@@ -102,7 +112,7 @@ function fmtDate(d: string) { label="Services" :value="incidentActive ? `${degradedCount} degraded` : 'all green'" :delta-tone="incidentActive ? 'down' : 'up'" - :hint="incidentActive ? 'P2 · authentik' : `${SERVICES.length} / ${SERVICES.length} healthy`" + :hint="`${totalServices - degradedCount} / ${totalServices} healthy`" />
@@ -249,6 +259,7 @@ function fmtDate(d: string) { cursor: pointer; color: var(--text); font-family: inherit; + text-decoration: none; } .pill { display: inline-flex; diff --git a/apps/operator/pages/infrastructure.vue b/apps/operator/pages/infrastructure.vue index 0db9afa..17be489 100644 --- a/apps/operator/pages/infrastructure.vue +++ b/apps/operator/pages/infrastructure.vue @@ -1,5 +1,5 @@