diff --git a/src/app/dashboard/storage/restore/restore-client.tsx b/src/app/dashboard/storage/restore/restore-client.tsx index cd88a28d..30b76c90 100644 --- a/src/app/dashboard/storage/restore/restore-client.tsx +++ b/src/app/dashboard/storage/restore/restore-client.tsx @@ -37,6 +37,7 @@ interface AdapterConfig { id: string; name: string; adapterId: string; + metadata?: string; } interface DbConfig { @@ -520,6 +521,15 @@ export function RestoreClient() { {sources .filter(s => { + // Filter out restore-excluded sources + try { + if (s.metadata) { + const meta = JSON.parse(s.metadata); + if (meta.isRestoreExcluded) return false; + } + } catch { } + + // Filter by source type compatibility if (!file?.sourceType) return true; const type = file.sourceType.toLowerCase(); const adapter = s.adapterId.toLowerCase(); diff --git a/src/components/adapter/adapter-form.tsx b/src/components/adapter/adapter-form.tsx index 2020dd40..4259be6c 100644 --- a/src/components/adapter/adapter-form.tsx +++ b/src/components/adapter/adapter-form.tsx @@ -59,6 +59,9 @@ export function AdapterForm({ type, adapters, onSuccess, initialData, onBack }: const initialMeta = initialData?.metadata ? JSON.parse(initialData.metadata) : {}; const [healthNotificationsDisabled, setHealthNotificationsDisabled] = useState(initialMeta.healthNotificationsDisabled === true); + // Exclude from restore (database only) + const [isRestoreExcluded, setIsRestoreExcluded] = useState(initialMeta.isRestoreExcluded === true); + // Credential profile assignments (Phase 4 - Generic Credential Profile System) const [primaryCredentialId, setPrimaryCredentialId] = useState(initialData?.primaryCredentialId ?? null); const [sshCredentialId, setSshCredentialId] = useState(initialData?.sshCredentialId ?? null); @@ -198,7 +201,7 @@ export function AdapterForm({ type, adapters, onSuccess, initialData, onBack }: // Build metadata with health notification preference for database/storage adapters const existingMeta = initialData?.metadata ? JSON.parse(initialData.metadata) : {}; const metadata = (type === 'database' || type === 'storage') - ? { ...existingMeta, healthNotificationsDisabled } + ? { ...existingMeta, healthNotificationsDisabled, ...(type === 'database' ? { isRestoreExcluded } : {}) } : existingMeta; const payload = { @@ -394,6 +397,8 @@ export function AdapterForm({ type, adapters, onSuccess, initialData, onBack }: detectedVersion={detectedVersion} healthNotificationsDisabled={healthNotificationsDisabled} onHealthNotificationsDisabledChange={setHealthNotificationsDisabled} + isRestoreExcluded={isRestoreExcluded} + onIsRestoreExcludedChange={setIsRestoreExcluded} primaryCredentialId={primaryCredentialId} sshCredentialId={sshCredentialId} onPrimaryChange={setPrimaryCredentialId} diff --git a/src/components/adapter/form-sections.tsx b/src/components/adapter/form-sections.tsx index 7bb4cac5..794c4a66 100644 --- a/src/components/adapter/form-sections.tsx +++ b/src/components/adapter/form-sections.tsx @@ -54,6 +54,8 @@ interface SectionProps extends CredentialPickerHostProps { detectedVersion?: string | null; healthNotificationsDisabled?: boolean; onHealthNotificationsDisabledChange?: (disabled: boolean) => void; + isRestoreExcluded?: boolean; + onIsRestoreExcludedChange?: (excluded: boolean) => void; } /** @@ -127,11 +129,37 @@ function HealthCheckNotificationSwitch({ ); } +function RestoreExcludedSwitch({ + excluded, + onChange, +}: { + excluded: boolean; + onChange: (excluded: boolean) => void; +}) { + return ( +
+
+ +

+ This source will not appear as a restore target when recovering backups. +

+
+ +
+ ); +} + export function DatabaseFormContent({ adapter, detectedVersion, healthNotificationsDisabled, onHealthNotificationsDisabledChange, + isRestoreExcluded, + onIsRestoreExcludedChange, primaryCredentialId, sshCredentialId, onPrimaryChange, @@ -200,6 +228,12 @@ export function DatabaseFormContent({ onChange={onHealthNotificationsDisabledChange} /> )} + {onIsRestoreExcludedChange && ( + + )} ) : ( @@ -258,6 +292,12 @@ export function DatabaseFormContent({ onChange={onHealthNotificationsDisabledChange} /> )} + {onIsRestoreExcludedChange && ( + + )} )} @@ -293,6 +333,8 @@ export function DatabaseFormContent({ detectedVersion={detectedVersion} healthNotificationsDisabled={healthNotificationsDisabled} onHealthNotificationsDisabledChange={onHealthNotificationsDisabledChange} + isRestoreExcluded={isRestoreExcluded} + onIsRestoreExcludedChange={onIsRestoreExcludedChange} primaryCredentialId={primaryCredentialId} sshCredentialId={sshCredentialId} onPrimaryChange={onPrimaryChange} @@ -364,6 +406,12 @@ export function DatabaseFormContent({ onChange={onHealthNotificationsDisabledChange} /> )} + {onIsRestoreExcludedChange && ( + + )} {isMSSQL && ( @@ -406,6 +454,8 @@ function SshAwareTabLayout({ detectedVersion, healthNotificationsDisabled, onHealthNotificationsDisabledChange, + isRestoreExcluded, + onIsRestoreExcludedChange, primaryCredentialId, sshCredentialId, onPrimaryChange, @@ -418,6 +468,8 @@ function SshAwareTabLayout({ detectedVersion?: string | null; healthNotificationsDisabled?: boolean; onHealthNotificationsDisabledChange?: (disabled: boolean) => void; + isRestoreExcluded?: boolean; + onIsRestoreExcludedChange?: (excluded: boolean) => void; } & CredentialPickerHostProps) { return (
@@ -478,6 +530,12 @@ function SshAwareTabLayout({ onChange={onHealthNotificationsDisabledChange} /> )} + {onIsRestoreExcludedChange && ( + + )} ) : ( @@ -515,6 +573,12 @@ function SshAwareTabLayout({ onChange={onHealthNotificationsDisabledChange} /> )} + {onIsRestoreExcludedChange && ( + + )} )}