@futo-org/backups-orchestrator-ui 0.27.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
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
Text,
|
|
17
17
|
} from "@immich/ui";
|
|
18
18
|
import { SvelteSet } from "svelte/reactivity";
|
|
19
|
-
import
|
|
19
|
+
import ViewStatusModal from "./ViewStatusModal.svelte";
|
|
20
20
|
|
|
21
21
|
type Props = {
|
|
22
22
|
repository: string;
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
onSuccess: ({ logId }) => {
|
|
48
48
|
onClose();
|
|
49
49
|
|
|
50
|
-
modalManager.open(
|
|
50
|
+
modalManager.open(ViewStatusModal, {
|
|
51
51
|
logId,
|
|
52
52
|
});
|
|
53
53
|
},
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import StackList from "../../ui/StackList.svelte";
|
|
3
|
-
import
|
|
3
|
+
import StackListOption from "../../ui/StackListOption.svelte";
|
|
4
|
+
import StackListPlaceholder from "../../ui/StackListPlaceholder.svelte";
|
|
5
|
+
import Suspense from "../../util/Suspense.svelte";
|
|
4
6
|
import { useInspectRepositories } from "../../../services/repository.service";
|
|
5
7
|
import {
|
|
6
8
|
Button,
|
|
7
9
|
HStack,
|
|
8
|
-
Icon,
|
|
9
10
|
Modal,
|
|
10
11
|
ModalBody,
|
|
11
12
|
ModalFooter,
|
|
12
13
|
Stack,
|
|
13
|
-
Text,
|
|
14
14
|
} from "@immich/ui";
|
|
15
|
-
import { mdiChevronRight } from "@mdi/js";
|
|
16
15
|
import type { Snippet } from "svelte";
|
|
17
16
|
|
|
18
17
|
type Props = {
|
|
@@ -54,41 +53,32 @@
|
|
|
54
53
|
<Stack>
|
|
55
54
|
{@render leadingContent?.()}
|
|
56
55
|
|
|
57
|
-
<StackList
|
|
58
|
-
{
|
|
59
|
-
{
|
|
56
|
+
<StackList>
|
|
57
|
+
<Suspense {query}>
|
|
58
|
+
{#each sortedRepositories ?? [] as repository (repository.id)}
|
|
59
|
+
{@const accessible = repository.snapshots !== undefined}
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
No backups yet
|
|
75
|
-
{/if}
|
|
76
|
-
</Text>
|
|
77
|
-
</Stack>
|
|
78
|
-
|
|
79
|
-
{#snippet trailing()}
|
|
80
|
-
{#if accessible}
|
|
81
|
-
<Icon icon={mdiChevronRight} />
|
|
61
|
+
<StackListOption
|
|
62
|
+
title={repository.name}
|
|
63
|
+
disabled={!accessible}
|
|
64
|
+
onclick={() => onSelect(repository.id)}
|
|
65
|
+
>
|
|
66
|
+
{#if !accessible}
|
|
67
|
+
Can't access, is your recovery key correct?
|
|
68
|
+
{:else if repository.snapshots.length}
|
|
69
|
+
Last backup: {new Date(
|
|
70
|
+
repository.snapshots[0].time,
|
|
71
|
+
).toLocaleDateString()}
|
|
72
|
+
{:else}
|
|
73
|
+
No backups yet
|
|
82
74
|
{/if}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{/each}
|
|
75
|
+
</StackListOption>
|
|
76
|
+
{/each}
|
|
86
77
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
{/if}
|
|
78
|
+
{#if (sortedRepositories ?? []).length === 0}
|
|
79
|
+
<StackListPlaceholder>No backups found.</StackListPlaceholder>
|
|
80
|
+
{/if}
|
|
81
|
+
</Suspense>
|
|
92
82
|
</StackList>
|
|
93
83
|
</Stack>
|
|
94
84
|
</ModalBody>
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import BackupStatus, {
|
|
3
|
+
type BackupStatusState,
|
|
4
|
+
type BackupStatusType,
|
|
5
|
+
} from "../BackupStatus.svelte";
|
|
6
|
+
import OnEvents from "../../util/OnEvents.svelte";
|
|
7
|
+
import { options } from "../../../options";
|
|
8
|
+
import { createLogObserver } from "../../../services/log.service.svelte";
|
|
9
|
+
import { useRun, useRunEventHandler } from "../../../services/runHistory.service";
|
|
10
|
+
import { formatDuration } from "../../../utils/format";
|
|
11
|
+
import {
|
|
12
|
+
CloseButton,
|
|
13
|
+
FormatBytes,
|
|
14
|
+
Heading,
|
|
15
|
+
HStack,
|
|
16
|
+
Modal,
|
|
17
|
+
ModalBody,
|
|
18
|
+
ModalHeader,
|
|
19
|
+
Scrollable,
|
|
20
|
+
Stack,
|
|
21
|
+
Text,
|
|
22
|
+
} from "@immich/ui";
|
|
23
|
+
import { DateTime } from "luxon";
|
|
24
|
+
import { onDestroy } from "svelte";
|
|
25
|
+
|
|
26
|
+
type Props = {
|
|
27
|
+
logId: string;
|
|
28
|
+
onClose: () => void;
|
|
29
|
+
onRetry?: () => void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const { logId, onClose, onRetry }: Props = $props();
|
|
33
|
+
|
|
34
|
+
const advanced = options.advanced;
|
|
35
|
+
const showAdvanced = $derived($advanced);
|
|
36
|
+
// svelte-ignore state_referenced_locally
|
|
37
|
+
const log = createLogObserver(logId);
|
|
38
|
+
// svelte-ignore state_referenced_locally
|
|
39
|
+
const runQuery = useRun(logId);
|
|
40
|
+
const run = $derived(runQuery.data);
|
|
41
|
+
const { onRunUpdate } = useRunEventHandler();
|
|
42
|
+
|
|
43
|
+
let now = $state(DateTime.now());
|
|
44
|
+
const tick = setInterval(() => (now = DateTime.now()), 1000);
|
|
45
|
+
|
|
46
|
+
onDestroy(() => {
|
|
47
|
+
clearInterval(tick);
|
|
48
|
+
log.destroy();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const type: BackupStatusType = $derived(
|
|
52
|
+
run?.type === "restore" || run?.type === "forget" ? run.type : "backup",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const backupState: BackupStatusState = $derived.by(() => {
|
|
56
|
+
if (!run) {
|
|
57
|
+
return "connecting";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (run.status === "incomplete") {
|
|
61
|
+
return "running";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return run.status === "failed" ? "failed" : "complete";
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const duration = $derived(
|
|
68
|
+
run
|
|
69
|
+
? formatDuration(
|
|
70
|
+
(run.end ? DateTime.fromISO(run.end) : now).toMillis() -
|
|
71
|
+
DateTime.fromISO(run.start).toMillis(),
|
|
72
|
+
"long",
|
|
73
|
+
)
|
|
74
|
+
: "",
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const titles: Record<BackupStatusType, Record<"running" | "done", string>> = {
|
|
78
|
+
backup: { running: "Backing up your library", done: "Backup" },
|
|
79
|
+
restore: { running: "Restoring your library", done: "Restore" },
|
|
80
|
+
forget: { running: "Pruning old backups", done: "Prune" },
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const retry = $derived(
|
|
84
|
+
onRetry
|
|
85
|
+
? () => {
|
|
86
|
+
onClose();
|
|
87
|
+
onRetry();
|
|
88
|
+
}
|
|
89
|
+
: undefined,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const title = $derived.by(() => {
|
|
93
|
+
switch (backupState) {
|
|
94
|
+
case "complete": {
|
|
95
|
+
return `${titles[type].done} complete`;
|
|
96
|
+
}
|
|
97
|
+
case "failed": {
|
|
98
|
+
return `${titles[type].done} failed`;
|
|
99
|
+
}
|
|
100
|
+
default: {
|
|
101
|
+
return titles[type].running;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<OnEvents {onRunUpdate} />
|
|
108
|
+
|
|
109
|
+
<Modal
|
|
110
|
+
size="medium"
|
|
111
|
+
{onClose}
|
|
112
|
+
// this is a bit of a hack to remove the frames from base modal...
|
|
113
|
+
// this one might be removed ; twin code is in UpsellModal
|
|
114
|
+
class="[&>div>div:first-child]:border-b-0 [&>div>div:first-child]:px-8 [&>div>div:first-child]:pb-0"
|
|
115
|
+
>
|
|
116
|
+
<ModalHeader>
|
|
117
|
+
<HStack fullWidth class="justify-end">
|
|
118
|
+
<CloseButton onclick={onClose} />
|
|
119
|
+
</HStack>
|
|
120
|
+
</ModalHeader>
|
|
121
|
+
|
|
122
|
+
<ModalBody class="px-8 pt-2 pb-8">
|
|
123
|
+
<BackupStatus
|
|
124
|
+
{title}
|
|
125
|
+
{type}
|
|
126
|
+
state={backupState}
|
|
127
|
+
progress={log.status.progress}
|
|
128
|
+
start={run?.start}
|
|
129
|
+
{duration}
|
|
130
|
+
errors={log.errors}
|
|
131
|
+
currentFiles={log.status.currentFiles}
|
|
132
|
+
onRetry={retry}
|
|
133
|
+
>
|
|
134
|
+
{#snippet details()}
|
|
135
|
+
{#if log.summary && type === "backup"}
|
|
136
|
+
{(log.summary.total_files_processed ?? 0).toLocaleString()} items backed
|
|
137
|
+
up ·
|
|
138
|
+
{(log.summary.files_new ?? 0).toLocaleString()} new items
|
|
139
|
+
{#if log.summary.total_bytes_processed !== undefined}
|
|
140
|
+
·
|
|
141
|
+
<FormatBytes bytes={log.summary.total_bytes_processed} /> processed
|
|
142
|
+
{/if}
|
|
143
|
+
{:else if log.summary && type === "restore"}
|
|
144
|
+
{(log.summary.files_restored ?? 0).toLocaleString()} items restored{#if log.summary.files_skipped}
|
|
145
|
+
· {log.summary.files_skipped.toLocaleString()} skipped
|
|
146
|
+
{/if}
|
|
147
|
+
{#if log.summary.bytes_restored !== undefined}
|
|
148
|
+
· <FormatBytes bytes={log.summary.bytes_restored} /> restored
|
|
149
|
+
{/if}
|
|
150
|
+
{:else if type === "forget"}
|
|
151
|
+
{log.pruned.removed.toLocaleString()}
|
|
152
|
+
{log.pruned.removed === 1 ? "backup" : "backups"} removed ·
|
|
153
|
+
{log.pruned.kept.toLocaleString()} kept
|
|
154
|
+
{/if}
|
|
155
|
+
{/snippet}
|
|
156
|
+
|
|
157
|
+
{#snippet advanced()}
|
|
158
|
+
{#if showAdvanced}
|
|
159
|
+
<Stack gap={1}>
|
|
160
|
+
<Heading size="small">Event Log</Heading>
|
|
161
|
+
<Scrollable class="h-80 overflow-x-hidden">
|
|
162
|
+
<Stack gap={1}>
|
|
163
|
+
{#each log.events as event, index (index)}
|
|
164
|
+
<Text size="tiny" class="font-mono select-all">
|
|
165
|
+
{JSON.stringify(event)}
|
|
166
|
+
</Text>
|
|
167
|
+
{/each}
|
|
168
|
+
</Stack>
|
|
169
|
+
</Scrollable>
|
|
170
|
+
</Stack>
|
|
171
|
+
{/if}
|
|
172
|
+
{/snippet}
|
|
173
|
+
</BackupStatus>
|
|
174
|
+
</ModalBody>
|
|
175
|
+
</Modal>
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import StackList from "../../ui/StackList.svelte";
|
|
3
|
+
import StackListPlaceholder from "../../ui/StackListPlaceholder.svelte";
|
|
4
|
+
import Suspense from "../../util/Suspense.svelte";
|
|
3
5
|
import OnEvents from "../../util/OnEvents.svelte";
|
|
4
6
|
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
5
7
|
import {
|
|
6
8
|
useRunEventHandler,
|
|
7
9
|
useRunHistory,
|
|
8
10
|
} from "../../../services/runHistory.service";
|
|
9
|
-
import {
|
|
11
|
+
import { Button } from "@immich/ui";
|
|
10
12
|
import RepositoryRunHistoryItem from "./RepositoryRunHistoryItem.svelte";
|
|
11
13
|
|
|
12
14
|
type Props = {
|
|
13
15
|
repository: LocalRepositoryDto;
|
|
16
|
+
limit?: number;
|
|
17
|
+
onViewAll?: () => void;
|
|
14
18
|
};
|
|
15
19
|
|
|
16
|
-
let { repository }: Props = $props();
|
|
20
|
+
let { repository, limit = 5, onViewAll }: Props = $props();
|
|
17
21
|
|
|
18
22
|
// svelte-ignore state_referenced_locally
|
|
19
23
|
const query = useRunHistory(repository.id);
|
|
@@ -22,18 +26,26 @@
|
|
|
22
26
|
|
|
23
27
|
<OnEvents {onRunCreate} {onRunUpdate} />
|
|
24
28
|
|
|
25
|
-
<StackList
|
|
29
|
+
<StackList>
|
|
26
30
|
{#snippet title()}
|
|
27
31
|
Recent backup attempts
|
|
28
32
|
{/snippet}
|
|
29
33
|
|
|
30
|
-
{#snippet
|
|
31
|
-
{#if
|
|
32
|
-
<
|
|
33
|
-
{:else}
|
|
34
|
-
{#each runs.slice(0, 5) as run (run.id)}
|
|
35
|
-
<RepositoryRunHistoryItem {run} />
|
|
36
|
-
{/each}
|
|
34
|
+
{#snippet action()}
|
|
35
|
+
{#if onViewAll && query.data?.length}
|
|
36
|
+
<Button variant="ghost" size="small" onclick={onViewAll}>View all</Button>
|
|
37
37
|
{/if}
|
|
38
38
|
{/snippet}
|
|
39
|
+
|
|
40
|
+
<Suspense {query}>
|
|
41
|
+
{#snippet children(runs)}
|
|
42
|
+
{#each runs.slice(0, limit) as run (run.id)}
|
|
43
|
+
<RepositoryRunHistoryItem {run} />
|
|
44
|
+
{/each}
|
|
45
|
+
|
|
46
|
+
{#if runs.length === 0}
|
|
47
|
+
<StackListPlaceholder>No recent backups</StackListPlaceholder>
|
|
48
|
+
{/if}
|
|
49
|
+
{/snippet}
|
|
50
|
+
</Suspense>
|
|
39
51
|
</StackList>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
2
2
|
type Props = {
|
|
3
3
|
repository: LocalRepositoryDto;
|
|
4
|
+
limit?: number;
|
|
5
|
+
onViewAll?: () => void;
|
|
4
6
|
};
|
|
5
7
|
declare const RepositoryRunHistory: import("svelte").Component<Props, {}, "">;
|
|
6
8
|
type RepositoryRunHistory = ReturnType<typeof RepositoryRunHistory>;
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
import { getRunActions } from "../../../services/runHistory.service";
|
|
6
6
|
import { Icon } from "@immich/ui";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
mdiCloudCheckOutline,
|
|
9
|
+
mdiCloudOffOutline,
|
|
10
|
+
mdiCloudSyncOutline,
|
|
11
11
|
} from "@mdi/js";
|
|
12
12
|
|
|
13
13
|
type Props = {
|
|
@@ -16,30 +16,56 @@
|
|
|
16
16
|
|
|
17
17
|
const { run }: Props = $props();
|
|
18
18
|
const { ViewLog } = $derived(getRunActions(run));
|
|
19
|
+
|
|
20
|
+
const nouns = {
|
|
21
|
+
restore: { name: "restore", running: "Restore", done: "Restored" },
|
|
22
|
+
forget: { name: "prune", running: "Prune", done: "Pruned" },
|
|
23
|
+
backup: { name: "backup", running: "Backup", done: "Backed up" },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const noun = $derived(
|
|
27
|
+
run.type === "restore" || run.type === "forget"
|
|
28
|
+
? nouns[run.type]
|
|
29
|
+
: nouns.backup,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const status = $derived.by(() => {
|
|
33
|
+
switch (run.status) {
|
|
34
|
+
case "failed": {
|
|
35
|
+
return {
|
|
36
|
+
title: `Failed ${noun.name}`,
|
|
37
|
+
color: "danger",
|
|
38
|
+
icon: mdiCloudOffOutline,
|
|
39
|
+
} as const;
|
|
40
|
+
}
|
|
41
|
+
case "incomplete": {
|
|
42
|
+
return {
|
|
43
|
+
title: `${noun.running} in progress`,
|
|
44
|
+
color: "primary",
|
|
45
|
+
icon: mdiCloudSyncOutline,
|
|
46
|
+
} as const;
|
|
47
|
+
}
|
|
48
|
+
default: {
|
|
49
|
+
return {
|
|
50
|
+
title: `Successful ${noun.name}`,
|
|
51
|
+
color: "success",
|
|
52
|
+
icon: mdiCloudCheckOutline,
|
|
53
|
+
} as const;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
19
57
|
</script>
|
|
20
58
|
|
|
21
|
-
<StackListItem actions={[ViewLog]}>
|
|
59
|
+
<StackListItem title={status.title} color={status.color} actions={[ViewLog]}>
|
|
22
60
|
{#snippet icon()}
|
|
23
|
-
{
|
|
24
|
-
<Icon icon={mdiCheckCircleOutline} class="text-success-500" />
|
|
25
|
-
{:else if run.status === "failed"}
|
|
26
|
-
<Icon icon={mdiAlertCircleOutline} class="text-danger-500" />
|
|
27
|
-
{:else}
|
|
28
|
-
<Icon icon={mdiLoading} class="animate-spin opacity-60" />
|
|
29
|
-
{/if}
|
|
61
|
+
<Icon icon={status.icon} />
|
|
30
62
|
{/snippet}
|
|
31
63
|
|
|
32
64
|
{#if run.status === "incomplete"}
|
|
33
|
-
|
|
34
|
-
up{/if} · started
|
|
35
|
-
<RelativeTime time={run.start} />
|
|
65
|
+
Started <RelativeTime time={run.start} />
|
|
36
66
|
{:else if run.status === "failed"}
|
|
37
|
-
{#if run.
|
|
38
|
-
failed
|
|
39
|
-
{#if run.end}<RelativeTime time={run.end} />{/if}
|
|
67
|
+
Attempted {#if run.end}<RelativeTime time={run.end} />{/if}
|
|
40
68
|
{:else}
|
|
41
|
-
{#if run.
|
|
42
|
-
up{/if}
|
|
43
|
-
{#if run.end}<RelativeTime time={run.end} />{/if}
|
|
69
|
+
{noun.done} {#if run.end}<RelativeTime time={run.end} />{/if}
|
|
44
70
|
{/if}
|
|
45
71
|
</StackListItem>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ListToolbar from "../../ui/ListToolbar.svelte";
|
|
3
|
+
import Pagination from "../../ui/Pagination.svelte";
|
|
4
|
+
import StackList from "../../ui/StackList.svelte";
|
|
5
|
+
import StackListPlaceholder from "../../ui/StackListPlaceholder.svelte";
|
|
6
|
+
import OnEvents from "../../util/OnEvents.svelte";
|
|
7
|
+
import Suspense from "../../util/Suspense.svelte";
|
|
8
|
+
import type { LocalRepositoryDto, RunDto } from "../../../fetch-client";
|
|
9
|
+
import {
|
|
10
|
+
useRunEventHandler,
|
|
11
|
+
useRunHistory,
|
|
12
|
+
} from "../../../services/runHistory.service";
|
|
13
|
+
import { Stack, type ActionItem } from "@immich/ui";
|
|
14
|
+
import { mdiRadioboxBlank, mdiRadioboxMarked } from "@mdi/js";
|
|
15
|
+
import RepositoryRunHistoryItem from "./RepositoryRunHistoryItem.svelte";
|
|
16
|
+
|
|
17
|
+
type Props = {
|
|
18
|
+
repository: LocalRepositoryDto;
|
|
19
|
+
pageSize?: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let { repository, pageSize = 10 }: Props = $props();
|
|
23
|
+
|
|
24
|
+
// svelte-ignore state_referenced_locally
|
|
25
|
+
const query = useRunHistory(repository.id);
|
|
26
|
+
const { onRunCreate, onRunUpdate } = useRunEventHandler();
|
|
27
|
+
|
|
28
|
+
let search = $state("");
|
|
29
|
+
let newestFirst = $state(true);
|
|
30
|
+
let status = $state<"all" | "complete" | "failed" | "incomplete">("all");
|
|
31
|
+
let page = $state(1);
|
|
32
|
+
|
|
33
|
+
const onSearch = (value: string) => {
|
|
34
|
+
search = value;
|
|
35
|
+
page = 1;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const onFilter = (value: typeof status) => {
|
|
39
|
+
status = value;
|
|
40
|
+
page = 1;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const sortOptions = [
|
|
44
|
+
{ newest: true, title: "Newest first" },
|
|
45
|
+
{ newest: false, title: "Oldest first" },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const statusOptions: { value: typeof status; title: string }[] = [
|
|
49
|
+
{ value: "all", title: "All attempts" },
|
|
50
|
+
{ value: "complete", title: "Successful only" },
|
|
51
|
+
{ value: "failed", title: "Failed only" },
|
|
52
|
+
{ value: "incomplete", title: "In progress only" },
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const tick = (selected: boolean) =>
|
|
56
|
+
selected ? mdiRadioboxMarked : mdiRadioboxBlank;
|
|
57
|
+
|
|
58
|
+
const sort = $derived({
|
|
59
|
+
label: newestFirst ? "Newest first" : "Oldest first",
|
|
60
|
+
items: sortOptions.map(
|
|
61
|
+
(option): ActionItem => ({
|
|
62
|
+
title: option.title,
|
|
63
|
+
icon: tick(newestFirst === option.newest),
|
|
64
|
+
onAction: () => (newestFirst = option.newest),
|
|
65
|
+
}),
|
|
66
|
+
),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const filters = $derived({
|
|
70
|
+
label:
|
|
71
|
+
statusOptions.find((option) => option.value === status)?.title ??
|
|
72
|
+
"Filters",
|
|
73
|
+
active: status !== "all",
|
|
74
|
+
items: statusOptions.map(
|
|
75
|
+
(option): ActionItem => ({
|
|
76
|
+
title: option.title,
|
|
77
|
+
icon: tick(status === option.value),
|
|
78
|
+
onAction: () => onFilter(option.value),
|
|
79
|
+
}),
|
|
80
|
+
),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const matches = (run: RunDto) => {
|
|
84
|
+
const term = search.trim().toLowerCase();
|
|
85
|
+
|
|
86
|
+
if (!term) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return `${run.type} ${run.status}`.toLowerCase().includes(term);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const filtered = $derived(
|
|
94
|
+
(query.data ?? [])
|
|
95
|
+
.filter((run) => status === "all" || run.status === status)
|
|
96
|
+
.filter(matches)
|
|
97
|
+
.toSorted((a, b) =>
|
|
98
|
+
newestFirst
|
|
99
|
+
? +new Date(b.start) - +new Date(a.start)
|
|
100
|
+
: +new Date(a.start) - +new Date(b.start),
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const pageCount = $derived(Math.max(Math.ceil(filtered.length / pageSize), 1));
|
|
105
|
+
const current = $derived(Math.min(page, pageCount));
|
|
106
|
+
const visible = $derived(
|
|
107
|
+
filtered.slice((current - 1) * pageSize, current * pageSize),
|
|
108
|
+
);
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
<OnEvents {onRunCreate} {onRunUpdate} />
|
|
112
|
+
|
|
113
|
+
<Stack gap={4}>
|
|
114
|
+
<ListToolbar
|
|
115
|
+
placeholder="Search backups"
|
|
116
|
+
{search}
|
|
117
|
+
{onSearch}
|
|
118
|
+
{sort}
|
|
119
|
+
{filters}
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<StackList>
|
|
123
|
+
<Suspense {query}>
|
|
124
|
+
{#each visible as run (run.id)}
|
|
125
|
+
<RepositoryRunHistoryItem {run} />
|
|
126
|
+
{/each}
|
|
127
|
+
|
|
128
|
+
{#if filtered.length === 0}
|
|
129
|
+
<StackListPlaceholder>No backups found</StackListPlaceholder>
|
|
130
|
+
{/if}
|
|
131
|
+
</Suspense>
|
|
132
|
+
</StackList>
|
|
133
|
+
|
|
134
|
+
<Pagination page={current} {pageCount} onChange={(next) => (page = next)} />
|
|
135
|
+
</Stack>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
2
|
+
type Props = {
|
|
3
|
+
repository: LocalRepositoryDto;
|
|
4
|
+
pageSize?: number;
|
|
5
|
+
};
|
|
6
|
+
declare const RepositoryRunHistoryPage: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type RepositoryRunHistoryPage = ReturnType<typeof RepositoryRunHistoryPage>;
|
|
8
|
+
export default RepositoryRunHistoryPage;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
3
3
|
import { Modal, ModalBody } from "@immich/ui";
|
|
4
|
-
import
|
|
4
|
+
import RepositoryRunHistoryPage from "./RepositoryRunHistoryPage.svelte";
|
|
5
5
|
|
|
6
6
|
type Props = {
|
|
7
7
|
repository: LocalRepositoryDto;
|
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
|
|
14
14
|
<Modal title={`Run History for ${repository.name}`} size="giant" {onClose}>
|
|
15
15
|
<ModalBody>
|
|
16
|
-
<
|
|
16
|
+
<RepositoryRunHistoryPage {repository} />
|
|
17
17
|
</ModalBody>
|
|
18
18
|
</Modal>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import StackList from "../../ui/StackList.svelte";
|
|
3
|
+
import StackListPlaceholder from "../../ui/StackListPlaceholder.svelte";
|
|
3
4
|
import OnEvents from "../../util/OnEvents.svelte";
|
|
5
|
+
import Suspense from "../../util/Suspense.svelte";
|
|
4
6
|
import type { LocalRepositoryDto } from "../../../fetch-client";
|
|
5
7
|
import { options } from "../../../options";
|
|
6
8
|
import { handlePruneRepository } from "../../../services/repository.service";
|
|
@@ -8,15 +10,17 @@
|
|
|
8
10
|
useSnapshotEventHandler,
|
|
9
11
|
useSnapshots,
|
|
10
12
|
} from "../../../services/snapshot.service";
|
|
11
|
-
import { Button, HStack
|
|
13
|
+
import { Button, HStack } from "@immich/ui";
|
|
12
14
|
import RepositorySnapshotsListItem from "./RepositorySnapshotsListItem.svelte";
|
|
13
15
|
|
|
14
16
|
type Props = {
|
|
15
17
|
repository: LocalRepositoryDto;
|
|
16
18
|
immich?: boolean;
|
|
19
|
+
limit?: number;
|
|
20
|
+
onViewAll?: () => void;
|
|
17
21
|
};
|
|
18
22
|
|
|
19
|
-
let { repository, immich = false }: Props = $props();
|
|
23
|
+
let { repository, immich = false, limit, onViewAll }: Props = $props();
|
|
20
24
|
|
|
21
25
|
const { advanced } = options;
|
|
22
26
|
// svelte-ignore state_referenced_locally
|
|
@@ -26,24 +30,32 @@
|
|
|
26
30
|
|
|
27
31
|
<OnEvents {onRunUpdate} />
|
|
28
32
|
|
|
29
|
-
<StackList
|
|
33
|
+
<StackList>
|
|
30
34
|
{#snippet title()}
|
|
31
35
|
Snapshots
|
|
32
36
|
{/snippet}
|
|
33
37
|
|
|
34
|
-
{#snippet
|
|
35
|
-
{#if
|
|
36
|
-
<
|
|
37
|
-
{
|
|
38
|
-
|
|
38
|
+
{#snippet action()}
|
|
39
|
+
{#if onViewAll && query.data?.length}
|
|
40
|
+
<Button variant="ghost" size="small" onclick={onViewAll}>View all</Button>
|
|
41
|
+
{/if}
|
|
42
|
+
{/snippet}
|
|
43
|
+
|
|
44
|
+
<Suspense {query}>
|
|
45
|
+
{#snippet children(snapshots)}
|
|
46
|
+
{#each limit ? snapshots.slice(0, limit) : snapshots as snapshot (snapshot.id)}
|
|
39
47
|
<RepositorySnapshotsListItem
|
|
40
48
|
repositoryId={repository.id}
|
|
41
49
|
{snapshot}
|
|
42
50
|
{immich}
|
|
43
51
|
/>
|
|
44
52
|
{/each}
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
|
|
54
|
+
{#if snapshots.length === 0}
|
|
55
|
+
<StackListPlaceholder>No backups yet</StackListPlaceholder>
|
|
56
|
+
{/if}
|
|
57
|
+
{/snippet}
|
|
58
|
+
</Suspense>
|
|
47
59
|
</StackList>
|
|
48
60
|
|
|
49
61
|
{#if $advanced}
|