Publish curated Unraid-AppOps-Gateway source from ad0a390e431166c3198a2ac3bec010c2b44f2dea
This commit is contained in:
1 parent
29c1e3d5a1
commit
98d31477f9
35 files changed
+7264
-185
No files matched your search
@@ -0,0 +1,216 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
buildEnrollmentStorageView,
|
||||
buildStorageRootSelection,
|
||||
canInitializeManagedStorage,
|
||||
canRequestManagedStorageInitialization,
|
||||
canSaveStorageRootSelection,
|
||||
LEGACY_APPDATA_ROOT,
|
||||
MANAGED_APPDATA_ROOT,
|
||||
MANAGED_STORAGE_INITIALIZE_ACTION,
|
||||
MANAGED_STORAGE_TARGET,
|
||||
normalizeManagedStoragePreview,
|
||||
normalizeManagedStorageActionOutcome,
|
||||
} from './enrollmentStorageView.ts';
|
||||
|
||||
const baseTrust = {
|
||||
configured: true,
|
||||
status: 'ready',
|
||||
trustedAppdataRoot: MANAGED_APPDATA_ROOT,
|
||||
managedAppdataBackupAcknowledged: true,
|
||||
};
|
||||
const baseCapabilities = {
|
||||
availableTrustedAppdataRoots: [
|
||||
{ root: LEGACY_APPDATA_ROOT, status: 'ready' },
|
||||
{ root: MANAGED_APPDATA_ROOT, status: 'needs_initialization' },
|
||||
],
|
||||
managedStorage: {
|
||||
status: 'initialization_required',
|
||||
blockerCode: 'managed_appdata_initialization_required',
|
||||
root: MANAGED_APPDATA_ROOT,
|
||||
backupScope: 'dockerRootDir',
|
||||
backupAcknowledged: true,
|
||||
},
|
||||
};
|
||||
|
||||
test('malformed or unknown roots fail closed without echoing untrusted paths', () => {
|
||||
const view = buildEnrollmentStorageView({ ...baseTrust, trustedAppdataRoot: '/tmp/unknown-secret-path' }, {
|
||||
...baseCapabilities,
|
||||
availableTrustedAppdataRoots: [{ root: '/tmp/private', status: 'ready' }, ...baseCapabilities.availableTrustedAppdataRoots],
|
||||
});
|
||||
|
||||
assert.equal(view.currentRoot, null);
|
||||
assert.equal(view.configured, false);
|
||||
assert.equal(view.rootReadiness[LEGACY_APPDATA_ROOT], 'unavailable');
|
||||
assert.equal(view.rootReadiness[MANAGED_APPDATA_ROOT], 'unavailable');
|
||||
assert.equal(view.managedStatus, 'unavailable');
|
||||
assert.equal(view.managedCapabilityValid, false);
|
||||
});
|
||||
|
||||
test('readiness and saved selection fail closed when root evidence or acknowledgement contradicts', () => {
|
||||
const unavailableManagedRoot = buildEnrollmentStorageView(baseTrust, {
|
||||
...baseCapabilities,
|
||||
availableTrustedAppdataRoots: [
|
||||
{ root: LEGACY_APPDATA_ROOT, status: 'ready' },
|
||||
{ root: MANAGED_APPDATA_ROOT, status: 'unavailable' },
|
||||
],
|
||||
managedStorage: { ...baseCapabilities.managedStorage, status: 'ready' },
|
||||
});
|
||||
assert.equal(unavailableManagedRoot.managedStatus, 'unavailable');
|
||||
assert.equal(unavailableManagedRoot.managedCapabilityValid, false);
|
||||
|
||||
const contradictoryManagedAcknowledgement = buildEnrollmentStorageView(baseTrust, {
|
||||
...baseCapabilities,
|
||||
managedStorage: { ...baseCapabilities.managedStorage, backupAcknowledged: false },
|
||||
});
|
||||
assert.equal(contradictoryManagedAcknowledgement.configured, false);
|
||||
assert.equal(contradictoryManagedAcknowledgement.currentRoot, null);
|
||||
assert.equal(contradictoryManagedAcknowledgement.trustStatus, 'unknown');
|
||||
assert.equal(contradictoryManagedAcknowledgement.managedStatus, 'unavailable');
|
||||
|
||||
const malformedRootEvidence = buildEnrollmentStorageView(baseTrust, {
|
||||
...baseCapabilities,
|
||||
availableTrustedAppdataRoots: [{ root: LEGACY_APPDATA_ROOT, status: 'ready' }],
|
||||
managedStorage: { ...baseCapabilities.managedStorage, status: 'ready' },
|
||||
});
|
||||
assert.equal(malformedRootEvidence.managedStatus, 'unavailable');
|
||||
});
|
||||
|
||||
test('managed-root choice requires explicit backup acknowledgement and sends only the two contract fields', () => {
|
||||
assert.equal(buildStorageRootSelection(MANAGED_APPDATA_ROOT, false), null);
|
||||
assert.deepEqual(buildStorageRootSelection(MANAGED_APPDATA_ROOT, true), {
|
||||
trusted_appdata_root: MANAGED_APPDATA_ROOT,
|
||||
managed_appdata_backup_acknowledged: true,
|
||||
});
|
||||
assert.deepEqual(buildStorageRootSelection(LEGACY_APPDATA_ROOT, true), {
|
||||
trusted_appdata_root: LEGACY_APPDATA_ROOT,
|
||||
managed_appdata_backup_acknowledged: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('a failed refresh disables a dirty storage-root draft from being saved', () => {
|
||||
const gate = {
|
||||
canManage: true,
|
||||
configured: true,
|
||||
payloadAvailable: true,
|
||||
dirty: true,
|
||||
loadError: '',
|
||||
refreshing: false,
|
||||
saving: false,
|
||||
initializing: false,
|
||||
};
|
||||
|
||||
assert.equal(canSaveStorageRootSelection(gate), true);
|
||||
assert.equal(canSaveStorageRootSelection({ ...gate, loadError: 'Refresh failed; showing last-known configuration.' }), false);
|
||||
assert.equal(canSaveStorageRootSelection({ ...gate, refreshing: true }), false);
|
||||
assert.equal(canSaveStorageRootSelection({ ...gate, initializing: true }), false);
|
||||
});
|
||||
|
||||
test('initialization requires selected managed root, capability evidence, exact high-risk action and allowed manual policy', () => {
|
||||
const view = buildEnrollmentStorageView(baseTrust, baseCapabilities);
|
||||
const preview = {
|
||||
action: MANAGED_STORAGE_INITIALIZE_ACTION,
|
||||
target: MANAGED_STORAGE_TARGET,
|
||||
risk: 'high',
|
||||
policyDecision: 'allowed',
|
||||
};
|
||||
|
||||
assert.equal(canInitializeManagedStorage(view, preview), true);
|
||||
assert.equal(canInitializeManagedStorage(view, { ...preview, policyDecision: 'blocked' }), false);
|
||||
assert.equal(canInitializeManagedStorage(view, { ...preview, target: 'tower/other' }), false);
|
||||
assert.equal(canInitializeManagedStorage(view, { ...preview, risk: 'low' }), false);
|
||||
assert.equal(canInitializeManagedStorage(view, { ...view, policyDecision: 'allowed' }), false);
|
||||
assert.equal(canInitializeManagedStorage(
|
||||
buildEnrollmentStorageView({ ...baseTrust, managedAppdataBackupAcknowledged: false }, baseCapabilities),
|
||||
preview,
|
||||
), false);
|
||||
assert.equal(canInitializeManagedStorage(
|
||||
buildEnrollmentStorageView(baseTrust, { ...baseCapabilities, managedStorage: { ...baseCapabilities.managedStorage, status: 'ready' } }),
|
||||
preview,
|
||||
), false);
|
||||
});
|
||||
|
||||
test('initialization stays disabled during stale refresh, conflict recovery, dirty drafts and mutations', () => {
|
||||
const view = buildEnrollmentStorageView(baseTrust, baseCapabilities);
|
||||
const gate = {
|
||||
canManage: true,
|
||||
view,
|
||||
preview: {
|
||||
action: MANAGED_STORAGE_INITIALIZE_ACTION,
|
||||
target: MANAGED_STORAGE_TARGET,
|
||||
risk: 'high',
|
||||
policyDecision: 'allowed',
|
||||
},
|
||||
dirty: false,
|
||||
loadError: '',
|
||||
conflict: '',
|
||||
refreshing: false,
|
||||
saving: false,
|
||||
initializing: false,
|
||||
};
|
||||
|
||||
assert.equal(canRequestManagedStorageInitialization(gate), true);
|
||||
assert.equal(canRequestManagedStorageInitialization({ ...gate, loadError: 'Refresh failed; showing last known data.' }), false);
|
||||
assert.equal(canRequestManagedStorageInitialization({ ...gate, conflict: 'The saved selection changed.' }), false);
|
||||
assert.equal(canRequestManagedStorageInitialization({ ...gate, dirty: true }), false);
|
||||
assert.equal(canRequestManagedStorageInitialization({ ...gate, refreshing: true }), false);
|
||||
assert.equal(canRequestManagedStorageInitialization({ ...gate, saving: true }), false);
|
||||
assert.equal(canRequestManagedStorageInitialization({ ...gate, initializing: true }), false);
|
||||
assert.equal(canRequestManagedStorageInitialization({ ...gate, canManage: false }), false);
|
||||
});
|
||||
|
||||
test('malformed runtime policy preview is rejected before UI uses its arrays or text', () => {
|
||||
assert.equal(normalizeManagedStoragePreview({
|
||||
action: MANAGED_STORAGE_INITIALIZE_ACTION,
|
||||
target: MANAGED_STORAGE_TARGET,
|
||||
risk: 'high',
|
||||
policyDecision: 'allowed',
|
||||
auditPreview: 'Initialize managed storage',
|
||||
affectedApps: [],
|
||||
dependencies: [],
|
||||
preChecks: 'not-an-array',
|
||||
postChecks: [],
|
||||
}), null);
|
||||
assert.equal(normalizeManagedStoragePreview({
|
||||
action: MANAGED_STORAGE_INITIALIZE_ACTION,
|
||||
target: MANAGED_STORAGE_TARGET,
|
||||
risk: 'high',
|
||||
policyDecision: 'allowed',
|
||||
auditPreview: 42,
|
||||
affectedApps: [],
|
||||
dependencies: [],
|
||||
preChecks: [],
|
||||
postChecks: [],
|
||||
}), null);
|
||||
});
|
||||
|
||||
test('malformed initializer results never produce a success notice or stringify arbitrary messages', () => {
|
||||
const malformedResults: unknown[] = [
|
||||
{ success: true, jobId: {}, auditId: 'audit-1', message: 'Started' },
|
||||
{ success: true, jobId: 'job-1', auditId: ' ', message: 'Started' },
|
||||
{ success: true, jobId: `${' '.repeat(201)}job-1`, auditId: 'audit-1', message: 'Started' },
|
||||
{ success: true, jobId: 'job-1', auditId: `${' '.repeat(201)}audit-1`, message: 'Started' },
|
||||
{ success: true, jobId: 'job-1', auditId: 'audit-1', message: { detail: 'Started' } },
|
||||
{ success: true, jobId: 'job-1', auditId: 'audit-1' },
|
||||
{ success: 'true', jobId: 'job-1', auditId: 'audit-1', message: 'Started' },
|
||||
{ success: false, message: { detail: 'Denied' } },
|
||||
];
|
||||
|
||||
for (const result of malformedResults) {
|
||||
assert.deepEqual(normalizeManagedStorageActionOutcome(result), {
|
||||
tone: 'alert',
|
||||
message: 'Het initialisatieverzoek is niet bevestigd met geldige referenties.',
|
||||
});
|
||||
}
|
||||
|
||||
assert.deepEqual(normalizeManagedStorageActionOutcome({
|
||||
success: true,
|
||||
jobId: 'job-123',
|
||||
auditId: 'audit-456',
|
||||
message: 'Accepted',
|
||||
}), {
|
||||
tone: 'status',
|
||||
message: 'Initialisatieverzoek geregistreerd. Job job-123; audit audit-456. De opslag wordt pas als gereed getoond nadat de controller dat bevestigt.',
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,277 @@
|
||||
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<TrustedAppdataRoot, TrustedAppdataRootReadinessStatus>;
|
||||
managedStatus: ManagedStorageStatus;
|
||||
managedBlocker: string | null;
|
||||
managedCapabilityValid: boolean;
|
||||
}
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> => (
|
||||
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<TrustedAppdataRoot, TrustedAppdataRootReadinessStatus> = {
|
||||
[LEGACY_APPDATA_ROOT]: 'unavailable',
|
||||
[MANAGED_APPDATA_ROOT]: 'unavailable',
|
||||
};
|
||||
let rootsValid = false;
|
||||
|
||||
if (Array.isArray(reportedRoots) && reportedRoots.length === 2) {
|
||||
const entries = new Map<TrustedAppdataRoot, TrustedAppdataRootReadinessStatus>();
|
||||
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;
|
||||
}
|
||||
};
|
||||
Reference in new issue
Block a user