@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.
- package/dist/components/backups/BackupStatus.svelte +14 -3
- package/dist/components/backups/BackupStatus.svelte.d.ts +1 -1
- package/dist/components/backups/dialogs/ViewStatusModal.svelte +6 -0
- package/dist/components/backups/metrics-history/MetricsHistoryModal.svelte +8 -0
- package/dist/components/backups/run-history/RepositoryRunHistoryItem.svelte +10 -3
- package/dist/components/dashboard/{Dashboard.svelte.d.ts → DashboardPage.svelte.d.ts} +3 -3
- package/dist/components/integrations/immich/ImmichBackupAttemptsPage.svelte +19 -0
- package/dist/components/integrations/immich/ImmichBackupAttemptsPage.svelte.d.ts +6 -0
- package/dist/components/integrations/immich/ImmichBackupSettingsPage.svelte +25 -0
- package/dist/components/integrations/immich/ImmichBackupSettingsPage.svelte.d.ts +6 -0
- package/dist/components/integrations/immich/ImmichBackupsPage.svelte +13 -3
- package/dist/components/integrations/immich/ImmichBackupsPage.svelte.d.ts +3 -0
- package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte +12 -2
- package/dist/components/integrations/immich/ImmichConfiguredGate.svelte +32 -0
- package/dist/components/integrations/immich/ImmichConfiguredGate.svelte.d.ts +9 -0
- package/dist/components/integrations/immich/ImmichDatabaseDumpAlert.svelte +42 -0
- package/dist/components/integrations/immich/{ImmichBackupSettings.svelte.d.ts → ImmichDatabaseDumpAlert.svelte.d.ts} +3 -3
- package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte +28 -10
- package/dist/components/integrations/immich/ImmichManageBackupPage.svelte +52 -0
- package/dist/components/integrations/immich/ImmichManageBackupPage.svelte.d.ts +8 -0
- package/dist/components/integrations/immich/ImmichSnapshotsPage.svelte +19 -0
- package/dist/components/integrations/immich/ImmichSnapshotsPage.svelte.d.ts +6 -0
- package/dist/components/integrations/immich/settings/ImmichSettingsBandwidth.svelte +150 -0
- package/dist/components/integrations/immich/settings/ImmichSettingsBandwidth.svelte.d.ts +3 -0
- package/dist/components/test/ImmichTestUi.svelte +30 -3
- package/dist/components/test/TestUi.svelte +2 -2
- package/dist/fetch-client.d.ts +28 -3
- package/dist/fetch-client.js +30 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/services/config.service.d.ts +6 -0
- package/dist/services/config.service.js +16 -0
- package/dist/services/immich.integration.service.d.ts +5 -1
- package/dist/services/immich.integration.service.js +27 -2
- package/dist/services/runHistory.service.d.ts +1 -0
- package/dist/services/runHistory.service.js +6 -1
- package/dist/utils/backup-status.d.ts +1 -1
- package/dist/utils/backup-status.js +3 -0
- package/package.json +2 -2
- package/dist/components/integrations/immich/ImmichBackupSettings.svelte +0 -12
- package/dist/components/integrations/immich/ImmichManageBackup.svelte +0 -76
- package/dist/components/integrations/immich/ImmichManageBackup.svelte.d.ts +0 -3
- /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>
|
|
@@ -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<
|
|
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
|
|
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
|
|
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
|
|
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
|
-
<
|
|
102
|
+
<DashboardPage onViewBackups={() => (route = "backups")} />
|
|
103
103
|
{:else if route === "backups"}
|
|
104
104
|
<BackupsList />
|
|
105
105
|
{:else if route === "config"}
|
package/dist/fetch-client.d.ts
CHANGED
|
@@ -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>;
|
package/dist/fetch-client.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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);
|
|
@@ -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
|
-
|
|
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;
|
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.
|
|
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.
|
|
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>
|