@futo-org/backups-orchestrator-ui 0.40.2 → 0.41.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 (38) hide show
  1. package/dist/components/backups/BackupItem.svelte +4 -2
  2. package/dist/components/backups/BackupItem.svelte.d.ts +0 -1
  3. package/dist/components/backups/BackupsList.svelte +5 -3
  4. package/dist/components/backups/BackupsList.svelte.d.ts +0 -1
  5. package/dist/components/dashboard/Dashboard.svelte +6 -8
  6. package/dist/components/dashboard/Dashboard.svelte.d.ts +0 -1
  7. package/dist/components/dashboard/DashboardBackupHealth.svelte +4 -2
  8. package/dist/components/dashboard/DashboardBackupHealth.svelte.d.ts +0 -1
  9. package/dist/components/dashboard/DashboardInstall.svelte +1 -1
  10. package/dist/components/dashboard/DashboardRecentBackups.svelte +4 -2
  11. package/dist/components/dashboard/DashboardRecentBackups.svelte.d.ts +0 -1
  12. package/dist/components/integrations/immich/ImmichBackupsSidebarItem.svelte +88 -140
  13. package/dist/components/integrations/immich/ImmichManageBackup.svelte +8 -48
  14. package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte +31 -22
  15. package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte.d.ts +2 -0
  16. package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte +8 -3
  17. package/dist/components/onboarding/RecoveryKeyDisplay.svelte +3 -3
  18. package/dist/components/test/ImmichTestUi.svelte +5 -5
  19. package/dist/components/test/TestUi.svelte +9 -32
  20. package/dist/components/test/TestUi.svelte.d.ts +1 -4
  21. package/dist/components/test/dashboard/Dashboard.svelte +0 -3
  22. package/dist/components/test/immich/MockImmichPhotos.svelte +1 -1
  23. package/dist/components/util/OnEvents.svelte +3 -1
  24. package/dist/components/util/RelativeTime.svelte +4 -1
  25. package/dist/components/util/YuccaContext.svelte +1 -8
  26. package/dist/components/util/YuccaContext.svelte.d.ts +0 -1
  27. package/dist/events.js +2 -2
  28. package/dist/fetch-client.d.ts +69 -60
  29. package/dist/fetch-client.js +8 -5
  30. package/dist/providers.d.ts +14 -10
  31. package/dist/providers.js +16 -26
  32. package/dist/services/immich.integration.service.d.ts +22 -0
  33. package/dist/services/immich.integration.service.js +58 -0
  34. package/dist/services/integrations.service.d.ts +4 -2
  35. package/dist/services/integrations.service.js +3 -2
  36. package/dist/services/log.service.svelte.js +2 -2
  37. package/dist/services/session.service.js +3 -2
  38. package/package.json +2 -2
@@ -13,13 +13,14 @@
13
13
  } from "../../../services/backend.service";
14
14
  import {
15
15
  IMMICH_DEFAULT_CRON,
16
- useConfigureImmichDefaults,
16
+ useConfigureAndStartImmichIntegration,
17
17
  } from "../../../services/integrations.service";
18
18
  import {
19
19
  handleConfirmRecoveryKey,
20
20
  handleCurrentRecoveryKey,
21
21
  handleOnboardingStatus,
22
22
  } from "../../../services/onboarding.service";
23
+ import { handleCreateBackup } from "../../../services/repository.service";
23
24
  import { LoadingSpinner, Modal, ModalBody } from "@immich/ui";
24
25
  import cronstrue from "cronstrue";
25
26
  import { onMount, type Snippet } from "svelte";
@@ -48,7 +49,7 @@
48
49
  let resume: Stage = $state("intro");
49
50
  let confirming = $state(false);
50
51
 
51
- const defaults = useConfigureImmichDefaults();
52
+ const defaults = useConfigureAndStartImmichIntegration();
52
53
 
53
54
  const schedule = cronstrue.toString(IMMICH_DEFAULT_CRON, { verbose: true });
54
55
 
@@ -116,7 +117,11 @@
116
117
  };
117
118
 
118
119
  const onStartBackup = () =>
119
- defaults.mutate(undefined, { onSuccess: () => (stage = "finished") });
120
+ defaults.mutate(undefined, { onSuccess: ({ repositoryId }) => {
121
+ stage = "finished";
122
+ handleCreateBackup(repositoryId).catch(() => void 0);
123
+ }
124
+ });
120
125
  </script>
121
126
 
122
127
  {#if status === undefined || status.status === "not-ready"}
@@ -104,11 +104,11 @@
104
104
  Print
105
105
  </Button>
106
106
  </HStack>
107
-
108
- {#if /* intentionally hide this to not overcrowd the modal */ PasswordCredential && false}
107
+ {#if PasswordCredential}
109
108
  <Button
110
109
  leadingIcon={mdiAsterisk}
111
- variant="outline"
110
+ variant="ghost"
111
+ shape="round"
112
112
  onclick={storeCredentials}
113
113
  >
114
114
  Save to password manager
@@ -1,5 +1,4 @@
1
1
  <script lang="ts">
2
- import { orchestrationApiProvider, setProvider } from "../../providers";
3
2
  import {
4
3
  AppShell,
5
4
  AppShellHeader,
@@ -72,9 +71,6 @@
72
71
 
73
72
  demoPadding.set(true);
74
73
  onDestroy(() => demoPadding.set(false));
75
-
76
- // svelte-ignore state_referenced_locally
77
- setProvider(orchestrationApiProvider);
78
74
  </script>
79
75
 
80
76
  {#snippet hideReminder()}
@@ -237,7 +233,11 @@
237
233
  icon={{ icon: mdiImageAlbum, flipped: true }}
238
234
  />
239
235
  <NavbarItem href="#" title="Utilities" icon={mdiToolboxOutline} />
240
- <NavbarItem href="#" title="Archive" icon={mdiArchiveArrowDownOutline} />
236
+ <NavbarItem
237
+ href="#"
238
+ title="Archive"
239
+ icon={mdiArchiveArrowDownOutline}
240
+ />
241
241
  <NavbarItem href="#" title="Locked folder" icon={mdiLockOutline} />
242
242
  <NavbarItem href="#" title="Trash" icon={mdiTrashCanOutline} />
243
243
  {/if}
@@ -1,16 +1,10 @@
1
1
  <script lang="ts">
2
- import {
3
- MockProvider,
4
- orchestrationApiProvider,
5
- setProvider,
6
- } from "../../providers";
7
2
  import {
8
3
  AppShell,
9
4
  AppShellHeader,
10
5
  AppShellSidebar,
11
6
  Heading,
12
7
  NavbarItem,
13
- Text,
14
8
  ThemeSwitcher,
15
9
  } from "@immich/ui";
16
10
  import {
@@ -24,15 +18,6 @@
24
18
  import Dashboard from "../dashboard/Dashboard.svelte";
25
19
  import ScheduleList from "../schedules/ScheduleList.svelte";
26
20
 
27
- const { mock }: { mock: boolean } = $props();
28
-
29
- // svelte-ignore state_referenced_locally
30
- if (mock) {
31
- setProvider(new MockProvider());
32
- } else {
33
- setProvider(orchestrationApiProvider);
34
- }
35
-
36
21
  let open = $state(true);
37
22
  let route = $state("dashboard");
38
23
  </script>
@@ -41,15 +26,11 @@
41
26
  <AppShellHeader>
42
27
  <div class="flex items-center justify-between w-full px-4 py-2">
43
28
  <Heading>
44
- {#if mock}
45
- <Text class="h-12 font-bold">FUTO Backups</Text>
46
- {:else}
47
- <img
48
- alt="App Name Here"
49
- src="/app-name-here.png"
50
- class="inline h-12"
51
- />
52
- {/if}
29
+ <img
30
+ alt="App Name Here"
31
+ src="/app-name-here.png"
32
+ class="inline h-12"
33
+ />
53
34
  </Heading>
54
35
  <ThemeSwitcher size="medium" color="secondary" />
55
36
  </div>
@@ -118,17 +99,13 @@
118
99
 
119
100
  <div class="p-4 flex flex-col gap-2 max-w-6xl m-auto">
120
101
  {#if route === "dashboard"}
121
- <Dashboard local onViewBackups={() => (route = "backups")} />
102
+ <Dashboard onViewBackups={() => (route = "backups")} />
122
103
  {:else if route === "backups"}
123
- <BackupsList local />
104
+ <BackupsList />
124
105
  {:else if route === "config"}
125
- {#if !mock}
126
- <GlobalSettings />
127
- {/if}
106
+ <GlobalSettings />
128
107
  {:else if route === "schedules"}
129
- {#if !mock}
130
- <ScheduleList />
131
- {/if}
108
+ <ScheduleList />
132
109
  {/if}
133
110
  </div>
134
111
  </AppShell>
@@ -1,6 +1,3 @@
1
- type $$ComponentProps = {
2
- mock: boolean;
3
- };
4
- declare const TestUi: import("svelte").Component<$$ComponentProps, {}, "">;
1
+ declare const TestUi: import("svelte").Component<Record<string, never>, {}, "">;
5
2
  type TestUi = ReturnType<typeof TestUi>;
6
3
  export default TestUi;
@@ -1,5 +1,4 @@
1
1
  <script lang="ts">
2
- import { options } from "../../../options";
3
2
  import { getReadableErrorMessage } from "../../../utils/handle-error";
4
3
  import {
5
4
  Alert,
@@ -27,8 +26,6 @@
27
26
  onNavigate?: (route: string) => void;
28
27
  };
29
28
 
30
- const { advanced } = options;
31
-
32
29
  const { onNavigate }: Props = $props();
33
30
 
34
31
  const query = useRepositories();
@@ -25,7 +25,7 @@
25
25
  {@const seed = seedOf(groupIndex, index)}
26
26
  <div
27
27
  class="h-36 grow rounded-sm"
28
- style="background-color: hsl(0 0% {grey(seed)}%); flex-basis: {aspect(
28
+ style="background-color: color-mix(in oklch, var(--immich-ui-light) {grey(seed)}%, var(--immich-ui-dark)); flex-basis: {aspect(
29
29
  seed,
30
30
  ) * 9}rem"
31
31
  ></div>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { events, SocketEvent, useSocket } from "../../events";
3
+ import { getProvider } from "../../providers";
3
4
  import { onDestroy, onMount } from "svelte";
4
5
 
5
6
  type Props = {
@@ -7,7 +8,8 @@
7
8
  };
8
9
 
9
10
  const props: Props = $props();
10
- const disconnect = useSocket();
11
+ const disconnect =
12
+ getProvider().api === "orchestrator" ? useSocket() : () => {};
11
13
 
12
14
  onMount(() => {
13
15
  for (const key of Object.keys(props)) {
@@ -8,7 +8,10 @@
8
8
 
9
9
  const props: Props = $props();
10
10
 
11
- const format = () => DateTime.fromISO(props.time).toRelative();
11
+ const format = () => {
12
+ const time = DateTime.fromISO(props.time);
13
+ return DateTime.now().diff(time).as('minute') < 1 ? "a moment ago" : time.toRelative();
14
+ };
12
15
 
13
16
  let text = $state(format());
14
17
 
@@ -1,20 +1,13 @@
1
1
  <script lang="ts">
2
2
  import { TooltipProvider } from "@immich/ui";
3
3
  import { QueryClientProvider } from "@tanstack/svelte-query";
4
- import { defaults as orchestrationDefaults } from "../../fetch-client";
5
4
  import type { Snippet } from "svelte";
6
5
 
7
6
  type Props = {
8
7
  children: Snippet;
9
- baseUrl?: string;
10
8
  };
11
9
 
12
- const { baseUrl, children }: Props = $props();
13
-
14
- // svelte-ignore state_referenced_locally
15
- if (typeof baseUrl === "string") {
16
- orchestrationDefaults.baseUrl = baseUrl;
17
- }
10
+ const { children }: Props = $props();
18
11
 
19
12
  import { queryClient } from "../../query-client";
20
13
  </script>
@@ -1,7 +1,6 @@
1
1
  import type { Snippet } from "svelte";
2
2
  type Props = {
3
3
  children: Snippet;
4
- baseUrl?: string;
5
4
  };
6
5
  declare const YuccaContext: import("svelte").Component<Props, {}, "">;
7
6
  type YuccaContext = ReturnType<typeof YuccaContext>;
package/dist/events.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { io, Socket } from 'socket.io-client';
2
- import { defaults } from './fetch-client';
2
+ import { getProvider } from './providers';
3
3
  let socket;
4
4
  export const events = new EventTarget();
5
5
  export class SocketEvent extends Event {
@@ -10,7 +10,7 @@ export class SocketEvent extends Event {
10
10
  }
11
11
  }
12
12
  function createSocket() {
13
- socket = io(defaults.baseUrl || undefined, {
13
+ socket = io(getProvider().baseUrl || undefined, {
14
14
  path: '/api/yucca/socket.io',
15
15
  transports: ['websocket'],
16
16
  autoConnect: false,
@@ -6,9 +6,7 @@
6
6
  */
7
7
  import * as Oazapfts from "@oazapfts/runtime";
8
8
  export declare const defaults: Oazapfts.Defaults<Oazapfts.CustomHeaders>;
9
- export declare const servers: {
10
- server1: string;
11
- };
9
+ export declare const servers: {};
12
10
  export type DeviceFlowEventType = "START" | "SUCCESS" | "FAILURE";
13
11
  export type DeviceFlowFailureReason = "NOT_CONNECTED" | "DEVICE_FLOW_FAILED" | "WRONG_ACCOUNT" | "UNKNOWN";
14
12
  export type DeviceFlowEventDto = {
@@ -83,6 +81,27 @@ export type IntegrationsResponseDto = {
83
81
  immichState?: ImmichStateDto;
84
82
  immichIntegration?: ImmichIntegrationDto;
85
83
  };
84
+ export type TaskStatus = "incomplete" | "complete" | "warn" | "failed";
85
+ export type RepositoryMetricsDto = {
86
+ lastBackup?: string | null;
87
+ lastBackupStatus?: TaskStatus;
88
+ lastBackupDuration?: number;
89
+ sizeBytes: number;
90
+ };
91
+ export type RepositoryMeterDto = {
92
+ sizeBytes: number;
93
+ objectCount: number;
94
+ lastUpdated?: string | null;
95
+ };
96
+ export type RepositoryBackendDto = {
97
+ id: string;
98
+ "type": BackendType;
99
+ online: boolean;
100
+ };
101
+ export type RepositoryBackendsDto = {
102
+ primary: RepositoryBackendDto;
103
+ secondary: RepositoryBackendDto[];
104
+ };
86
105
  export type RetentionPolicyDto = {
87
106
  keepLast?: number;
88
107
  keepWithin?: string;
@@ -92,6 +111,48 @@ export type RetentionPolicyDto = {
92
111
  keepWithinMonthly?: string;
93
112
  keepWithinYearly?: string;
94
113
  };
114
+ export type RepositoryConfigurationDto = {
115
+ paths: string[];
116
+ retentionPolicy?: (RetentionPolicyDto) | null;
117
+ };
118
+ export type LocalRepositoryDto = {
119
+ id: string;
120
+ worm: boolean;
121
+ name: string;
122
+ siteCode: string | null;
123
+ storageClusterCode: string | null;
124
+ metrics: RepositoryMetricsDto;
125
+ meter?: RepositoryMeterDto;
126
+ backends?: RepositoryBackendsDto;
127
+ configuration?: RepositoryConfigurationDto;
128
+ };
129
+ export type ScheduleDto = {
130
+ id: string;
131
+ name: string;
132
+ paused: boolean;
133
+ cron: string;
134
+ repositories: string[];
135
+ lastRun?: string;
136
+ lastFinished?: string;
137
+ };
138
+ export type RunStatus = "incomplete" | "complete" | "warn" | "failed";
139
+ export type RunType = "schedule" | "restore" | "backup" | "forget";
140
+ export type RunDto = {
141
+ id: string;
142
+ repositoryId: string;
143
+ start: string;
144
+ end?: string;
145
+ logFilePath: string;
146
+ status: RunStatus;
147
+ "type": RunType;
148
+ };
149
+ export type ImmichBackupStatusDto = {
150
+ integration?: ImmichIntegrationDto;
151
+ repository?: LocalRepositoryDto;
152
+ backend?: BackendDto;
153
+ schedule?: ScheduleDto;
154
+ latestBackupRun?: RunDto;
155
+ };
95
156
  export type ConfigureImmichIntegrationRequestDto = {
96
157
  name: string;
97
158
  worm: boolean;
@@ -102,6 +163,9 @@ export type ConfigureImmichIntegrationRequestDto = {
102
163
  retentionPolicy?: (RetentionPolicyDto) | null;
103
164
  paused?: boolean;
104
165
  };
166
+ export type ConfigureImmichIntegrationResponseDto = {
167
+ repositoryId: string;
168
+ };
105
169
  export type ImmichRollbackRequestDto = {
106
170
  repositoryId: string;
107
171
  snapshotId: string;
@@ -134,42 +198,6 @@ export type RepositoryCreateRequestDto = {
134
198
  site?: string;
135
199
  paths?: string[];
136
200
  };
137
- export type TaskStatus = "incomplete" | "complete" | "warn" | "failed";
138
- export type RepositoryMetricsDto = {
139
- lastBackup?: string | null;
140
- lastBackupStatus?: TaskStatus;
141
- lastBackupDuration?: number;
142
- sizeBytes: number;
143
- };
144
- export type RepositoryMeterDto = {
145
- sizeBytes: number;
146
- objectCount: number;
147
- lastUpdated?: string | null;
148
- };
149
- export type RepositoryBackendDto = {
150
- id: string;
151
- "type": BackendType;
152
- online: boolean;
153
- };
154
- export type RepositoryBackendsDto = {
155
- primary: RepositoryBackendDto;
156
- secondary: RepositoryBackendDto[];
157
- };
158
- export type RepositoryConfigurationDto = {
159
- paths: string[];
160
- retentionPolicy?: (RetentionPolicyDto) | null;
161
- };
162
- export type LocalRepositoryDto = {
163
- id: string;
164
- worm: boolean;
165
- name: string;
166
- siteCode: string | null;
167
- storageClusterCode: string | null;
168
- metrics: RepositoryMetricsDto;
169
- meter?: RepositoryMeterDto;
170
- backends?: RepositoryBackendsDto;
171
- configuration?: RepositoryConfigurationDto;
172
- };
173
201
  export type RepositoryCreateResponseDto = {
174
202
  repository: LocalRepositoryDto;
175
203
  };
@@ -224,17 +252,6 @@ export type RepositoryCheckImportResponseDto = {
224
252
  export type RepositoryPrimaryBackendReconfigureRequestDto = {
225
253
  backendId: string;
226
254
  };
227
- export type RunStatus = "incomplete" | "complete" | "warn" | "failed";
228
- export type RunType = "schedule" | "restore" | "backup" | "forget";
229
- export type RunDto = {
230
- id: string;
231
- repositoryId: string;
232
- start: string;
233
- end?: string;
234
- logFilePath: string;
235
- status: RunStatus;
236
- "type": RunType;
237
- };
238
255
  export type RunHistoryResponseDto = {
239
256
  runs: RunDto[];
240
257
  };
@@ -271,15 +288,6 @@ export type ScheduleCreateRequestDto = {
271
288
  cron: string;
272
289
  repositories: string[];
273
290
  };
274
- export type ScheduleDto = {
275
- id: string;
276
- name: string;
277
- paused: boolean;
278
- cron: string;
279
- repositories: string[];
280
- lastRun?: string;
281
- lastFinished?: string;
282
- };
283
291
  export type ScheduleCreateResponseDto = {
284
292
  schedule: ScheduleDto;
285
293
  };
@@ -306,7 +314,8 @@ export declare function getFileListing({ path }?: {
306
314
  path?: string;
307
315
  }, opts?: Oazapfts.RequestOpts): Promise<FilesystemListingResponseDto>;
308
316
  export declare function getIntegrations(opts?: Oazapfts.RequestOpts): Promise<IntegrationsResponseDto>;
309
- export declare function configureImmichIntegration(configureImmichIntegrationRequestDto: ConfigureImmichIntegrationRequestDto, opts?: Oazapfts.RequestOpts): Promise<never>;
317
+ export declare function getImmichBackupStatus(opts?: Oazapfts.RequestOpts): Promise<ImmichBackupStatusDto>;
318
+ export declare function configureImmichIntegration(configureImmichIntegrationRequestDto: ConfigureImmichIntegrationRequestDto, opts?: Oazapfts.RequestOpts): Promise<ConfigureImmichIntegrationResponseDto>;
310
319
  export declare function startImmichRollback(immichRollbackRequestDto: ImmichRollbackRequestDto, opts?: Oazapfts.RequestOpts): Promise<never>;
311
320
  export declare function onboardingStatus(opts?: Oazapfts.RequestOpts): Promise<OnboardingStatusResponseDto>;
312
321
  export declare function currentRecoveryKey(opts?: Oazapfts.RequestOpts): Promise<CurrentRecoveryKeyResponse>;
@@ -8,12 +8,10 @@ import * as Oazapfts from "@oazapfts/runtime";
8
8
  import * as QS from "@oazapfts/runtime/query";
9
9
  export const defaults = {
10
10
  headers: {},
11
- baseUrl: "http://localhost:22676"
11
+ baseUrl: "/"
12
12
  };
13
13
  const oazapfts = Oazapfts.runtime(defaults);
14
- export const servers = {
15
- server1: "http://localhost:22676"
16
- };
14
+ export const servers = {};
17
15
  export function connectDeviceFlow(opts) {
18
16
  return oazapfts.ok(oazapfts.fetchJson("/api/yucca/auth/oidc/device", {
19
17
  ...opts
@@ -68,8 +66,13 @@ export function getIntegrations(opts) {
68
66
  ...opts
69
67
  }));
70
68
  }
69
+ export function getImmichBackupStatus(opts) {
70
+ return oazapfts.ok(oazapfts.fetchJson("/api/yucca/integrations/immich/status", {
71
+ ...opts
72
+ }));
73
+ }
71
74
  export function configureImmichIntegration(configureImmichIntegrationRequestDto, opts) {
72
- return oazapfts.ok(oazapfts.fetchText("/api/yucca/integrations/immich", oazapfts.json({
75
+ return oazapfts.ok(oazapfts.fetchJson("/api/yucca/integrations/immich", oazapfts.json({
73
76
  ...opts,
74
77
  method: "POST",
75
78
  body: configureImmichIntegrationRequestDto
@@ -1,11 +1,15 @@
1
1
  import * as orchestrationApiClient from './fetch-client';
2
- export declare abstract class BaseProvider {
3
- abstract getRepositories(): Promise<orchestrationApiClient.RepositoryListResponseDto>;
4
- }
5
- export declare const yuccaApiProvider: BaseProvider;
6
- export declare const orchestrationApiProvider: BaseProvider;
7
- export declare class MockProvider extends BaseProvider {
8
- getRepositories(): Promise<orchestrationApiClient.RepositoryListResponseDto>;
9
- }
10
- export declare const setProvider: (provider: BaseProvider) => void;
11
- export declare const getProvider: () => BaseProvider;
2
+ export type YuccaApi = 'orchestrator' | 'customer';
3
+ type GetRepositories = () => Promise<orchestrationApiClient.RepositoryListResponseDto>;
4
+ export type YuccaProvider = {
5
+ api: YuccaApi;
6
+ baseUrl: string;
7
+ getRepositories: GetRepositories;
8
+ };
9
+ export type YuccaOptions = {
10
+ api: YuccaApi;
11
+ baseUrl?: string;
12
+ };
13
+ export declare const configureYucca: ({ api, baseUrl, }: YuccaOptions) => YuccaProvider;
14
+ export declare const getProvider: () => YuccaProvider;
15
+ export {};
package/dist/providers.js CHANGED
@@ -1,37 +1,27 @@
1
1
  import * as yuccaApiClient from '@futo-org/backups-api-client';
2
2
  import * as orchestrationApiClient from './fetch-client';
3
- export class BaseProvider {
4
- }
5
- /* eslint-disable @typescript-eslint/require-await */
6
- export const yuccaApiProvider = yuccaApiClient;
7
- export const orchestrationApiProvider = orchestrationApiClient;
8
- export class MockProvider extends BaseProvider {
9
- async getRepositories() {
10
- return {
11
- repositories: [
12
- {
13
- id: 'repo1',
14
- name: 'My Repository',
15
- worm: false,
16
- siteCode: 'local',
17
- storageClusterCode: 'local-dev',
18
- metrics: {
19
- sizeBytes: 1337,
20
- },
21
- },
22
- ],
23
- };
24
- }
25
- }
26
- /* eslint-enable @typescript-eslint/require-await */
3
+ const clients = {
4
+ orchestrator: orchestrationApiClient,
5
+ customer: yuccaApiClient,
6
+ };
27
7
  const KEY = '__yucca_provider__';
28
- export const setProvider = (provider) => {
8
+ export const configureYucca = ({ api, baseUrl, }) => {
9
+ const client = clients[api];
10
+ if (baseUrl !== undefined) {
11
+ client.defaults.baseUrl = baseUrl;
12
+ }
13
+ const provider = {
14
+ api,
15
+ baseUrl: client.defaults.baseUrl.replace(/\/$/, ''),
16
+ getRepositories: client.getRepositories,
17
+ };
29
18
  globalThis[KEY] = provider;
19
+ return provider;
30
20
  };
31
21
  export const getProvider = () => {
32
22
  const provider = globalThis[KEY];
33
23
  if (!provider) {
34
- throw new Error('Provider not set call setProvider() before getProvider()');
24
+ throw new Error('Yucca is not configured: call configureYucca() before rendering any component');
35
25
  }
36
26
  return provider;
37
27
  };
@@ -4,3 +4,25 @@ export declare const getBackupPageActions: (repositoryId: string | undefined, on
4
4
  ViewRecoveryKey: ActionItem;
5
5
  BackUpNow: ActionItem;
6
6
  };
7
+ export declare const immichBackupStatusKeys: {
8
+ all: readonly ["immichBackupStatus"];
9
+ };
10
+ export declare const useImmichBackupStatusEventHandler: () => {
11
+ onIntegrationUpdate: () => undefined;
12
+ onRepositoryUpdate: () => undefined;
13
+ onRepositoryDelete: () => undefined;
14
+ onScheduleUpdate: () => undefined;
15
+ onRunCreate: () => undefined;
16
+ onRunUpdate: () => undefined;
17
+ };
18
+ export type ImmichBackupStatus = {
19
+ kind: 'loading' | 'offline' | 'missing' | 'running' | 'paused' | 'unconfigured' | 'never';
20
+ } | {
21
+ kind: 'complete' | 'warn' | 'failed';
22
+ lastBackup: string;
23
+ };
24
+ export declare const useImmichBackupStatus: () => {
25
+ readonly repository: import("../fetch-client").LocalRepositoryDto | undefined;
26
+ readonly schedule: import("../fetch-client").ScheduleDto | undefined;
27
+ readonly status: ImmichBackupStatus;
28
+ };
@@ -1,6 +1,10 @@
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';
5
+ import { queryClient } from '../query-client';
6
+ import { getBackupOutcome } from '../utils/backup-status';
7
+ import { createQuery } from '@tanstack/svelte-query';
4
8
  import { handleCreateBackup } from './repository.service';
5
9
  export const getBackupPageActions = (repositoryId, onConfigure) => {
6
10
  const Configure = {
@@ -21,3 +25,57 @@ export const getBackupPageActions = (repositoryId, onConfigure) => {
21
25
  };
22
26
  return { Configure, ViewRecoveryKey, BackUpNow };
23
27
  };
28
+ export const immichBackupStatusKeys = {
29
+ all: ['immichBackupStatus'],
30
+ };
31
+ const invalidateImmichBackupStatus = () => void queryClient.invalidateQueries({
32
+ queryKey: immichBackupStatusKeys.all,
33
+ });
34
+ export const useImmichBackupStatusEventHandler = () => ({
35
+ onIntegrationUpdate: invalidateImmichBackupStatus,
36
+ onRepositoryUpdate: invalidateImmichBackupStatus,
37
+ onRepositoryDelete: invalidateImmichBackupStatus,
38
+ onScheduleUpdate: invalidateImmichBackupStatus,
39
+ onRunCreate: invalidateImmichBackupStatus,
40
+ onRunUpdate: invalidateImmichBackupStatus,
41
+ });
42
+ const toImmichBackupStatus = (data, loading) => {
43
+ if (loading || !data) {
44
+ return { kind: 'loading' };
45
+ }
46
+ if (data.backend?.isOnline === false) {
47
+ return { kind: 'offline' };
48
+ }
49
+ if (data.repository?.backends?.primary.online === false) {
50
+ return { kind: 'missing' };
51
+ }
52
+ if (data.latestBackupRun?.status === 'incomplete') {
53
+ return { kind: 'running' };
54
+ }
55
+ const outcome = getBackupOutcome(data.repository?.metrics);
56
+ const lastBackup = data.repository?.metrics.lastBackup;
57
+ if (lastBackup && outcome !== 'never') {
58
+ return { kind: outcome, lastBackup };
59
+ }
60
+ if (data.schedule?.paused) {
61
+ return { kind: 'paused' };
62
+ }
63
+ return { kind: data.repository ? 'never' : 'unconfigured' };
64
+ };
65
+ export const useImmichBackupStatus = () => {
66
+ const query = createQuery(() => ({
67
+ queryKey: immichBackupStatusKeys.all,
68
+ queryFn: () => getImmichBackupStatus(),
69
+ }), () => queryClient);
70
+ return {
71
+ get repository() {
72
+ return query.data?.repository;
73
+ },
74
+ get schedule() {
75
+ return query.data?.schedule;
76
+ },
77
+ get status() {
78
+ return toImmichBackupStatus(query.data, query.isLoading);
79
+ },
80
+ };
81
+ };
@@ -10,5 +10,7 @@ export declare const useIntegrationEventHandler: () => {
10
10
  integrations: IntegrationsResponseDto;
11
11
  }>): void;
12
12
  };
13
- export declare const useConfigureImmichIntegration: () => import("@tanstack/svelte-query").CreateMutationResult<never, Error, ConfigureImmichIntegrationRequestDto, unknown>;
14
- export declare const useConfigureImmichDefaults: () => import("@tanstack/svelte-query").CreateMutationResult<void, Error, void, unknown>;
13
+ export declare const useConfigureImmichIntegration: () => import("@tanstack/svelte-query").CreateMutationResult<import("../fetch-client").ConfigureImmichIntegrationResponseDto, Error, ConfigureImmichIntegrationRequestDto, unknown>;
14
+ export declare const useConfigureAndStartImmichIntegration: () => import("@tanstack/svelte-query").CreateMutationResult<{
15
+ repositoryId: string;
16
+ }, Error, void, unknown>;