@futo-org/backups-orchestrator-ui 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/backends/BackendItem.svelte +28 -24
- package/dist/components/backends/BackendsList.svelte +33 -34
- package/dist/components/backends/BackendsList.svelte.d.ts +1 -0
- package/dist/components/backends/dialogs/SelectBackendModal.svelte +44 -55
- package/dist/components/backups/BackupItem.svelte +33 -26
- package/dist/components/backups/BackupStatus.svelte +200 -0
- package/dist/components/backups/BackupStatus.svelte.d.ts +19 -0
- package/dist/components/backups/BackupsList.svelte +17 -11
- package/dist/components/backups/dialogs/RestoreSnapshotModal.svelte +2 -2
- package/dist/components/backups/dialogs/SelectRepositoryModal.svelte +26 -36
- package/dist/components/backups/dialogs/ViewStatusModal.svelte +175 -0
- package/dist/components/backups/dialogs/ViewStatusModal.svelte.d.ts +8 -0
- package/dist/components/backups/run-history/RepositoryRunHistory.svelte +22 -10
- package/dist/components/backups/run-history/RepositoryRunHistory.svelte.d.ts +2 -0
- package/dist/components/backups/run-history/RepositoryRunHistoryItem.svelte +46 -20
- package/dist/components/backups/run-history/RepositoryRunHistoryPage.svelte +135 -0
- package/dist/components/backups/run-history/RepositoryRunHistoryPage.svelte.d.ts +8 -0
- package/dist/components/backups/run-history/RunHistoryModal.svelte +2 -2
- package/dist/components/backups/snapshots-list/RepositorySnapshotsList.svelte +22 -10
- package/dist/components/backups/snapshots-list/RepositorySnapshotsList.svelte.d.ts +2 -0
- package/dist/components/backups/snapshots-list/RepositorySnapshotsListItem.svelte +23 -21
- package/dist/components/backups/snapshots-list/RepositorySnapshotsPage.svelte +115 -0
- package/dist/components/backups/snapshots-list/RepositorySnapshotsPage.svelte.d.ts +9 -0
- package/dist/components/backups/snapshots-list/SnapshotsListModal.svelte +2 -2
- package/dist/components/dashboard/Dashboard.svelte +14 -7
- package/dist/components/dashboard/Dashboard.svelte.d.ts +1 -1
- package/dist/components/dashboard/DashboardAvgBackupTime.svelte +1 -1
- package/dist/components/dashboard/DashboardBackupHealth.svelte +5 -9
- package/dist/components/dashboard/DashboardBackupHealth.svelte.d.ts +1 -1
- package/dist/components/dashboard/DashboardCurrentUsage.svelte +1 -1
- package/dist/components/dashboard/DashboardDailyBackupTime.svelte +1 -1
- package/dist/components/dashboard/DashboardInstall.svelte +1 -1
- package/dist/components/dashboard/DashboardRecentBackups.svelte +47 -78
- package/dist/components/dashboard/DashboardTotalStored.svelte +1 -1
- package/dist/components/integrations/immich/ImmichBackupSettings.svelte +12 -0
- package/dist/components/integrations/immich/ImmichBackupSettings.svelte.d.ts +18 -0
- package/dist/components/integrations/immich/ImmichBackupsCard.svelte +73 -0
- package/dist/components/integrations/immich/ImmichBackupsCard.svelte.d.ts +12 -0
- package/dist/components/integrations/immich/ImmichBackupsNavButton.svelte +55 -0
- package/dist/components/integrations/immich/ImmichBackupsNavButton.svelte.d.ts +10 -0
- package/dist/components/integrations/immich/ImmichBackupsPage.svelte +15 -3
- package/dist/components/integrations/immich/ImmichBackupsPage.svelte.d.ts +7 -1
- package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte +66 -0
- package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte.d.ts +8 -0
- package/dist/components/integrations/immich/ImmichManageBackup.svelte +53 -14
- package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte +45 -71
- package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte +123 -82
- package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte.d.ts +1 -1
- package/dist/components/integrations/immich/settings/ImmichSettingsBackupContents.svelte +313 -0
- package/dist/components/integrations/immich/settings/ImmichSettingsBackupContents.svelte.d.ts +3 -0
- package/dist/components/integrations/immich/settings/ImmichSettingsSchedule.svelte +218 -0
- package/dist/components/integrations/immich/settings/ImmichSettingsSchedule.svelte.d.ts +3 -0
- package/dist/components/integrations/immich/settings/ImmichSettingsStorage.svelte +207 -0
- package/dist/components/integrations/immich/settings/ImmichSettingsStorage.svelte.d.ts +3 -0
- package/dist/components/onboarding/RecoveryKeyDisplay.svelte +34 -20
- package/dist/components/onboarding/SampleOnboarding.svelte +57 -38
- package/dist/components/onboarding/stages/OnboardingStageBackupServices.svelte +10 -24
- package/dist/components/onboarding/steps/OnboardingStep1FinishSetup.svelte +44 -0
- package/dist/components/onboarding/steps/OnboardingStep1FinishSetup.svelte.d.ts +7 -0
- package/dist/components/onboarding/steps/OnboardingStep2ConnectAccount.svelte +50 -0
- package/dist/components/onboarding/steps/OnboardingStep2ConnectAccount.svelte.d.ts +7 -0
- package/dist/components/onboarding/steps/OnboardingStep3SaveRecoveryKey.svelte +45 -0
- package/dist/components/onboarding/steps/OnboardingStep3SaveRecoveryKey.svelte.d.ts +8 -0
- package/dist/components/onboarding/steps/OnboardingStep4FirstBackup.svelte +67 -0
- package/dist/components/onboarding/steps/OnboardingStep4FirstBackup.svelte.d.ts +9 -0
- package/dist/components/onboarding/steps/OnboardingStepLayout.svelte +58 -0
- package/dist/components/onboarding/steps/OnboardingStepLayout.svelte.d.ts +19 -0
- package/dist/components/onboarding/upsell/UpsellBackupFeaturesGrid.svelte +52 -0
- package/dist/components/onboarding/upsell/UpsellBackupFeaturesGrid.svelte.d.ts +18 -0
- package/dist/components/onboarding/upsell/UpsellExplainFutoBackups.svelte +33 -0
- package/dist/components/onboarding/upsell/UpsellExplainFutoBackups.svelte.d.ts +8 -0
- package/dist/components/onboarding/upsell/UpsellFrequentlyAskedQuestions.svelte +27 -0
- package/dist/components/onboarding/upsell/UpsellFrequentlyAskedQuestions.svelte.d.ts +10 -0
- package/dist/components/onboarding/upsell/UpsellFutoBackupsBadge.svelte +7 -0
- package/dist/components/onboarding/upsell/UpsellFutoBackupsBadge.svelte.d.ts +18 -0
- package/dist/components/onboarding/upsell/UpsellHowItWorks.svelte +46 -0
- package/dist/components/onboarding/upsell/UpsellHowItWorks.svelte.d.ts +6 -0
- package/dist/components/onboarding/upsell/UpsellModal.svelte +43 -0
- package/dist/components/onboarding/upsell/UpsellModal.svelte.d.ts +9 -0
- package/dist/components/onboarding/upsell/UpsellPage.svelte +39 -0
- package/dist/components/onboarding/upsell/UpsellPage.svelte.d.ts +13 -0
- package/dist/components/onboarding/upsell/UpsellProtectYourImmichLibrary.svelte +58 -0
- package/dist/components/onboarding/upsell/UpsellProtectYourImmichLibrary.svelte.d.ts +8 -0
- package/dist/components/schedules/RepositoryPicker.svelte +45 -38
- package/dist/components/schedules/ScheduleItem.svelte +32 -26
- package/dist/components/schedules/ScheduleList.svelte +16 -10
- package/dist/components/settings/GlobalSettings.svelte +8 -0
- package/dist/components/settings/GlobalSettings.svelte.d.ts +18 -0
- package/dist/components/settings/SettingsBackends.svelte +16 -0
- package/dist/components/settings/SettingsBackends.svelte.d.ts +18 -0
- package/dist/components/settings/SettingsBilling.svelte +69 -0
- package/dist/components/settings/SettingsBilling.svelte.d.ts +6 -0
- package/dist/components/settings/SettingsNotifications.svelte +47 -0
- package/dist/components/settings/SettingsNotifications.svelte.d.ts +6 -0
- package/dist/components/test/ImmichTestUi.svelte +129 -27
- package/dist/components/test/TestUi.svelte +4 -4
- package/dist/components/test/dashboard/ActiveJobs.svelte +2 -2
- package/dist/components/test/immich/MockImmichPhotos.svelte +36 -0
- package/dist/components/test/immich/MockImmichPhotos.svelte.d.ts +18 -0
- package/dist/components/test/immich/MockImmichPurchaseInfo.svelte +29 -0
- package/dist/components/test/immich/MockImmichPurchaseInfo.svelte.d.ts +18 -0
- package/dist/components/test/immich/MockImmichStorageSpace.svelte +37 -0
- package/dist/components/test/immich/MockImmichStorageSpace.svelte.d.ts +6 -0
- package/dist/components/ui/Accordion.svelte +67 -0
- package/dist/components/ui/Accordion.svelte.d.ts +11 -0
- package/dist/components/ui/ListToolbar.svelte +60 -0
- package/dist/components/ui/ListToolbar.svelte.d.ts +16 -0
- package/dist/components/ui/PageLayout.svelte +21 -9
- package/dist/components/ui/PageLayout.svelte.d.ts +1 -0
- package/dist/components/ui/Pagination.svelte +80 -0
- package/dist/components/ui/Pagination.svelte.d.ts +8 -0
- package/dist/components/ui/PathListField.svelte +17 -14
- package/dist/components/ui/StackList.svelte +18 -18
- package/dist/components/ui/StackList.svelte.d.ts +6 -27
- package/dist/components/ui/StackListItem.svelte +47 -25
- package/dist/components/ui/StackListItem.svelte.d.ts +5 -2
- package/dist/components/ui/StackListOption.svelte +33 -0
- package/dist/components/ui/StackListOption.svelte.d.ts +11 -0
- package/dist/components/ui/StackListPlaceholder.svelte +12 -0
- package/dist/components/ui/StackListPlaceholder.svelte.d.ts +7 -0
- package/dist/components/ui/StepNumber.svelte +20 -0
- package/dist/components/ui/StepNumber.svelte.d.ts +7 -0
- package/dist/components/util/Suspense.svelte +7 -3
- package/dist/components/util/YuccaContext.svelte +1 -1
- package/dist/events.js +1 -1
- package/dist/fetch-client.d.ts +2 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/services/backend.service.js +3 -0
- package/dist/services/immich.integration.service.d.ts +1 -1
- package/dist/services/immich.integration.service.js +2 -3
- package/dist/services/integrations.service.d.ts +1 -0
- package/dist/services/integrations.service.js +2 -1
- package/dist/services/log.service.svelte.d.ts +12 -0
- package/dist/services/log.service.svelte.js +15 -1
- package/dist/services/repository.service.js +9 -3
- package/dist/services/runHistory.service.js +2 -2
- package/dist/utils/format.d.ts +1 -1
- package/dist/utils/format.js +2 -2
- package/package.json +3 -3
- package/dist/components/backups/dialogs/ViewLogModal.svelte +0 -208
- package/dist/components/backups/dialogs/ViewLogModal.svelte.d.ts +0 -7
- package/dist/components/integrations/immich/ImmichConfigureBackup.svelte +0 -403
- package/dist/components/integrations/immich/ImmichConfigureBackup.svelte.d.ts +0 -9
- package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte +0 -80
- package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte.d.ts +0 -8
- package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte +0 -56
- package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte.d.ts +0 -9
- package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte +0 -50
- package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte.d.ts +0 -7
- package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte +0 -44
- package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte.d.ts +0 -8
- package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte +0 -56
- package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte.d.ts +0 -9
- package/dist/components/util/TimedButton.svelte +0 -37
- package/dist/components/util/TimedButton.svelte.d.ts +0 -7
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
getBackendActions,
|
|
10
10
|
handleStartYuccaLogin,
|
|
11
11
|
} from "../../services/backend.service";
|
|
12
|
-
import { Badge, Icon
|
|
12
|
+
import { Badge, Icon } from "@immich/ui";
|
|
13
13
|
import { mdiCloudOutline, mdiHarddisk, mdiShieldCheckOutline } from "@mdi/js";
|
|
14
14
|
import StackListItem from "../ui/StackListItem.svelte";
|
|
15
15
|
|
|
@@ -33,6 +33,22 @@
|
|
|
33
33
|
s3: "S3 Server",
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
const BackendDescriptions: Record<BackendType, string> = {
|
|
37
|
+
yucca: "Hosted cloud backup storage",
|
|
38
|
+
local: "A folder on this computer",
|
|
39
|
+
s3: "An S3-compatible server",
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const title = $derived(
|
|
43
|
+
repositoryBackend?.primary
|
|
44
|
+
? `${BackendNames[backend.type]} (primary)`
|
|
45
|
+
: BackendNames[backend.type],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const online = $derived(
|
|
49
|
+
backend.isOnline && (!repositoryBackend || repositoryBackend.online),
|
|
50
|
+
);
|
|
51
|
+
|
|
36
52
|
const { LoginAgain, Reconfigure } = $derived(
|
|
37
53
|
getBackendActions(repository, backend, repositoryBackend, () =>
|
|
38
54
|
handleStartYuccaLogin(),
|
|
@@ -40,34 +56,22 @@
|
|
|
40
56
|
);
|
|
41
57
|
</script>
|
|
42
58
|
|
|
43
|
-
<StackListItem actions={[LoginAgain, Reconfigure]}>
|
|
59
|
+
<StackListItem {title} actions={[LoginAgain, Reconfigure]}>
|
|
44
60
|
{#snippet icon()}
|
|
45
61
|
<Icon icon={BackendIcons[backend.type]} />
|
|
46
62
|
{/snippet}
|
|
47
63
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
{#if repositoryBackend?.primary}
|
|
51
|
-
<Text size="small">(primary)</Text>
|
|
52
|
-
{/if}
|
|
64
|
+
{backend.description ?? BackendDescriptions[backend.type]}
|
|
53
65
|
|
|
54
66
|
{#snippet trailing()}
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
: "Missing on service"
|
|
65
|
-
: "Offline"}</Badge
|
|
66
|
-
>
|
|
67
|
-
{:else}
|
|
68
|
-
<Badge color={backend.isOnline ? "success" : "danger"} size="small"
|
|
69
|
-
>{backend.isOnline ? "Online" : "Offline"}</Badge
|
|
70
|
-
>
|
|
71
|
-
{/if}
|
|
67
|
+
<Badge color={online ? "primary" : "danger"} size="small">
|
|
68
|
+
{#if !backend.isOnline}
|
|
69
|
+
Offline
|
|
70
|
+
{:else if repositoryBackend}
|
|
71
|
+
{repositoryBackend.online ? "Active" : "Missing on service"}
|
|
72
|
+
{:else}
|
|
73
|
+
Online
|
|
74
|
+
{/if}
|
|
75
|
+
</Badge>
|
|
72
76
|
{/snippet}
|
|
73
77
|
</StackListItem>
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
|
|
16
16
|
type Props = {
|
|
17
17
|
repository?: LocalRepositoryDto;
|
|
18
|
+
actions?: boolean;
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
const { repository }: Props = $props();
|
|
21
|
+
const { repository, actions = false }: Props = $props();
|
|
21
22
|
|
|
22
23
|
const { advanced } = options;
|
|
23
24
|
const query = useBackends();
|
|
@@ -38,39 +39,37 @@
|
|
|
38
39
|
|
|
39
40
|
<OnEvents {onBackendCreate} />
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
{#if repository}
|
|
45
|
-
Where your backup is stored
|
|
46
|
-
{:else}
|
|
47
|
-
Configured backends
|
|
48
|
-
{/if}
|
|
49
|
-
{/snippet}
|
|
42
|
+
{#snippet heading()}
|
|
43
|
+
Where your backup is stored
|
|
44
|
+
{/snippet}
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)}
|
|
46
|
+
<StackList title={repository ? heading : undefined}>
|
|
47
|
+
<Suspense {query}>
|
|
48
|
+
{#snippet children(backends)}
|
|
49
|
+
{#each backends as backend (backend.id)}
|
|
50
|
+
{@const repositoryBackend = repositoryBackends.find(
|
|
51
|
+
({ id }) => backend.id === id,
|
|
52
|
+
)}
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
{#if typeof repository === typeof repositoryBackend}
|
|
55
|
+
<BackendItem {repository} {backend} {repositoryBackend} />
|
|
56
|
+
{/if}
|
|
57
|
+
{/each}
|
|
58
|
+
{/snippet}
|
|
59
|
+
</Suspense>
|
|
60
|
+
</StackList>
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
</Suspense>
|
|
62
|
+
{#if actions || $advanced}
|
|
63
|
+
<HStack>
|
|
64
|
+
<Button
|
|
65
|
+
size="small"
|
|
66
|
+
variant="outline"
|
|
67
|
+
onclick={() => handleStartYuccaLogin()}>Login with FUTO Backups</Button
|
|
68
|
+
>
|
|
69
|
+
<Button
|
|
70
|
+
size="small"
|
|
71
|
+
variant="outline"
|
|
72
|
+
onclick={() => handleSetupLocalStorage()}>New local storage</Button
|
|
73
|
+
>
|
|
74
|
+
</HStack>
|
|
75
|
+
{/if}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import StackList from "../../ui/StackList.svelte";
|
|
3
|
-
import
|
|
3
|
+
import StackListOption from "../../ui/StackListOption.svelte";
|
|
4
4
|
import Suspense from "../../util/Suspense.svelte";
|
|
5
5
|
import {
|
|
6
6
|
handleSetupLocalStorage,
|
|
@@ -15,9 +15,8 @@
|
|
|
15
15
|
ModalBody,
|
|
16
16
|
ModalFooter,
|
|
17
17
|
Stack,
|
|
18
|
-
Text,
|
|
19
18
|
} from "@immich/ui";
|
|
20
|
-
import {
|
|
19
|
+
import { mdiHarddisk, mdiShieldCheck } from "@mdi/js";
|
|
21
20
|
import type { Snippet } from "svelte";
|
|
22
21
|
|
|
23
22
|
type Props = {
|
|
@@ -61,62 +60,52 @@
|
|
|
61
60
|
<Stack>
|
|
62
61
|
{@render leadingContent?.()}
|
|
63
62
|
|
|
64
|
-
<
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
<StackList>
|
|
64
|
+
<StackListOption
|
|
65
|
+
{disabled}
|
|
66
|
+
title="FUTO Backups"
|
|
67
|
+
onclick={onFutoBackups}
|
|
68
|
+
>
|
|
69
|
+
{#snippet icon()}
|
|
70
|
+
<Icon icon={mdiShieldCheck} />
|
|
71
|
+
{/snippet}
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
Simple, hosted backups.
|
|
74
|
+
</StackListOption>
|
|
75
|
+
<StackListOption
|
|
76
|
+
{disabled}
|
|
77
|
+
title="Local Storage"
|
|
78
|
+
onclick={onLocalBackups}
|
|
79
|
+
>
|
|
80
|
+
{#snippet icon()}
|
|
81
|
+
<Icon icon={mdiHarddisk} />
|
|
82
|
+
{/snippet}
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
{/snippet}
|
|
79
|
-
</StackListItem>
|
|
80
|
-
<StackListItem {disabled} onclick={onLocalBackups}>
|
|
81
|
-
{#snippet icon()}
|
|
82
|
-
<Icon icon={mdiHarddisk} size="36px" />
|
|
83
|
-
{/snippet}
|
|
84
|
+
A folder on this computer.
|
|
85
|
+
</StackListOption>
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
<Suspense query={backends}>
|
|
88
|
+
{#snippet children(available)}
|
|
89
|
+
{#each available as backend (backend.id)}
|
|
90
|
+
{#if backend.type === "local"}
|
|
91
|
+
<StackListOption
|
|
92
|
+
{disabled}
|
|
93
|
+
title="Existing Local Storage"
|
|
94
|
+
onclick={() => {
|
|
95
|
+
onSelect(backend.id);
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
{#snippet icon()}
|
|
99
|
+
<Icon icon={mdiHarddisk} />
|
|
100
|
+
{/snippet}
|
|
89
101
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
<StackListItem
|
|
98
|
-
{disabled}
|
|
99
|
-
onclick={() => {
|
|
100
|
-
onSelect(backend.id);
|
|
101
|
-
}}
|
|
102
|
-
>
|
|
103
|
-
{#snippet icon()}
|
|
104
|
-
<Icon icon={mdiHarddisk} size="36px" />
|
|
105
|
-
{/snippet}
|
|
106
|
-
|
|
107
|
-
<Stack gap={0}>
|
|
108
|
-
<Text class="font-bold">Existing Local Storage</Text>
|
|
109
|
-
<Text>{backend.description}</Text>
|
|
110
|
-
</Stack>
|
|
111
|
-
|
|
112
|
-
{#snippet trailing()}
|
|
113
|
-
<Icon icon={mdiChevronRight} />
|
|
114
|
-
{/snippet}
|
|
115
|
-
</StackListItem>
|
|
116
|
-
{/if}
|
|
117
|
-
{/each}
|
|
118
|
-
</StackList>
|
|
119
|
-
</Suspense>
|
|
102
|
+
{backend.description}
|
|
103
|
+
</StackListOption>
|
|
104
|
+
{/if}
|
|
105
|
+
{/each}
|
|
106
|
+
{/snippet}
|
|
107
|
+
</Suspense>
|
|
108
|
+
</StackList>
|
|
120
109
|
</Stack>
|
|
121
110
|
</ModalBody>
|
|
122
111
|
<ModalFooter>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { LocalRepositoryDto } from "../../fetch-client";
|
|
3
3
|
import { getRepositoryActions } from "../../services/repository.service";
|
|
4
|
-
import { Badge, FormatBytes,
|
|
4
|
+
import { Badge, FormatBytes, Icon } from "@immich/ui";
|
|
5
|
+
import { mdiArchiveOutline } from "@mdi/js";
|
|
5
6
|
import StackListItem from "../ui/StackListItem.svelte";
|
|
6
7
|
import RelativeTime from "../util/RelativeTime.svelte";
|
|
7
8
|
|
|
@@ -11,48 +12,54 @@
|
|
|
11
12
|
|
|
12
13
|
const { repository }: Props = $props();
|
|
13
14
|
|
|
15
|
+
const BackendNames = {
|
|
16
|
+
yucca: "FUTO Backups",
|
|
17
|
+
local: "Local Storage",
|
|
18
|
+
s3: "S3 Server",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const failed = $derived(
|
|
22
|
+
repository.metrics.lastBackup &&
|
|
23
|
+
(!repository.metrics.lastSuccessfulBackup ||
|
|
24
|
+
+new Date(repository.metrics.lastBackup) >
|
|
25
|
+
+new Date(repository.metrics.lastSuccessfulBackup)),
|
|
26
|
+
);
|
|
27
|
+
|
|
14
28
|
const { BackupNow, Snapshots, History, Configure, Import, MetricsHistory } =
|
|
15
29
|
$derived(getRepositoryActions(repository));
|
|
16
30
|
</script>
|
|
17
31
|
|
|
18
32
|
<StackListItem
|
|
33
|
+
title={repository.name}
|
|
34
|
+
color={failed ? "danger" : "primary"}
|
|
19
35
|
actions={[BackupNow, Snapshots, History, Configure, Import, MetricsHistory]}
|
|
20
36
|
>
|
|
21
|
-
|
|
37
|
+
{#snippet icon()}
|
|
38
|
+
<Icon icon={mdiArchiveOutline} />
|
|
39
|
+
{/snippet}
|
|
22
40
|
|
|
23
41
|
{#if repository.backends}
|
|
24
|
-
|
|
25
|
-
{#if repository.backends.primary.type === "yucca"}
|
|
26
|
-
FUTO Backups
|
|
27
|
-
{:else if repository.backends.primary.type === "local"}
|
|
28
|
-
Local Storage
|
|
29
|
-
{:else}
|
|
30
|
-
S3 Server
|
|
31
|
-
{/if}
|
|
32
|
-
</Badge>
|
|
33
|
-
{#if !repository.backends.primary.online}
|
|
34
|
-
<Badge size="tiny" color="danger">Offline</Badge>
|
|
35
|
-
{/if}
|
|
36
|
-
{/if}
|
|
37
|
-
|
|
38
|
-
{#if repository.worm}
|
|
39
|
-
<Badge size="tiny" color="info">WORM</Badge>
|
|
42
|
+
{BackendNames[repository.backends.primary.type]} ·
|
|
40
43
|
{/if}
|
|
41
44
|
|
|
42
45
|
{#if repository.meter}
|
|
43
|
-
<
|
|
44
|
-
<FormatBytes bytes={repository.meter?.sizeBytes} />
|
|
45
|
-
</Badge>
|
|
46
|
+
<FormatBytes bytes={repository.meter.sizeBytes} />
|
|
46
47
|
{:else}
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
Estimated <FormatBytes bytes={repository.metrics.sizeBytes} />
|
|
49
|
+
{/if}
|
|
50
|
+
|
|
51
|
+
{#if repository.worm}
|
|
52
|
+
· write-only
|
|
50
53
|
{/if}
|
|
51
54
|
|
|
52
55
|
{#snippet trailing()}
|
|
53
|
-
{#if repository.
|
|
56
|
+
{#if repository.backends && !repository.backends.primary.online}
|
|
57
|
+
<Badge size="tiny" color="danger">Offline</Badge>
|
|
58
|
+
{/if}
|
|
59
|
+
|
|
60
|
+
{#if failed}
|
|
54
61
|
<Badge size="tiny" color="danger">
|
|
55
|
-
Failed <RelativeTime time={repository.metrics.lastBackup} />
|
|
62
|
+
Failed <RelativeTime time={repository.metrics.lastBackup!} />
|
|
56
63
|
</Badge>
|
|
57
64
|
{:else if repository.metrics.lastSuccessfulBackup}
|
|
58
65
|
<Badge size="tiny" color="success">
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
export type BackupStatusType = "backup" | "restore" | "forget";
|
|
3
|
+
|
|
4
|
+
export type BackupStatusState =
|
|
5
|
+
| "connecting"
|
|
6
|
+
| "running"
|
|
7
|
+
| "complete"
|
|
8
|
+
| "failed";
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<script lang="ts">
|
|
12
|
+
import RelativeTime from "../util/RelativeTime.svelte";
|
|
13
|
+
import {
|
|
14
|
+
Alert,
|
|
15
|
+
Button,
|
|
16
|
+
Heading,
|
|
17
|
+
HStack,
|
|
18
|
+
ProgressBar,
|
|
19
|
+
Scrollable,
|
|
20
|
+
Stack,
|
|
21
|
+
Text,
|
|
22
|
+
} from "@immich/ui";
|
|
23
|
+
import type { Snippet } from "svelte";
|
|
24
|
+
|
|
25
|
+
type Props = {
|
|
26
|
+
title: string;
|
|
27
|
+
type: BackupStatusType;
|
|
28
|
+
state: BackupStatusState;
|
|
29
|
+
progress?: number;
|
|
30
|
+
start?: string;
|
|
31
|
+
duration?: string;
|
|
32
|
+
errors?: string[];
|
|
33
|
+
details?: Snippet;
|
|
34
|
+
currentFiles?: string[];
|
|
35
|
+
advanced?: Snippet;
|
|
36
|
+
onRetry?: () => void;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
title,
|
|
41
|
+
type,
|
|
42
|
+
state: backupState,
|
|
43
|
+
progress = 0,
|
|
44
|
+
start,
|
|
45
|
+
duration,
|
|
46
|
+
errors = [],
|
|
47
|
+
details,
|
|
48
|
+
currentFiles = [],
|
|
49
|
+
advanced,
|
|
50
|
+
onRetry,
|
|
51
|
+
}: Props = $props();
|
|
52
|
+
|
|
53
|
+
let showErrors = $state(false);
|
|
54
|
+
|
|
55
|
+
const phases: Record<BackupStatusType, [string, string, string]> = {
|
|
56
|
+
backup: ["Preparing backup", "Backing up", "Finalizing backup"],
|
|
57
|
+
restore: ["Preparing restore", "Restoring", "Finalizing restore"],
|
|
58
|
+
forget: ["Preparing prune", "Pruning", "Finalizing prune"],
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const succeeded: Record<BackupStatusType, string> = {
|
|
62
|
+
backup: "Your library was backed up successfully",
|
|
63
|
+
restore: "Your library was restored successfully",
|
|
64
|
+
forget: "Old backups were pruned successfully",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const failed: Record<BackupStatusType, string> = {
|
|
68
|
+
backup: "Your library could not be backed up",
|
|
69
|
+
restore: "Your library could not be restored",
|
|
70
|
+
forget: "Old backups could not be pruned",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const reassurance: Record<BackupStatusType, string> = {
|
|
74
|
+
backup: "No changes were made to your existing backups.",
|
|
75
|
+
restore: "Your backups are untouched — nothing was lost.",
|
|
76
|
+
forget: "No backups were removed.",
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const running: Record<BackupStatusType, string> = {
|
|
80
|
+
backup:
|
|
81
|
+
"You can close this window and the backup will continue in the background.",
|
|
82
|
+
restore:
|
|
83
|
+
"You can close this window and the restore will continue in the background.",
|
|
84
|
+
forget:
|
|
85
|
+
"You can close this window and the prune will continue in the background.",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const phase = $derived(
|
|
89
|
+
progress <= 0
|
|
90
|
+
? phases[type][0]
|
|
91
|
+
: progress >= 0.95
|
|
92
|
+
? phases[type][2]
|
|
93
|
+
: phases[type][1],
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const headline = $derived.by(() => {
|
|
97
|
+
switch (backupState) {
|
|
98
|
+
case "connecting": {
|
|
99
|
+
return "Connecting…";
|
|
100
|
+
}
|
|
101
|
+
case "failed": {
|
|
102
|
+
return failed[type];
|
|
103
|
+
}
|
|
104
|
+
case "complete": {
|
|
105
|
+
return succeeded[type];
|
|
106
|
+
}
|
|
107
|
+
default: {
|
|
108
|
+
return `${phase} · ${Math.round(progress * 100)}%`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const titleColor = $derived(
|
|
114
|
+
backupState === "complete" ? "success" : backupState === "failed" ? "danger" : "primary",
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const terminal = $derived(backupState === "complete" || backupState === "failed");
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<Stack gap={4}>
|
|
121
|
+
<Stack gap={2}>
|
|
122
|
+
<Heading size="medium" color={titleColor} fontWeight="bold">{title}</Heading>
|
|
123
|
+
|
|
124
|
+
<Stack gap={1}>
|
|
125
|
+
<Heading size="small">{headline}</Heading>
|
|
126
|
+
|
|
127
|
+
{#if backupState === "running" && start}
|
|
128
|
+
<Text color="muted">Started <RelativeTime time={start} /></Text>
|
|
129
|
+
{:else if backupState === "failed"}
|
|
130
|
+
<Text color="muted">{reassurance[type]}</Text>
|
|
131
|
+
{:else if duration}
|
|
132
|
+
<Text color="muted">Completed in {duration}</Text>
|
|
133
|
+
{/if}
|
|
134
|
+
|
|
135
|
+
{#if backupState === "complete" && errors.length > 0}
|
|
136
|
+
<Text color="warning">
|
|
137
|
+
Completed with {errors.length}
|
|
138
|
+
{errors.length === 1 ? "warning" : "warnings"}.
|
|
139
|
+
</Text>
|
|
140
|
+
{/if}
|
|
141
|
+
</Stack>
|
|
142
|
+
</Stack>
|
|
143
|
+
|
|
144
|
+
{#if !terminal}
|
|
145
|
+
<ProgressBar
|
|
146
|
+
{progress}
|
|
147
|
+
shape="round"
|
|
148
|
+
size="tiny"
|
|
149
|
+
class="bg-primary-100 border-none"
|
|
150
|
+
/>
|
|
151
|
+
{/if}
|
|
152
|
+
|
|
153
|
+
{#if terminal && (errors.length > 0 || (backupState === "failed" && onRetry))}
|
|
154
|
+
<HStack gap={4} class="items-center">
|
|
155
|
+
{#if backupState === "failed" && onRetry}
|
|
156
|
+
<Button shape="round" onclick={onRetry}>Try again</Button>
|
|
157
|
+
{/if}
|
|
158
|
+
|
|
159
|
+
{#if errors.length > 0}
|
|
160
|
+
<Button
|
|
161
|
+
variant="ghost"
|
|
162
|
+
shape="round"
|
|
163
|
+
onclick={() => (showErrors = !showErrors)}
|
|
164
|
+
>
|
|
165
|
+
{showErrors ? "Hide details" : "View details"}
|
|
166
|
+
</Button>
|
|
167
|
+
{/if}
|
|
168
|
+
</HStack>
|
|
169
|
+
|
|
170
|
+
{#if showErrors}
|
|
171
|
+
<Scrollable class="max-h-32">
|
|
172
|
+
<Stack gap={1}>
|
|
173
|
+
{#each errors as error, index (index)}
|
|
174
|
+
<Alert color={backupState === "failed" ? "danger" : "warning"}>
|
|
175
|
+
{error}
|
|
176
|
+
</Alert>
|
|
177
|
+
{/each}
|
|
178
|
+
</Stack>
|
|
179
|
+
</Scrollable>
|
|
180
|
+
{/if}
|
|
181
|
+
{/if}
|
|
182
|
+
|
|
183
|
+
{#if backupState === "running"}
|
|
184
|
+
<Text color="muted">{running[type]}</Text>
|
|
185
|
+
{:else if backupState === "complete" && details}
|
|
186
|
+
<Text color="muted">{@render details()}</Text>
|
|
187
|
+
{/if}
|
|
188
|
+
|
|
189
|
+
{#if backupState === "running" && currentFiles.length > 0}
|
|
190
|
+
<Stack gap={1}>
|
|
191
|
+
{#each currentFiles.slice(0, 3) as file, index (index)}
|
|
192
|
+
<Text size="small" color="muted" class="truncate" title={file}>
|
|
193
|
+
{file}
|
|
194
|
+
</Text>
|
|
195
|
+
{/each}
|
|
196
|
+
</Stack>
|
|
197
|
+
{/if}
|
|
198
|
+
|
|
199
|
+
{@render advanced?.()}
|
|
200
|
+
</Stack>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type BackupStatusType = "backup" | "restore" | "forget";
|
|
2
|
+
export type BackupStatusState = "connecting" | "running" | "complete" | "failed";
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
type Props = {
|
|
5
|
+
title: string;
|
|
6
|
+
type: BackupStatusType;
|
|
7
|
+
state: BackupStatusState;
|
|
8
|
+
progress?: number;
|
|
9
|
+
start?: string;
|
|
10
|
+
duration?: string;
|
|
11
|
+
errors?: string[];
|
|
12
|
+
details?: Snippet;
|
|
13
|
+
currentFiles?: string[];
|
|
14
|
+
advanced?: Snippet;
|
|
15
|
+
onRetry?: () => void;
|
|
16
|
+
};
|
|
17
|
+
declare const BackupStatus: import("svelte").Component<Props, {}, "">;
|
|
18
|
+
type BackupStatus = ReturnType<typeof BackupStatus>;
|
|
19
|
+
export default BackupStatus;
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
useRepositories,
|
|
5
5
|
useRepositoryEventHandler,
|
|
6
6
|
} from "../../services/repository.service";
|
|
7
|
-
import { Button, HStack, modalManager, Stack
|
|
7
|
+
import { Button, HStack, modalManager, Stack } from "@immich/ui";
|
|
8
8
|
import StackList from "../ui/StackList.svelte";
|
|
9
|
+
import StackListPlaceholder from "../ui/StackListPlaceholder.svelte";
|
|
9
10
|
import OnEvents from "../util/OnEvents.svelte";
|
|
11
|
+
import Suspense from "../util/Suspense.svelte";
|
|
10
12
|
import BackupItem from "./BackupItem.svelte";
|
|
11
13
|
import CreateRepositoryModal from "./dialogs/CreateRepositoryModal.svelte";
|
|
12
14
|
|
|
@@ -39,18 +41,20 @@
|
|
|
39
41
|
<Stack gap={6}>
|
|
40
42
|
{#if local}
|
|
41
43
|
<Stack gap={2}>
|
|
42
|
-
<StackList
|
|
44
|
+
<StackList>
|
|
43
45
|
{#snippet title()}Backups on this machine{/snippet}
|
|
44
|
-
|
|
46
|
+
|
|
47
|
+
<Suspense {query}>
|
|
45
48
|
{#each localRepositories as repository (repository.id)}
|
|
46
49
|
<BackupItem {repository} />
|
|
47
50
|
{/each}
|
|
51
|
+
|
|
48
52
|
{#if localRepositories.length === 0}
|
|
49
|
-
<
|
|
53
|
+
<StackListPlaceholder>
|
|
50
54
|
No backups on this machine yet.
|
|
51
|
-
</
|
|
55
|
+
</StackListPlaceholder>
|
|
52
56
|
{/if}
|
|
53
|
-
|
|
57
|
+
</Suspense>
|
|
54
58
|
</StackList>
|
|
55
59
|
|
|
56
60
|
<HStack>
|
|
@@ -64,19 +68,21 @@
|
|
|
64
68
|
</Stack>
|
|
65
69
|
{/if}
|
|
66
70
|
|
|
67
|
-
<StackList
|
|
71
|
+
<StackList>
|
|
68
72
|
{#snippet title()}
|
|
69
73
|
{local ? "Backups found elsewhere" : "Your Backups"}
|
|
70
74
|
{/snippet}
|
|
71
|
-
|
|
75
|
+
|
|
76
|
+
<Suspense {query}>
|
|
72
77
|
{#each remoteRepositories as repository (repository.id)}
|
|
73
78
|
<BackupItem {repository} />
|
|
74
79
|
{/each}
|
|
80
|
+
|
|
75
81
|
{#if remoteRepositories.length === 0}
|
|
76
|
-
<
|
|
82
|
+
<StackListPlaceholder>
|
|
77
83
|
{local ? "No other backups found." : "No backups yet."}
|
|
78
|
-
</
|
|
84
|
+
</StackListPlaceholder>
|
|
79
85
|
{/if}
|
|
80
|
-
|
|
86
|
+
</Suspense>
|
|
81
87
|
</StackList>
|
|
82
88
|
</Stack>
|