// Arcadia storage configs API helpers. // // `GET /api/v1/storage_configs` and `POST /api/v1/storage_configs` are the // only operations with full OpenAPI coverage today. Update/delete and the // state-transition actions (activate, deactivate, mark-degraded, // mark-maintenance, set-default, validate) are listed in the spec but their // operations are still stubbed as `never`, so we hand-roll types and use the // generic `arcadia.GET` / `arcadia.POST` / etc. — same pattern as // `tenants.ts`. Switch to `arcadia.typed.*` when the spec gains coverage. import type { ArcadiaClient } from "@crema/arcadia-core-client" export type StorageBackend = "s3" | "local" | "gcs" export type StorageStatus = "active" | "inactive" | "degraded" | "maintenance" export interface StorageConfig { id: string tenant_id: string name: string backend_type: StorageBackend status: StorageStatus is_default: boolean max_file_size_bytes: number | null allowed_content_types: string[] | null // Backend-specific fields. Secret fields are returned as "***" by the API. config: Record inserted_at: string updated_at: string } export interface StorageConfigInput { name: string backend_type: StorageBackend config: Record is_default?: boolean max_file_size_bytes?: number | null allowed_content_types?: string[] } export interface StorageStats { total_objects: number total_size_bytes: number by_backend: Record by_user: Record } export interface StorageProvidersResponse { data: Record } export async function listStorageConfigs(arcadia: ArcadiaClient): Promise { const res = await arcadia.GET<{ data: StorageConfig[] }>("/api/v1/storage_configs") return res.data } export async function getStorageConfig(arcadia: ArcadiaClient, id: string): Promise { const res = await arcadia.GET<{ data: StorageConfig }>(`/api/v1/storage_configs/${id}`) return res.data } export async function createStorageConfig( arcadia: ArcadiaClient, input: StorageConfigInput, ): Promise { const res = await arcadia.POST<{ data: StorageConfig }>("/api/v1/storage_configs", { body: { storage_config: input }, }) return res.data } export async function updateStorageConfig( arcadia: ArcadiaClient, id: string, input: Partial, ): Promise { const res = await arcadia.PATCH<{ data: StorageConfig }>(`/api/v1/storage_configs/${id}`, { body: { storage_config: input }, }) return res.data } export async function deleteStorageConfig(arcadia: ArcadiaClient, id: string): Promise { await arcadia.DELETE(`/api/v1/storage_configs/${id}`) } export async function activateStorageConfig(arcadia: ArcadiaClient, id: string): Promise { const res = await arcadia.POST<{ data: StorageConfig }>(`/api/v1/storage_configs/${id}/activate`) return res.data } export async function deactivateStorageConfig(arcadia: ArcadiaClient, id: string): Promise { const res = await arcadia.POST<{ data: StorageConfig }>(`/api/v1/storage_configs/${id}/deactivate`) return res.data } export async function markStorageConfigDegraded( arcadia: ArcadiaClient, id: string, ): Promise { const res = await arcadia.POST<{ data: StorageConfig }>(`/api/v1/storage_configs/${id}/mark-degraded`) return res.data } export async function markStorageConfigMaintenance( arcadia: ArcadiaClient, id: string, ): Promise { const res = await arcadia.POST<{ data: StorageConfig }>( `/api/v1/storage_configs/${id}/mark-maintenance`, ) return res.data } export async function setDefaultStorageConfig( arcadia: ArcadiaClient, id: string, ): Promise { const res = await arcadia.POST<{ data: StorageConfig }>(`/api/v1/storage_configs/${id}/set-default`) return res.data } export interface ValidateResult { ok: boolean message?: string details?: unknown } export async function validateStorageConfig( arcadia: ArcadiaClient, id: string, ): Promise { return arcadia.POST(`/api/v1/storage_configs/${id}/validate`) } export async function getStorageStats(arcadia: ArcadiaClient): Promise { const res = await arcadia.GET<{ data: StorageStats }>("/api/v1/storage_configs/stats") return res.data } // Backend-specific config field schemas. Secret fields appear as "***" on // reads — the form treats them as write-only and only sends a value when the // user has typed a fresh one. export const SECRET_FIELDS: Record = { s3: ["secret_access_key"], gcs: ["service_account_json"], local: [], } export const REQUIRED_FIELDS: Record = { s3: ["bucket", "region", "access_key_id", "secret_access_key"], gcs: ["bucket", "service_account_json"], // Local backend's filesystem root. Backend changeset rejects "path" — must // be `base_path`. Keep this in sync with `Arcadia.Storage.Adapters.Local`. local: ["base_path"], } export const OPTIONAL_FIELDS: Record = { s3: ["endpoint", "prefix"], gcs: ["prefix"], local: [], } export function isSecretField(backend: StorageBackend, field: string): boolean { return SECRET_FIELDS[backend].includes(field) } export function isMaskedSecret(value: unknown): boolean { return typeof value === "string" && value === "***" }