Introduces ApiError class in client.ts that parses Spring Boot error
bodies to extract human-readable messages (message, error, detail fields).
Adds errorMessage() helper used by all toast descriptions instead of
raw String(err) which dumped JSON blobs to the user.
Affected: all 10 page components that display error toasts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vendor UI:
- TenantDetailPage: full minting form with tier presets, 13 configurable
limits, expiry/grace period, label. Mint & Push or Mint & Copy actions.
License bundle display with all three env vars for standalone deployment.
- LicenseVerifyPage: paste token to decode + validate signature, shows
envelope details and state badge. Public key viewer with copy button.
- Layout: added "License Tools" nav item under Vendor section.
- vendor-hooks: useMintLicense, useLicensePresets, useVerifyLicense, usePublicKey
Tenant UI:
- TenantLicensePage: replaced features card with full 13-key limits display,
added grace period and label fields
- TenantDashboardPage: fixed limit keys (agents→max_agents, environments→max_environments)
Common:
- Updated types (dropped features, added label/gracePeriodDays/bundle types)
- Updated tier colors for STARTER/TEAM/BUSINESS/ENTERPRISE
- Updated CreateTenantPage tier dropdown
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds testSmtpConnection() that performs EHLO + auth via JavaMailSender
before persisting to Logto — saves fail fast with a clear error if
SMTP credentials are wrong. Password is now optional when editing:
if left blank the backend fetches the existing password from Logto's
connector config, so users can update host/port/fromEmail without
re-entering the password every time.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix vertical alignment of Lucide icons inside Button children across
all pages by adding verticalAlign offsets (-3px for 16px icons, -2px
for 14px icons). The design system Button wraps children in an inline
span, so SVG icons defaulted to baseline alignment.
Hide the redundant top-right "Create Tenant" button on VendorTenantsPage
when no tenants exist — the EmptyState already provides that action.
Add icons to all vendor sidebar sub-items for consistency (previously
only Email Connector had one).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fleet overview page at /vendor/metrics showing per-tenant operational
metrics (agents, CPU, heap, HTTP requests, ingestion drops, uptime).
Queries each tenant's server via the new POST /api/v1/admin/server-metrics/query
REST API instead of direct ClickHouse access, supporting future per-tenant
CH instances.
Backend: TenantMetricsService fires 11 metric queries per tenant
concurrently over a 5-minute window, assembles into a summary snapshot.
ServerApiClient.queryServerMetrics() handles the M2M authenticated POST.
Frontend: VendorMetricsPage with KPI strip (fleet totals) and per-tenant
table with color-coded badges and heap usage bars. Auto-refreshes every 60s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use Card and KpiStrip design system components, add database icons to
section headers, right-align numeric columns, replace text toggles with
chevron icons, and constrain max width to prevent ultra-wide stretching.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restart only stops/starts existing containers with the same image. The new
upgrade action removes server + UI containers, force-pulls the latest
Docker images, then re-provisions (preserving app containers, volumes, and
networks). Available to both vendor (tenant detail) and tenant admin
(dashboard).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace inline FileField and native <input type="file"> with
FileInput from @cameleer/design-system (drag-and-drop, icons, clear)
- Update CertificatesPage and SsoPage to use FileInput + FormField
- Fix /certs volume permissions (chmod 775) so cameleer user can write
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Encrypted PKCS#8 private keys are decrypted during staging using the
provided password. The decrypted key is stored for Traefik (which needs
cleartext PEM). Unencrypted keys continue to work without a password.
- CertificateManager.stage() accepts optional keyPassword
- DockerCertificateManager handles EncryptedPrivateKeyInfo decryption
- UI: password field in upload form (vendor CertificatesPage)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Truncate fingerprint with hover tooltip
- Remove duplicate warning icon in stale banner
- Style file inputs to match design system
- Bump grid min-width for better card spacing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vendor: POST /api/vendor/tenants/{id}/restart (platform:admin scope)
Tenant: POST /api/tenant/server/restart (tenant:manage scope)
Both call TenantProvisioner.stop() then start() on the server + UI
containers. Restart button on vendor TenantDetailPage (Actions card)
and tenant TenantDashboardPage (Server card). Allowed in any status
including PROVISIONING.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: extract Docker provisioning into @Async method so the API
returns immediately with status=PROVISIONING. The tenant record, Logto
org, admin user, and license are created synchronously; container
provisioning, health check, license push, and OIDC config happen in a
background thread.
Frontend: navigate to tenant detail page immediately after creation.
Detail page polls every 3s while status=PROVISIONING and shows a
spinner indicator. Toast notification when provisioning completes.
Fixes#52.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Vendor sees all audit events with tenant filter at /vendor/audit.
Tenant admin sees only their own events at /tenant/audit.
Both support pagination, action/result filters, and text search.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logto usernames must match alphanumeric regex. The form now strips
invalid characters on input and shows a hint about the constraint.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When creating a tenant, the vendor can specify adminUsername +
adminPassword. The backend creates the user in Logto and assigns them
the owner org role. The vendor user is also auto-added to every new
org for support access.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Splits the flat 3-page UI into /vendor/* (platform:admin) and /tenant/*
(all authenticated users) route trees, with stub pages, new API hooks,
updated Layout with persona-aware sidebar, and SpaController forwarding.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>