Add Storage, Users, Secrets, Webhooks, Scheduled tasks, Audit log screens

Full management surfaces for the platform-admin tenant, mirroring the
existing Tenants pattern (DataTable + row actions + create/edit dialogs +
ConfirmDialog for destructive ops, all data-action tagged for the
command bus, useRegisterAdminContext publishing for the assistant).

- Storage (/storage): backends + credentials. Write-only secret fields,
  Validate/Activate/Deactivate/Set-default/Mark-degraded/Maintenance.
- Users (/users): tabs for Users, Invitations, Roles. Per-user View
  drawer with profile, role add/remove, API keys (one-time reveal on
  create), usage + quota.
- Secrets (/secrets): /api/v1/admin/secrets — create/rotate/rollback,
  versions dialog, enable/disable, generate-value helper.
- Webhooks (/webhooks): CRUD, pause/resume, regenerate-secret with
  one-time reveal, send test event, deliveries dialog.
- Scheduled tasks (/scheduled-tasks): cron CRUD, run-now trigger,
  enable/disable, expandable run history.
- Audit log (/activity): replaces the empty stub. Filter by severity,
  resource type, date range; click for full JSON detail.

All endpoints are hand-rolled HTTP because most aren't covered by the
generated OpenAPI typed paths yet — switch to arcadia.typed.* when the
backend wires them into OpenApiSpex.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jules
2026-05-01 22:50:09 +10:00
parent 45fa130951
commit a907e25a7c
19 changed files with 7439 additions and 25 deletions

View File

@@ -0,0 +1,56 @@
// Per-user usage + quota helpers.
import type { ArcadiaClient } from "@crema/arcadia-client"
export interface UserUsage {
storage_used_bytes: number
object_count: number
}
export interface UserQuota {
id: string
tenant_id: string
user_id: string
storage_limit_bytes: number | null
storage_used_bytes: number
object_count_limit: number | null
object_count: number
storage_remaining: number | null
objects_remaining: number | null
storage_usage_percentage: number | null
object_count_usage_percentage: number | null
storage_exceeded: boolean
object_count_exceeded: boolean
quota_exceeded: boolean
metadata: Record<string, unknown>
last_calculated_at: string | null
inserted_at: string
updated_at: string
}
export async function getUserUsage(
arcadia: ArcadiaClient,
userId: string,
): Promise<UserUsage> {
const res = await arcadia.GET<{ data: UserUsage }>(
`/api/v1/users/${userId}/usage`,
)
return res.data
}
export async function getUserQuota(
arcadia: ArcadiaClient,
userId: string,
): Promise<UserQuota | null> {
try {
const res = await arcadia.GET<{ data: UserQuota }>(
`/api/v1/users/${userId}/quota`,
)
return res.data
} catch (err) {
// 404 == no quota set for this user. Treat as null rather than throwing.
const msg = err instanceof Error ? err.message : String(err)
if (/404|not[_ ]found/i.test(msg)) return null
throw err
}
}