@futo-org/backups-orchestrator-ui 0.44.0 → 0.46.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.
Files changed (43) hide show
  1. package/dist/components/backups/BackupStatus.svelte +14 -3
  2. package/dist/components/backups/BackupStatus.svelte.d.ts +1 -1
  3. package/dist/components/backups/dialogs/ViewStatusModal.svelte +6 -0
  4. package/dist/components/backups/metrics-history/MetricsHistoryModal.svelte +8 -0
  5. package/dist/components/backups/run-history/RepositoryRunHistoryItem.svelte +10 -3
  6. package/dist/components/dashboard/{Dashboard.svelte.d.ts → DashboardPage.svelte.d.ts} +3 -3
  7. package/dist/components/integrations/immich/ImmichBackupAttemptsPage.svelte +19 -0
  8. package/dist/components/integrations/immich/ImmichBackupAttemptsPage.svelte.d.ts +6 -0
  9. package/dist/components/integrations/immich/ImmichBackupSettingsPage.svelte +25 -0
  10. package/dist/components/integrations/immich/ImmichBackupSettingsPage.svelte.d.ts +6 -0
  11. package/dist/components/integrations/immich/ImmichBackupsPage.svelte +13 -3
  12. package/dist/components/integrations/immich/ImmichBackupsPage.svelte.d.ts +3 -0
  13. package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte +12 -2
  14. package/dist/components/integrations/immich/ImmichConfiguredGate.svelte +32 -0
  15. package/dist/components/integrations/immich/ImmichConfiguredGate.svelte.d.ts +9 -0
  16. package/dist/components/integrations/immich/ImmichDatabaseDumpAlert.svelte +42 -0
  17. package/dist/components/integrations/immich/{ImmichBackupSettings.svelte.d.ts → ImmichDatabaseDumpAlert.svelte.d.ts} +3 -3
  18. package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte +28 -10
  19. package/dist/components/integrations/immich/ImmichManageBackupPage.svelte +52 -0
  20. package/dist/components/integrations/immich/ImmichManageBackupPage.svelte.d.ts +8 -0
  21. package/dist/components/integrations/immich/ImmichSnapshotsPage.svelte +19 -0
  22. package/dist/components/integrations/immich/ImmichSnapshotsPage.svelte.d.ts +6 -0
  23. package/dist/components/integrations/immich/settings/ImmichSettingsBandwidth.svelte +150 -0
  24. package/dist/components/integrations/immich/settings/ImmichSettingsBandwidth.svelte.d.ts +3 -0
  25. package/dist/components/test/ImmichTestUi.svelte +30 -3
  26. package/dist/components/test/TestUi.svelte +2 -2
  27. package/dist/fetch-client.d.ts +28 -3
  28. package/dist/fetch-client.js +30 -0
  29. package/dist/index.d.ts +5 -2
  30. package/dist/index.js +5 -2
  31. package/dist/services/config.service.d.ts +6 -0
  32. package/dist/services/config.service.js +16 -0
  33. package/dist/services/immich.integration.service.d.ts +5 -1
  34. package/dist/services/immich.integration.service.js +27 -2
  35. package/dist/services/runHistory.service.d.ts +1 -0
  36. package/dist/services/runHistory.service.js +6 -1
  37. package/dist/utils/backup-status.d.ts +1 -1
  38. package/dist/utils/backup-status.js +3 -0
  39. package/package.json +2 -2
  40. package/dist/components/integrations/immich/ImmichBackupSettings.svelte +0 -12
  41. package/dist/components/integrations/immich/ImmichManageBackup.svelte +0 -76
  42. package/dist/components/integrations/immich/ImmichManageBackup.svelte.d.ts +0 -3
  43. /package/dist/components/dashboard/{Dashboard.svelte → DashboardPage.svelte} +0 -0
@@ -0,0 +1,150 @@
1
+ <script lang="ts">
2
+ import Accordion from "../../../ui/Accordion.svelte";
3
+ import { useConfig, useUpdateConfig } from "../../../../services/config.service";
4
+ import {
5
+ Button,
6
+ Field,
7
+ HStack,
8
+ LoadingSpinner,
9
+ Select,
10
+ Stack,
11
+ Switch,
12
+ Text,
13
+ } from "@immich/ui";
14
+ import { mdiSpeedometerSlow } from "@mdi/js";
15
+
16
+ const bytesPerMegabyte = 1_000_000;
17
+ const unlimited = "0";
18
+
19
+ const speeds = [
20
+ { value: unlimited, label: "No limit" },
21
+ ...[0.5, 1, 2, 5, 10, 20, 50, 100].map((megabytes) => ({
22
+ value: String(megabytes * bytesPerMegabyte),
23
+ label: `${megabytes} MB/s`,
24
+ })),
25
+ ];
26
+
27
+ const hours = Array.from({ length: 24 }, (_, hour) => ({
28
+ value: `${String(hour).padStart(2, "0")}:00`,
29
+ label: `${String(hour).padStart(2, "0")}:00`,
30
+ }));
31
+
32
+ const defaultQuietHoursStart = "22:00";
33
+ const defaultQuietHoursEnd = "06:00";
34
+
35
+ const config = useConfig();
36
+ const mutation = useUpdateConfig();
37
+
38
+ let speed = $state(unlimited);
39
+ let quiet = $state(false);
40
+ let quietStart = $state(defaultQuietHoursStart);
41
+ let quietEnd = $state(defaultQuietHoursEnd);
42
+ let loaded = false;
43
+
44
+ const load = () => {
45
+ if (!config.data) {
46
+ return;
47
+ }
48
+
49
+ const { bytesPerSec, quietHours } = config.data.bandwidth;
50
+ const [start = defaultQuietHoursStart, end = defaultQuietHoursEnd] =
51
+ quietHours?.split("-") ?? [];
52
+
53
+ speed = String(bytesPerSec);
54
+ quiet = Boolean(quietHours);
55
+ quietStart = start;
56
+ quietEnd = end;
57
+ };
58
+
59
+ $effect(() => {
60
+ if (loaded || !config.data) {
61
+ return;
62
+ }
63
+
64
+ loaded = true;
65
+ load();
66
+ });
67
+
68
+ const limited = $derived(speed !== unlimited);
69
+
70
+ const onSave = () => {
71
+ mutation.mutate({
72
+ bandwidth: {
73
+ bytesPerSec: Number(speed),
74
+ quietHours: limited && quiet ? `${quietStart}-${quietEnd}` : undefined,
75
+ },
76
+ });
77
+ };
78
+ </script>
79
+
80
+ <Accordion
81
+ title="Bandwidth"
82
+ subtitle="Manage how fast backups upload."
83
+ icon={mdiSpeedometerSlow}
84
+ >
85
+ <Stack gap={4} class="pt-2">
86
+ {#if config.isLoading}
87
+ <LoadingSpinner />
88
+ {:else}
89
+ <Field
90
+ label="Upload speed"
91
+ description="Slow down backup uploads without affecting restores."
92
+ color="primary"
93
+ >
94
+ <Select
95
+ options={speeds}
96
+ value={speed}
97
+ onChange={(value) => (speed = value)}
98
+ />
99
+ </Field>
100
+
101
+ <Field
102
+ label="Full speed during quiet hours"
103
+ description="Lift the limit while you are asleep."
104
+ color="primary"
105
+ disabled={!limited}
106
+ >
107
+ <Switch bind:checked={quiet} />
108
+ </Field>
109
+
110
+ {#if limited && quiet}
111
+ <HStack gap={4}>
112
+ <Field label="From" color="primary">
113
+ <Select
114
+ options={hours}
115
+ value={quietStart}
116
+ onChange={(value) => (quietStart = value)}
117
+ />
118
+ </Field>
119
+ <Field label="Until" color="primary">
120
+ <Select
121
+ options={hours}
122
+ value={quietEnd}
123
+ onChange={(value) => (quietEnd = value)}
124
+ />
125
+ </Field>
126
+ </HStack>
127
+
128
+ {#if quietStart === quietEnd}
129
+ <Text size="small" color="muted">
130
+ With the same start and end time, the limit never applies.
131
+ </Text>
132
+ {/if}
133
+ {/if}
134
+
135
+ <HStack gap={2} class="justify-end">
136
+ <Button
137
+ shape="round"
138
+ color="secondary"
139
+ disabled={mutation.isPending}
140
+ onclick={load}
141
+ >
142
+ Reset
143
+ </Button>
144
+ <Button shape="round" loading={mutation.isPending} onclick={onSave}>
145
+ Save
146
+ </Button>
147
+ </HStack>
148
+ {/if}
149
+ </Stack>
150
+ </Accordion>
@@ -0,0 +1,3 @@
1
+ declare const ImmichSettingsBandwidth: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ImmichSettingsBandwidth = ReturnType<typeof ImmichSettingsBandwidth>;
3
+ export default ImmichSettingsBandwidth;
@@ -41,7 +41,11 @@
41
41
  import MockImmichServerStatus from "./immich/MockImmichServerStatus.svelte";
42
42
  import MockImmichPurchaseInfo from "./immich/MockImmichPurchaseInfo.svelte";
43
43
  import MockImmichStorageSpace from "./immich/MockImmichStorageSpace.svelte";
44
+ import PageLayout from "../ui/PageLayout.svelte";
45
+ import ImmichBackupAttemptsPage from "../integrations/immich/ImmichBackupAttemptsPage.svelte";
46
+ import ImmichBackupSettingsPage from "../integrations/immich/ImmichBackupSettingsPage.svelte";
44
47
  import ImmichBackupsPage from "../integrations/immich/ImmichBackupsPage.svelte";
48
+ import ImmichSnapshotsPage from "../integrations/immich/ImmichSnapshotsPage.svelte";
45
49
  import ImmichOnboardingRestoreFlow from "../integrations/immich/ImmichOnboardingRestoreFlow.svelte";
46
50
  import MockImmichHideBackupsReminder from "./immich/MockImmichHideBackupsReminder.svelte";
47
51
 
@@ -52,7 +56,13 @@
52
56
  const { onExit }: Props = $props();
53
57
  const { testUiRestore, demoPadding } = options;
54
58
 
55
- let route = $state<"photos" | "settings">("photos");
59
+ let route = $state<
60
+ | "photos"
61
+ | "settings"
62
+ | "backup-settings"
63
+ | "backup-attempts"
64
+ | "backup-snapshots"
65
+ >("photos");
56
66
 
57
67
  const sampleQuestions = [
58
68
  {
@@ -69,6 +79,8 @@
69
79
  },
70
80
  ];
71
81
 
82
+ const backToBackups = () => (route = "settings");
83
+
72
84
  demoPadding.set(true);
73
85
  onDestroy(() => demoPadding.set(false));
74
86
  </script>
@@ -186,7 +198,7 @@
186
198
 
187
199
  <AppShellSidebar class="relative">
188
200
  <div class="flex h-full flex-col pt-4 pr-2">
189
- {#if route === "settings"}
201
+ {#if route !== "photos"}
190
202
  <ImmichBackupsAdminNavButton href="#" />
191
203
 
192
204
  <NavbarItem
@@ -247,7 +259,7 @@
247
259
  <ImmichBackupsSidebarItem href="#" />
248
260
  </MockImmichStorageSpace>
249
261
 
250
- {#if route !== "settings"}
262
+ {#if route === "photos"}
251
263
  <MockImmichPurchaseInfo />
252
264
  {/if}
253
265
 
@@ -272,7 +284,22 @@
272
284
  },
273
285
  ...sampleQuestions,
274
286
  ]}
287
+ onConfigure={() => (route = "backup-settings")}
288
+ onViewAttempts={() => (route = "backup-attempts")}
289
+ onViewSnapshots={() => (route = "backup-snapshots")}
275
290
  />
291
+ {:else if route === "backup-settings"}
292
+ <PageLayout title="Backup settings" onBack={backToBackups}>
293
+ <ImmichBackupSettingsPage onUnconfigured={backToBackups} />
294
+ </PageLayout>
295
+ {:else if route === "backup-attempts"}
296
+ <PageLayout title="Backup attempts" onBack={backToBackups}>
297
+ <ImmichBackupAttemptsPage onUnconfigured={backToBackups} />
298
+ </PageLayout>
299
+ {:else if route === "backup-snapshots"}
300
+ <PageLayout title="Snapshots" onBack={backToBackups}>
301
+ <ImmichSnapshotsPage onUnconfigured={backToBackups} />
302
+ </PageLayout>
276
303
  {:else}
277
304
  <MockImmichPhotos />
278
305
  {/if}
@@ -15,7 +15,7 @@
15
15
  } from "@mdi/js";
16
16
  import GlobalSettings from "../settings/GlobalSettings.svelte";
17
17
  import BackupsList from "../backups/BackupsList.svelte";
18
- import Dashboard from "../dashboard/Dashboard.svelte";
18
+ import DashboardPage from "../dashboard/DashboardPage.svelte";
19
19
  import ScheduleList from "../schedules/ScheduleList.svelte";
20
20
 
21
21
  let open = $state(true);
@@ -99,7 +99,7 @@
99
99
 
100
100
  <div class="p-4 flex flex-col gap-2 max-w-6xl m-auto">
101
101
  {#if route === "dashboard"}
102
- <Dashboard onViewBackups={() => (route = "backups")} />
102
+ <DashboardPage onViewBackups={() => (route = "backups")} />
103
103
  {:else if route === "backups"}
104
104
  <BackupsList />
105
105
  {:else if route === "config"}
@@ -47,6 +47,16 @@ export type CreateLocalBackendRequestDto = {
47
47
  export type BackendResponseDto = {
48
48
  backend: BackendDto;
49
49
  };
50
+ export type BandwidthDto = {
51
+ bytesPerSec: number;
52
+ quietHours?: string;
53
+ };
54
+ export type ConfigResponseDto = {
55
+ bandwidth: BandwidthDto;
56
+ };
57
+ export type ConfigUpdateRequestDto = {
58
+ bandwidth: BandwidthDto;
59
+ };
50
60
  export type FilesystemListingItemDto = {
51
61
  path: string;
52
62
  isDirectory: boolean;
@@ -84,7 +94,7 @@ export type IntegrationsResponseDto = {
84
94
  export type RepositoryMetricsDto = {
85
95
  lastStarted?: string | null;
86
96
  lastBackup?: string | null;
87
- lastBackupStatus?: ("incomplete" | "complete" | "warn" | "failed" | null) | null;
97
+ lastBackupStatus?: ("incomplete" | "complete" | "warn" | "failed" | "cancelled" | null) | null;
88
98
  lastBackupDuration?: number | null;
89
99
  sizeBytes: number;
90
100
  };
@@ -135,7 +145,7 @@ export type ScheduleDto = {
135
145
  lastRun?: string;
136
146
  lastFinished?: string;
137
147
  };
138
- export type RunStatus = "incomplete" | "complete" | "warn" | "failed";
148
+ export type RunStatus = "incomplete" | "complete" | "warn" | "failed" | "cancelled";
139
149
  export type RunType = "schedule" | "restore" | "backup" | "forget";
140
150
  export type RunDto = {
141
151
  id: string;
@@ -146,12 +156,18 @@ export type RunDto = {
146
156
  status: RunStatus;
147
157
  "type": RunType;
148
158
  };
159
+ export type ImmichDatabaseDumpConfigDto = {
160
+ enabled: boolean;
161
+ keepLastAmount: number;
162
+ };
149
163
  export type ImmichBackupStatusDto = {
150
164
  integration?: ImmichIntegrationDto;
151
165
  repository?: LocalRepositoryDto;
152
166
  backend?: BackendDto;
153
167
  schedule?: ScheduleDto;
154
168
  latestBackupRun?: RunDto;
169
+ databaseDump?: ImmichDatabaseDumpConfigDto;
170
+ databaseDumpWarningIgnored?: boolean;
155
171
  };
156
172
  export type ConfigureImmichIntegrationRequestDto = {
157
173
  name: string;
@@ -167,6 +183,10 @@ export type ConfigureImmichIntegrationRequestDto = {
167
183
  export type ConfigureImmichIntegrationResponseDto = {
168
184
  repositoryId: string;
169
185
  };
186
+ export type ConfigureImmichDatabaseDumpRequestDto = {
187
+ enabled?: boolean;
188
+ keepLastAmount?: number;
189
+ };
170
190
  export type ImmichRollbackRequestDto = {
171
191
  repositoryId: string;
172
192
  snapshotId: string;
@@ -272,7 +292,7 @@ export type RunResponseDto = {
272
292
  run: RunDto;
273
293
  };
274
294
  export type TaskType = "schedule" | "restore" | "backup" | "forget";
275
- export type TaskStatus = "incomplete" | "complete" | "warn" | "failed";
295
+ export type TaskStatus = "incomplete" | "complete" | "warn" | "failed" | "cancelled";
276
296
  export type ActiveScheduleItemDto = {
277
297
  repositoryId: string;
278
298
  status: TaskStatus;
@@ -313,6 +333,8 @@ export declare function createSession(createSessionRequestDto: CreateSessionRequ
313
333
  export declare function createTicket(ticketCreateRequestDto: TicketCreateRequestDto, opts?: Oazapfts.RequestOpts): Promise<TicketCreateResponseDto>;
314
334
  export declare function getBackends(opts?: Oazapfts.RequestOpts): Promise<BackendsResponseDto>;
315
335
  export declare function createLocalBackend(createLocalBackendRequestDto: CreateLocalBackendRequestDto, opts?: Oazapfts.RequestOpts): Promise<BackendResponseDto>;
336
+ export declare function getConfig(opts?: Oazapfts.RequestOpts): Promise<ConfigResponseDto>;
337
+ export declare function updateConfig(configUpdateRequestDto: ConfigUpdateRequestDto, opts?: Oazapfts.RequestOpts): Promise<ConfigResponseDto>;
316
338
  export declare function resetOrchestrator(opts?: Oazapfts.RequestOpts): Promise<never>;
317
339
  export declare function getFileListing({ path }?: {
318
340
  path?: string;
@@ -320,6 +342,8 @@ export declare function getFileListing({ path }?: {
320
342
  export declare function getIntegrations(opts?: Oazapfts.RequestOpts): Promise<IntegrationsResponseDto>;
321
343
  export declare function getImmichBackupStatus(opts?: Oazapfts.RequestOpts): Promise<ImmichBackupStatusDto>;
322
344
  export declare function configureImmichIntegration(configureImmichIntegrationRequestDto: ConfigureImmichIntegrationRequestDto, opts?: Oazapfts.RequestOpts): Promise<ConfigureImmichIntegrationResponseDto>;
345
+ export declare function ignoreImmichDatabaseDumpWarning(opts?: Oazapfts.RequestOpts): Promise<never>;
346
+ export declare function configureImmichDatabaseDump(configureImmichDatabaseDumpRequestDto: ConfigureImmichDatabaseDumpRequestDto, opts?: Oazapfts.RequestOpts): Promise<never>;
323
347
  export declare function startImmichRollback(immichRollbackRequestDto: ImmichRollbackRequestDto, opts?: Oazapfts.RequestOpts): Promise<never>;
324
348
  export declare function onboardingStatus(opts?: Oazapfts.RequestOpts): Promise<OnboardingStatusResponseDto>;
325
349
  export declare function currentRecoveryKey(opts?: Oazapfts.RequestOpts): Promise<CurrentRecoveryKeyResponse>;
@@ -353,6 +377,7 @@ export declare function getSnapshotListing(id: string, snapshot: string, { path
353
377
  path?: string;
354
378
  }, opts?: Oazapfts.RequestOpts): Promise<FilesystemListingResponseDto>;
355
379
  export declare function getRun(id: string, opts?: Oazapfts.RequestOpts): Promise<RunResponseDto>;
380
+ export declare function downloadRunLog(id: string, opts?: Oazapfts.RequestOpts): Promise<Blob>;
356
381
  export declare function logStreamSse(id: string, opts?: Oazapfts.RequestOpts): Promise<never>;
357
382
  export declare function getRunningTasks(opts?: Oazapfts.RequestOpts): Promise<RunningTaskListResponse>;
358
383
  export declare function cancelTask(parentId: string, opts?: Oazapfts.RequestOpts): Promise<never>;
@@ -48,6 +48,18 @@ export function createLocalBackend(createLocalBackendRequestDto, opts) {
48
48
  body: createLocalBackendRequestDto
49
49
  })));
50
50
  }
51
+ export function getConfig(opts) {
52
+ return oazapfts.ok(oazapfts.fetchJson("/api/yucca/config", {
53
+ ...opts
54
+ }));
55
+ }
56
+ export function updateConfig(configUpdateRequestDto, opts) {
57
+ return oazapfts.ok(oazapfts.fetchJson("/api/yucca/config", oazapfts.json({
58
+ ...opts,
59
+ method: "PUT",
60
+ body: configUpdateRequestDto
61
+ })));
62
+ }
51
63
  export function resetOrchestrator(opts) {
52
64
  return oazapfts.ok(oazapfts.fetchText("/api/yucca/debug/reset", {
53
65
  ...opts,
@@ -78,6 +90,19 @@ export function configureImmichIntegration(configureImmichIntegrationRequestDto,
78
90
  body: configureImmichIntegrationRequestDto
79
91
  })));
80
92
  }
93
+ export function ignoreImmichDatabaseDumpWarning(opts) {
94
+ return oazapfts.ok(oazapfts.fetchText("/api/yucca/integrations/immich/database-dump/ignore-warning", {
95
+ ...opts,
96
+ method: "POST"
97
+ }));
98
+ }
99
+ export function configureImmichDatabaseDump(configureImmichDatabaseDumpRequestDto, opts) {
100
+ return oazapfts.ok(oazapfts.fetchText("/api/yucca/integrations/immich/database-dump", oazapfts.json({
101
+ ...opts,
102
+ method: "POST",
103
+ body: configureImmichDatabaseDumpRequestDto
104
+ })));
105
+ }
81
106
  export function startImmichRollback(immichRollbackRequestDto, opts) {
82
107
  return oazapfts.ok(oazapfts.fetchText("/api/yucca/integrations/immich/rollback", oazapfts.json({
83
108
  ...opts,
@@ -240,6 +265,11 @@ export function getRun(id, opts) {
240
265
  ...opts
241
266
  }));
242
267
  }
268
+ export function downloadRunLog(id, opts) {
269
+ return oazapfts.ok(oazapfts.fetchBlob(`/api/yucca/logs/${encodeURIComponent(id)}/download`, {
270
+ ...opts
271
+ }));
272
+ }
243
273
  export function logStreamSse(id, opts) {
244
274
  return oazapfts.ok(oazapfts.fetchText(`/api/yucca/logs/${encodeURIComponent(id)}/stream`, {
245
275
  ...opts
package/dist/index.d.ts CHANGED
@@ -2,13 +2,16 @@ export { default as Suspense } from './components/util/Suspense.svelte';
2
2
  export { default as BackendsList } from './components/backends/BackendsList.svelte';
3
3
  export { default as BackupsList } from './components/backups/BackupsList.svelte';
4
4
  export { default as ViewStatusModal } from './components/backups/dialogs/ViewStatusModal.svelte';
5
- export { default as Dashboard } from './components/dashboard/Dashboard.svelte';
5
+ export { default as DashboardPage } from './components/dashboard/DashboardPage.svelte';
6
+ export { default as ImmichBackupAttemptsPage } from './components/integrations/immich/ImmichBackupAttemptsPage.svelte';
7
+ export { default as ImmichBackupSettingsPage } from './components/integrations/immich/ImmichBackupSettingsPage.svelte';
6
8
  export { default as ImmichBackupsAdminNavButton } from './components/integrations/immich/ImmichBackupsAdminNavButton.svelte';
7
9
  export { default as ImmichBackupsPage } from './components/integrations/immich/ImmichBackupsPage.svelte';
8
10
  export { default as ImmichBackupsSidebarItem } from './components/integrations/immich/ImmichBackupsSidebarItem.svelte';
9
- export { default as ImmichManageBackup } from './components/integrations/immich/ImmichManageBackup.svelte';
11
+ export { default as ImmichManageBackupPage } from './components/integrations/immich/ImmichManageBackupPage.svelte';
10
12
  export { default as ImmichOnboardingRestoreFlow } from './components/integrations/immich/ImmichOnboardingRestoreFlow.svelte';
11
13
  export { default as ImmichOnboardingSetupFlow } from './components/integrations/immich/ImmichOnboardingSetupFlow.svelte';
14
+ export { default as ImmichSnapshotsPage } from './components/integrations/immich/ImmichSnapshotsPage.svelte';
12
15
  export { default as OnboardingGate } from './components/onboarding/OnboardingGate.svelte';
13
16
  export { default as UpsellModal } from './components/onboarding/upsell/UpsellModal.svelte';
14
17
  export { default as UpsellPage } from './components/onboarding/upsell/UpsellPage.svelte';
package/dist/index.js CHANGED
@@ -2,13 +2,16 @@ export { default as Suspense } from './components/util/Suspense.svelte';
2
2
  export { default as BackendsList } from './components/backends/BackendsList.svelte';
3
3
  export { default as BackupsList } from './components/backups/BackupsList.svelte';
4
4
  export { default as ViewStatusModal } from './components/backups/dialogs/ViewStatusModal.svelte';
5
- export { default as Dashboard } from './components/dashboard/Dashboard.svelte';
5
+ export { default as DashboardPage } from './components/dashboard/DashboardPage.svelte';
6
+ export { default as ImmichBackupAttemptsPage } from './components/integrations/immich/ImmichBackupAttemptsPage.svelte';
7
+ export { default as ImmichBackupSettingsPage } from './components/integrations/immich/ImmichBackupSettingsPage.svelte';
6
8
  export { default as ImmichBackupsAdminNavButton } from './components/integrations/immich/ImmichBackupsAdminNavButton.svelte';
7
9
  export { default as ImmichBackupsPage } from './components/integrations/immich/ImmichBackupsPage.svelte';
8
10
  export { default as ImmichBackupsSidebarItem } from './components/integrations/immich/ImmichBackupsSidebarItem.svelte';
9
- export { default as ImmichManageBackup } from './components/integrations/immich/ImmichManageBackup.svelte';
11
+ export { default as ImmichManageBackupPage } from './components/integrations/immich/ImmichManageBackupPage.svelte';
10
12
  export { default as ImmichOnboardingRestoreFlow } from './components/integrations/immich/ImmichOnboardingRestoreFlow.svelte';
11
13
  export { default as ImmichOnboardingSetupFlow } from './components/integrations/immich/ImmichOnboardingSetupFlow.svelte';
14
+ export { default as ImmichSnapshotsPage } from './components/integrations/immich/ImmichSnapshotsPage.svelte';
12
15
  export { default as OnboardingGate } from './components/onboarding/OnboardingGate.svelte';
13
16
  export { default as UpsellModal } from './components/onboarding/upsell/UpsellModal.svelte';
14
17
  export { default as UpsellPage } from './components/onboarding/upsell/UpsellPage.svelte';
@@ -0,0 +1,6 @@
1
+ import { type ConfigUpdateRequestDto } from '../fetch-client';
2
+ export declare const configKeys: {
3
+ all: readonly ["config"];
4
+ };
5
+ export declare const useConfig: () => import("@tanstack/svelte-query").CreateQueryResult<import("../fetch-client").ConfigResponseDto, Error>;
6
+ export declare const useUpdateConfig: () => import("@tanstack/svelte-query").CreateMutationResult<import("../fetch-client").ConfigResponseDto, Error, ConfigUpdateRequestDto, unknown>;
@@ -0,0 +1,16 @@
1
+ import { getConfig, updateConfig, } from '../fetch-client';
2
+ import { queryClient } from '../query-client';
3
+ import { handleError } from '../utils/handle-error';
4
+ import { createMutation, createQuery } from '@tanstack/svelte-query';
5
+ export const configKeys = {
6
+ all: ['config'],
7
+ };
8
+ export const useConfig = () => createQuery(() => ({
9
+ queryKey: configKeys.all,
10
+ queryFn: () => getConfig(),
11
+ }), () => queryClient);
12
+ export const useUpdateConfig = () => createMutation(() => ({
13
+ mutationFn: (dto) => updateConfig(dto),
14
+ onSuccess: (config) => queryClient.setQueryData(configKeys.all, config),
15
+ onError: (error) => handleError(error, 'Failed to save settings'),
16
+ }), () => queryClient);
@@ -1,4 +1,5 @@
1
1
  import { type ActionItem } from '@immich/ui';
2
+ import { type ConfigureImmichDatabaseDumpRequestDto } from '../fetch-client';
2
3
  export declare const getBackupPageActions: (repositoryId: string | undefined, onConfigure: () => void) => {
3
4
  Configure: ActionItem;
4
5
  ViewRecoveryKey: ActionItem;
@@ -18,11 +19,14 @@ export declare const useImmichBackupStatusEventHandler: () => {
18
19
  export type ImmichBackupStatus = {
19
20
  kind: 'loading' | 'offline' | 'missing' | 'running' | 'paused' | 'unconfigured' | 'never';
20
21
  } | {
21
- kind: 'complete' | 'warn' | 'incomplete' | 'failed';
22
+ kind: 'complete' | 'warn' | 'incomplete' | 'failed' | 'cancelled';
22
23
  lastBackup: string;
23
24
  };
24
25
  export declare const useImmichBackupStatus: () => {
25
26
  readonly repository: import("../fetch-client").LocalRepositoryDto | undefined;
26
27
  readonly schedule: import("../fetch-client").ScheduleDto | undefined;
27
28
  readonly status: ImmichBackupStatus;
29
+ readonly needsAttention: boolean;
28
30
  };
31
+ export declare const useConfigureImmichDatabaseDump: () => import("@tanstack/svelte-query").CreateMutationResult<never, Error, ConfigureImmichDatabaseDumpRequestDto, unknown>;
32
+ export declare const useIgnoreImmichDatabaseDumpWarning: () => import("@tanstack/svelte-query").CreateMutationResult<never, Error, void, unknown>;
@@ -1,10 +1,11 @@
1
1
  import BackupsRecoveryKeyModal from '../components/onboarding/dialogs/BackupsRecoveryKeyModal.svelte';
2
2
  import { modalManager } from '@immich/ui';
3
3
  import { mdiCloudUploadOutline, mdiCogOutline, mdiKeyOutline } from '@mdi/js';
4
- import { getImmichBackupStatus, } from '../fetch-client';
4
+ import { configureImmichDatabaseDump, getImmichBackupStatus, ignoreImmichDatabaseDumpWarning, } from '../fetch-client';
5
5
  import { queryClient } from '../query-client';
6
+ import { handleError } from '../utils/handle-error';
6
7
  import { getBackupOutcome } from '../utils/backup-status';
7
- import { createQuery } from '@tanstack/svelte-query';
8
+ import { createMutation, createQuery } from '@tanstack/svelte-query';
8
9
  import { handleCreateBackup } from './repository.service';
9
10
  export const getBackupPageActions = (repositoryId, onConfigure) => {
10
11
  const Configure = {
@@ -31,6 +32,7 @@ export const immichBackupStatusKeys = {
31
32
  const invalidateImmichBackupStatus = () => void queryClient.invalidateQueries({
32
33
  queryKey: immichBackupStatusKeys.all,
33
34
  });
35
+ const updateImmichBackupStatus = (update) => queryClient.setQueryData(immichBackupStatusKeys.all, (data) => data && update(data));
34
36
  export const useImmichBackupStatusEventHandler = () => ({
35
37
  onIntegrationUpdate: invalidateImmichBackupStatus,
36
38
  onRepositoryUpdate: invalidateImmichBackupStatus,
@@ -77,5 +79,28 @@ export const useImmichBackupStatus = () => {
77
79
  get status() {
78
80
  return toImmichBackupStatus(query.data, query.isLoading);
79
81
  },
82
+ get needsAttention() {
83
+ const usingImmichDbDump = query.data?.databaseDump?.enabled === true &&
84
+ query.data.schedule !== undefined &&
85
+ !query.data.schedule.paused &&
86
+ query.data.databaseDumpWarningIgnored !== true;
87
+ return usingImmichDbDump;
88
+ },
80
89
  };
81
90
  };
91
+ export const useConfigureImmichDatabaseDump = () => createMutation(() => ({
92
+ mutationFn: (dto) => configureImmichDatabaseDump(dto),
93
+ onSuccess: (_, dto) => updateImmichBackupStatus((data) => ({
94
+ ...data,
95
+ databaseDump: data.databaseDump && { ...data.databaseDump, ...dto },
96
+ })),
97
+ onError: (error) => handleError(error, 'Failed to update Immich database dump settings'),
98
+ }), () => queryClient);
99
+ export const useIgnoreImmichDatabaseDumpWarning = () => createMutation(() => ({
100
+ mutationFn: () => ignoreImmichDatabaseDumpWarning(),
101
+ onSuccess: () => updateImmichBackupStatus((data) => ({
102
+ ...data,
103
+ databaseDumpWarningIgnored: true,
104
+ })),
105
+ onError: (error) => handleError(error, 'Failed to ignore Immich database dump warning'),
106
+ }), () => queryClient);
@@ -23,4 +23,5 @@ export declare const useRunEventHandler: () => {
23
23
  export declare const handleGetRunHistory: (id: string) => Promise<sdk.RunHistoryResponseDto>;
24
24
  export declare const getRunActions: (run: RunDto) => {
25
25
  ViewLog: ActionItem;
26
+ DownloadLog: ActionItem;
26
27
  };
@@ -2,6 +2,7 @@ import { sdk } from '..';
2
2
  import ViewStatusModal from '../components/backups/dialogs/ViewStatusModal.svelte';
3
3
  import { SocketEvent } from '../events';
4
4
  import { getRun, getRunHistory } from '../fetch-client';
5
+ import { getProvider } from '../providers';
5
6
  import { queryClient } from '../query-client';
6
7
  import { handleError } from '../utils/handle-error';
7
8
  import { modalManager } from '@immich/ui';
@@ -51,5 +52,9 @@ export const getRunActions = (run) => {
51
52
  title: 'View Log',
52
53
  onAction: () => void modalManager.open(ViewStatusModal, { logId: run.id }),
53
54
  };
54
- return { ViewLog };
55
+ const DownloadLog = {
56
+ title: 'Download Full Log',
57
+ onAction: () => window.open(`${getProvider().baseUrl}/api/yucca/logs/${run.id}/download`, '_blank'),
58
+ };
59
+ return { ViewLog, DownloadLog };
55
60
  };
@@ -1,3 +1,3 @@
1
1
  import type { RepositoryMetricsDto } from '../fetch-client';
2
- export type BackupOutcome = 'never' | 'complete' | 'warn' | 'incomplete' | 'failed';
2
+ export type BackupOutcome = 'never' | 'complete' | 'warn' | 'incomplete' | 'failed' | 'cancelled';
3
3
  export declare const getBackupOutcome: (metrics: RepositoryMetricsDto | undefined) => BackupOutcome;
@@ -9,6 +9,9 @@ export const getBackupOutcome = (metrics) => {
9
9
  case 'failed': {
10
10
  return 'failed';
11
11
  }
12
+ case 'cancelled': {
13
+ return 'cancelled';
14
+ }
12
15
  case 'warn': {
13
16
  return 'warn';
14
17
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@futo-org/backups-orchestrator-ui",
3
3
  "repository": "https://github.com/immich-app/yucca",
4
4
  "description": "Backups orchestrator (UI library)",
5
- "version": "0.44.0",
5
+ "version": "0.46.0",
6
6
  "license": "Source First License 1.1",
7
7
  "files": [
8
8
  "dist",
@@ -64,7 +64,7 @@
64
64
  "lodash.debounce": "^4.0.8",
65
65
  "luxon": "^3.7.2",
66
66
  "socket.io-client": "^4.8.3",
67
- "@futo-org/backups-api-client": "^0.44.0"
67
+ "@futo-org/backups-api-client": "^0.46.0"
68
68
  },
69
69
  "scripts": {
70
70
  "dev": "vite dev --port 5174",
@@ -1,12 +0,0 @@
1
- <script lang="ts">
2
- import { Container } from "@immich/ui";
3
- import ImmichSettingsBackupContents from "./settings/ImmichSettingsBackupContents.svelte";
4
- import ImmichSettingsSchedule from "./settings/ImmichSettingsSchedule.svelte";
5
- import ImmichSettingsStorage from "./settings/ImmichSettingsStorage.svelte";
6
- </script>
7
-
8
- <Container size="large" center>
9
- <ImmichSettingsBackupContents />
10
- <ImmichSettingsSchedule />
11
- <ImmichSettingsStorage />
12
- </Container>