import type { PreflightPreview, EnrollmentStorageRootSelection, ManagedStorageStatus, TrustedAppdataRoot, TrustedAppdataRootReadinessStatus, } from '@/types/appops'; export const LEGACY_APPDATA_ROOT: TrustedAppdataRoot = '/srv/example/appdata'; export const MANAGED_APPDATA_ROOT: TrustedAppdataRoot = '/var/lib/docker/volumes/itworx-managed-appdata/_data'; export const MANAGED_STORAGE_INITIALIZE_ACTION = 'controller.managed-storage.initialize'; export const MANAGED_STORAGE_TARGET = 'tower/deployment-controller'; export interface EnrollmentStorageView { configured: boolean; trustStatus: 'ready' | 'blocked' | 'needs_setup' | 'unknown'; currentRoot: TrustedAppdataRoot | null; backupAcknowledged: boolean; rootReadiness: Record; managedStatus: ManagedStorageStatus; managedBlocker: string | null; managedCapabilityValid: boolean; } const isRecord = (value: unknown): value is Record => ( typeof value === 'object' && value !== null && !Array.isArray(value) ); const isRoot = (value: unknown): value is TrustedAppdataRoot => ( value === LEGACY_APPDATA_ROOT || value === MANAGED_APPDATA_ROOT ); const isRootReadiness = (value: unknown): value is TrustedAppdataRootReadinessStatus => ( value === 'ready' || value === 'needs_initialization' || value === 'unavailable' ); const isManagedStatus = (value: unknown): value is ManagedStorageStatus => ( value === 'not_selected' || value === 'blocked' || value === 'ready' || value === 'initialization_required' || value === 'unavailable' ); export const buildEnrollmentStorageView = (trust: unknown, capabilities: unknown): EnrollmentStorageView => { const safeTrust = isRecord(trust) ? trust : {}; const safeCapabilities = isRecord(capabilities) ? capabilities : {}; const trustedRoot = isRoot(safeTrust.trustedAppdataRoot) ? safeTrust.trustedAppdataRoot : null; const managed = isRecord(safeCapabilities.managedStorage) ? safeCapabilities.managedStorage : null; const reportedRoots = safeCapabilities.availableTrustedAppdataRoots; let rootReadiness: Record = { [LEGACY_APPDATA_ROOT]: 'unavailable', [MANAGED_APPDATA_ROOT]: 'unavailable', }; let rootsValid = false; if (Array.isArray(reportedRoots) && reportedRoots.length === 2) { const entries = new Map(); const valid = reportedRoots.every((entry) => { if (!isRecord(entry) || !isRoot(entry.root) || !isRootReadiness(entry.status) || entries.has(entry.root)) return false; entries.set(entry.root, entry.status); return true; }); if (valid && entries.has(LEGACY_APPDATA_ROOT) && entries.has(MANAGED_APPDATA_ROOT)) { rootsValid = true; rootReadiness = { [LEGACY_APPDATA_ROOT]: entries.get(LEGACY_APPDATA_ROOT)!, [MANAGED_APPDATA_ROOT]: entries.get(MANAGED_APPDATA_ROOT)!, }; } } const backupAcknowledged = safeTrust.managedAppdataBackupAcknowledged; const trustSelectionConsistent = typeof backupAcknowledged === 'boolean' && ( trustedRoot === MANAGED_APPDATA_ROOT ? backupAcknowledged && managed?.root === MANAGED_APPDATA_ROOT && managed.backupAcknowledged === backupAcknowledged : trustedRoot === LEGACY_APPDATA_ROOT && !backupAcknowledged ); const managedStatusConsistent = Boolean( managed && ( (managed.status !== 'ready' || rootReadiness[MANAGED_APPDATA_ROOT] === 'ready') && (managed.status !== 'initialization_required' || rootReadiness[MANAGED_APPDATA_ROOT] === 'needs_initialization') && (trustedRoot !== MANAGED_APPDATA_ROOT || managed.status !== 'not_selected') && (trustedRoot !== MANAGED_APPDATA_ROOT || managed.backupAcknowledged === backupAcknowledged) ) ); const managedCapabilityValid = Boolean( rootsValid && trustSelectionConsistent && managed && managed.root === MANAGED_APPDATA_ROOT && managed.backupScope === 'dockerRootDir' && typeof managed.backupAcknowledged === 'boolean' && isManagedStatus(managed.status) && managedStatusConsistent, ); const rawTrustStatus = safeTrust.status === 'ready' || safeTrust.status === 'blocked' || safeTrust.status === 'needs_setup' ? safeTrust.status : 'unknown'; const configured = safeTrust.configured === true && trustedRoot !== null && trustSelectionConsistent; const trustStatus = rawTrustStatus === 'needs_setup' || rawTrustStatus === 'blocked' ? rawTrustStatus : configured && rawTrustStatus === 'ready' ? 'ready' : 'unknown'; return { configured, trustStatus, currentRoot: configured ? trustedRoot : null, backupAcknowledged: backupAcknowledged === true, rootReadiness, managedStatus: managedCapabilityValid ? managed!.status as ManagedStorageStatus : 'unavailable', managedBlocker: managedCapabilityValid && typeof managed!.blockerCode === 'string' && managed!.blockerCode.length <= 100 ? managed!.blockerCode || null : null, managedCapabilityValid, }; }; const isBoundedStringArray = (value: unknown): value is string[] => ( Array.isArray(value) && value.length <= 100 && value.every((item) => typeof item === 'string' && item.length <= 500) ); export const normalizeManagedStoragePreview = (value: unknown): PreflightPreview | null => { if (!isRecord(value)) return null; const rollbackAvailable = value.rollbackAvailable; if ( typeof value.action !== 'string' || value.action.length > 120 || typeof value.target !== 'string' || value.target.length > 200 || (value.risk !== 'low' && value.risk !== 'medium' && value.risk !== 'high' && value.risk !== 'critical') || (value.policyDecision !== 'allowed' && value.policyDecision !== 'approval_required' && value.policyDecision !== 'blocked') || typeof value.auditPreview !== 'string' || value.auditPreview.length > 2000 || !isBoundedStringArray(value.affectedApps) || !isBoundedStringArray(value.dependencies) || !isBoundedStringArray(value.preChecks) || !isBoundedStringArray(value.postChecks) || (rollbackAvailable !== undefined && typeof rollbackAvailable !== 'boolean') ) return null; const normalizedRollbackAvailable: boolean | undefined = typeof rollbackAvailable === 'boolean' ? rollbackAvailable : undefined; return { action: value.action, target: value.target, risk: value.risk, policyDecision: value.policyDecision, auditPreview: value.auditPreview, affectedApps: value.affectedApps, dependencies: value.dependencies, preChecks: value.preChecks, postChecks: value.postChecks, ...(normalizedRollbackAvailable === undefined ? {} : { rollbackAvailable: normalizedRollbackAvailable }), }; }; export const buildStorageRootSelection = ( root: TrustedAppdataRoot, backupAcknowledged: boolean, ): EnrollmentStorageRootSelection | null => { if (root === MANAGED_APPDATA_ROOT && !backupAcknowledged) return null; return { trusted_appdata_root: root, managed_appdata_backup_acknowledged: root === MANAGED_APPDATA_ROOT, }; }; export const canInitializeManagedStorage = ( view: EnrollmentStorageView, preview: unknown, ): boolean => ( view.configured && view.currentRoot === MANAGED_APPDATA_ROOT && view.backupAcknowledged && view.managedCapabilityValid && view.managedStatus === 'initialization_required' && isRecord(preview) && preview.action === MANAGED_STORAGE_INITIALIZE_ACTION && preview.target === MANAGED_STORAGE_TARGET && preview.risk === 'high' && preview.policyDecision === 'allowed' ); export interface ManagedStorageInitializationGate { canManage: boolean; view: EnrollmentStorageView | null | undefined; preview: unknown; dirty: boolean; loadError: string; conflict: string; refreshing: boolean; saving: boolean; initializing: boolean; } export const canRequestManagedStorageInitialization = (gate: ManagedStorageInitializationGate): boolean => ( gate.canManage && Boolean(gate.view && canInitializeManagedStorage(gate.view, gate.preview)) && !gate.dirty && !gate.loadError && !gate.conflict && !gate.refreshing && !gate.saving && !gate.initializing ); export interface StorageRootSaveGate { canManage: boolean; configured: boolean; payloadAvailable: boolean; dirty: boolean; loadError: string; refreshing: boolean; saving: boolean; initializing: boolean; } export const canSaveStorageRootSelection = (gate: StorageRootSaveGate): boolean => ( gate.canManage && gate.configured && gate.payloadAvailable && gate.dirty && !gate.loadError && !gate.refreshing && !gate.saving && !gate.initializing ); export interface ManagedStorageActionOutcome { tone: 'status' | 'alert'; message: string; } const UNCONFIRMED_INITIALIZATION_MESSAGE = 'Het initialisatieverzoek is niet bevestigd met geldige referenties.'; const isBoundedIdentifier = (value: unknown): value is string => ( typeof value === 'string' && value.trim().length > 0 && value.length <= 200 ); export const normalizeManagedStorageActionOutcome = (value: unknown): ManagedStorageActionOutcome => { if (!isRecord(value) || typeof value.message !== 'string' || value.message.trim().length === 0 || value.message.length > 500) { return { tone: 'alert', message: UNCONFIRMED_INITIALIZATION_MESSAGE }; } if (value.success === true && isBoundedIdentifier(value.jobId) && isBoundedIdentifier(value.auditId)) { return { tone: 'status', message: `Initialisatieverzoek geregistreerd. Job ${value.jobId.trim()}; audit ${value.auditId.trim()}. De opslag wordt pas als gereed getoond nadat de controller dat bevestigt.`, }; } if (value.success === false) { return { tone: 'alert', message: value.message.trim() }; } return { tone: 'alert', message: UNCONFIRMED_INITIALIZATION_MESSAGE }; }; export const rootReadinessLabel = (status: TrustedAppdataRootReadinessStatus): string => { if (status === 'ready') return 'Controller confirms ready'; if (status === 'needs_initialization') return 'Controller reports initialization needed'; return 'Not currently confirmed'; }; export const managedStorageStatusLabel = (status: ManagedStorageStatus): string => { switch (status) { case 'not_selected': return 'Not selected for future targets'; case 'blocked': return 'Blocked by a required safety condition'; case 'ready': return 'Controller reports ready'; case 'initialization_required': return 'Controller requires one-time initialization'; default: return 'Unavailable or not confirmed'; } }; export const managedStorageBlockerLabel = (blocker: string | null): string | null => { switch (blocker) { case 'managed_appdata_backup_acknowledgement_required': return 'Confirm the Docker-root backup responsibility before continuing.'; case 'managed_appdata_initialization_required': return 'The controller reports that the managed storage volume needs initialization.'; case 'managed_appdata_volume_unavailable': return 'The controller cannot currently verify the managed storage volume.'; default: return blocker ? 'The controller reports an additional storage blocker.' : null; } };