@futo-org/backups-orchestrator-ui 0.1.71

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 (192) hide show
  1. package/LICENSE +41 -0
  2. package/dist/components/backends/BackendItem.svelte +64 -0
  3. package/dist/components/backends/BackendItem.svelte.d.ts +10 -0
  4. package/dist/components/backends/BackendsList.svelte +73 -0
  5. package/dist/components/backends/BackendsList.svelte.d.ts +7 -0
  6. package/dist/components/backends/CreateLocalBackend.svelte +46 -0
  7. package/dist/components/backends/CreateLocalBackend.svelte.d.ts +7 -0
  8. package/dist/components/backends/OAuthDeviceFlow.svelte +78 -0
  9. package/dist/components/backends/OAuthDeviceFlow.svelte.d.ts +9 -0
  10. package/dist/components/backups/BackupItem.svelte +59 -0
  11. package/dist/components/backups/BackupItem.svelte.d.ts +7 -0
  12. package/dist/components/backups/BackupsList.svelte +82 -0
  13. package/dist/components/backups/BackupsList.svelte.d.ts +8 -0
  14. package/dist/components/backups/dialogs/ConfigureRepositoryModal.svelte +102 -0
  15. package/dist/components/backups/dialogs/ConfigureRepositoryModal.svelte.d.ts +8 -0
  16. package/dist/components/backups/dialogs/CreateRepositoryModal.svelte +59 -0
  17. package/dist/components/backups/dialogs/CreateRepositoryModal.svelte.d.ts +6 -0
  18. package/dist/components/backups/dialogs/ImportRepositoryModal.svelte +62 -0
  19. package/dist/components/backups/dialogs/ImportRepositoryModal.svelte.d.ts +8 -0
  20. package/dist/components/backups/dialogs/RestoreSnapshotModal.svelte +109 -0
  21. package/dist/components/backups/dialogs/RestoreSnapshotModal.svelte.d.ts +8 -0
  22. package/dist/components/backups/dialogs/ViewLogModal.svelte +208 -0
  23. package/dist/components/backups/dialogs/ViewLogModal.svelte.d.ts +7 -0
  24. package/dist/components/backups/metrics-history/MetricsHistoryModal.svelte +166 -0
  25. package/dist/components/backups/metrics-history/MetricsHistoryModal.svelte.d.ts +8 -0
  26. package/dist/components/backups/run-history/RepositoryRunHistory.svelte +39 -0
  27. package/dist/components/backups/run-history/RepositoryRunHistory.svelte.d.ts +7 -0
  28. package/dist/components/backups/run-history/RepositoryRunHistoryItem.svelte +45 -0
  29. package/dist/components/backups/run-history/RepositoryRunHistoryItem.svelte.d.ts +7 -0
  30. package/dist/components/backups/run-history/RunHistoryModal.svelte +18 -0
  31. package/dist/components/backups/run-history/RunHistoryModal.svelte.d.ts +8 -0
  32. package/dist/components/backups/snapshots-list/RepositorySnapshotsList.svelte +53 -0
  33. package/dist/components/backups/snapshots-list/RepositorySnapshotsList.svelte.d.ts +7 -0
  34. package/dist/components/backups/snapshots-list/RepositorySnapshotsListItem.svelte +41 -0
  35. package/dist/components/backups/snapshots-list/RepositorySnapshotsListItem.svelte.d.ts +8 -0
  36. package/dist/components/backups/snapshots-list/SnapshotsListModal.svelte +18 -0
  37. package/dist/components/backups/snapshots-list/SnapshotsListModal.svelte.d.ts +8 -0
  38. package/dist/components/dashboard/Dashboard.svelte +52 -0
  39. package/dist/components/dashboard/Dashboard.svelte.d.ts +9 -0
  40. package/dist/components/dashboard/DashboardAvgBackupTime.svelte +34 -0
  41. package/dist/components/dashboard/DashboardAvgBackupTime.svelte.d.ts +7 -0
  42. package/dist/components/dashboard/DashboardBackupHealth.svelte +91 -0
  43. package/dist/components/dashboard/DashboardBackupHealth.svelte.d.ts +9 -0
  44. package/dist/components/dashboard/DashboardCurrentUsage.svelte +10 -0
  45. package/dist/components/dashboard/DashboardCurrentUsage.svelte.d.ts +18 -0
  46. package/dist/components/dashboard/DashboardDailyBackupTime.svelte +31 -0
  47. package/dist/components/dashboard/DashboardDailyBackupTime.svelte.d.ts +7 -0
  48. package/dist/components/dashboard/DashboardInstall.svelte +15 -0
  49. package/dist/components/dashboard/DashboardInstall.svelte.d.ts +18 -0
  50. package/dist/components/dashboard/DashboardRecentBackups.svelte +104 -0
  51. package/dist/components/dashboard/DashboardRecentBackups.svelte.d.ts +8 -0
  52. package/dist/components/dashboard/DashboardTotalStored.svelte +27 -0
  53. package/dist/components/dashboard/DashboardTotalStored.svelte.d.ts +7 -0
  54. package/dist/components/integrations/immich/ImmichBackupsPage.svelte +14 -0
  55. package/dist/components/integrations/immich/ImmichBackupsPage.svelte.d.ts +6 -0
  56. package/dist/components/integrations/immich/ImmichConfigureBackup.svelte +402 -0
  57. package/dist/components/integrations/immich/ImmichConfigureBackup.svelte.d.ts +9 -0
  58. package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte +80 -0
  59. package/dist/components/integrations/immich/ImmichConfirmDefaultBackup.svelte.d.ts +8 -0
  60. package/dist/components/integrations/immich/ImmichManageBackup.svelte +77 -0
  61. package/dist/components/integrations/immich/ImmichManageBackup.svelte.d.ts +3 -0
  62. package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte +100 -0
  63. package/dist/components/integrations/immich/ImmichManageBackupOverview.svelte.d.ts +8 -0
  64. package/dist/components/integrations/immich/ImmichOnboardingRestoreFlow.svelte +75 -0
  65. package/dist/components/integrations/immich/ImmichOnboardingRestoreFlow.svelte.d.ts +7 -0
  66. package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte +113 -0
  67. package/dist/components/integrations/immich/ImmichOnboardingSetupFlow.svelte.d.ts +8 -0
  68. package/dist/components/onboarding/OnboardingGate.svelte +48 -0
  69. package/dist/components/onboarding/OnboardingGate.svelte.d.ts +9 -0
  70. package/dist/components/onboarding/RecoveryKeyDisplay.svelte +103 -0
  71. package/dist/components/onboarding/RecoveryKeyDisplay.svelte.d.ts +6 -0
  72. package/dist/components/onboarding/SampleOnboarding.svelte +98 -0
  73. package/dist/components/onboarding/SampleOnboarding.svelte.d.ts +9 -0
  74. package/dist/components/onboarding/dialogs/BackupsRecoveryKeyModal.svelte +43 -0
  75. package/dist/components/onboarding/dialogs/BackupsRecoveryKeyModal.svelte.d.ts +6 -0
  76. package/dist/components/onboarding/restore-point-flow/RestorePointFlow.svelte +96 -0
  77. package/dist/components/onboarding/restore-point-flow/RestorePointFlow.svelte.d.ts +8 -0
  78. package/dist/components/onboarding/restore-point-flow/RestorePointFlow2SelectSnapshot.svelte +83 -0
  79. package/dist/components/onboarding/restore-point-flow/RestorePointFlow2SelectSnapshot.svelte.d.ts +9 -0
  80. package/dist/components/onboarding/restore-point-flow/RestorePointFlow3ConfirmRestore.svelte +118 -0
  81. package/dist/components/onboarding/restore-point-flow/RestorePointFlow3ConfirmRestore.svelte.d.ts +10 -0
  82. package/dist/components/onboarding/restore-point-flow/RestorePointFlow4Restore.svelte +59 -0
  83. package/dist/components/onboarding/restore-point-flow/RestorePointFlow4Restore.svelte.d.ts +8 -0
  84. package/dist/components/onboarding/stages/OnboardingStageBackupServices.svelte +82 -0
  85. package/dist/components/onboarding/stages/OnboardingStageBackupServices.svelte.d.ts +8 -0
  86. package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte +56 -0
  87. package/dist/components/onboarding/stages/OnboardingStageKeyConfirm.svelte.d.ts +9 -0
  88. package/dist/components/onboarding/stages/OnboardingStageKeyImport.svelte +57 -0
  89. package/dist/components/onboarding/stages/OnboardingStageKeyImport.svelte.d.ts +8 -0
  90. package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte +50 -0
  91. package/dist/components/onboarding/stages/OnboardingStageKeyIntro.svelte.d.ts +7 -0
  92. package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte +44 -0
  93. package/dist/components/onboarding/stages/OnboardingStageKeySave.svelte.d.ts +8 -0
  94. package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte +56 -0
  95. package/dist/components/onboarding/stages/OnboardingStageWelcome.svelte.d.ts +9 -0
  96. package/dist/components/onboarding/stages/SampleCreateFirstBackup.svelte +43 -0
  97. package/dist/components/onboarding/stages/SampleCreateFirstBackup.svelte.d.ts +7 -0
  98. package/dist/components/onboarding/stages/SampleCreateFirstSchedule.svelte +49 -0
  99. package/dist/components/onboarding/stages/SampleCreateFirstSchedule.svelte.d.ts +7 -0
  100. package/dist/components/schedules/RepositoryPicker.svelte +105 -0
  101. package/dist/components/schedules/RepositoryPicker.svelte.d.ts +6 -0
  102. package/dist/components/schedules/ScheduleItem.svelte +47 -0
  103. package/dist/components/schedules/ScheduleItem.svelte.d.ts +8 -0
  104. package/dist/components/schedules/ScheduleList.svelte +51 -0
  105. package/dist/components/schedules/ScheduleList.svelte.d.ts +3 -0
  106. package/dist/components/schedules/dialogs/ConfigureScheduleModal.svelte +48 -0
  107. package/dist/components/schedules/dialogs/ConfigureScheduleModal.svelte.d.ts +8 -0
  108. package/dist/components/schedules/dialogs/CreateScheduleModal.svelte +43 -0
  109. package/dist/components/schedules/dialogs/CreateScheduleModal.svelte.d.ts +6 -0
  110. package/dist/components/test/ImmichTestUi.svelte +183 -0
  111. package/dist/components/test/ImmichTestUi.svelte.d.ts +6 -0
  112. package/dist/components/test/TestUi.svelte +134 -0
  113. package/dist/components/test/TestUi.svelte.d.ts +6 -0
  114. package/dist/components/test/dashboard/ActiveJobs.svelte +380 -0
  115. package/dist/components/test/dashboard/ActiveJobs.svelte.d.ts +3 -0
  116. package/dist/components/test/dashboard/BackupHealth.svelte +95 -0
  117. package/dist/components/test/dashboard/BackupHealth.svelte.d.ts +7 -0
  118. package/dist/components/test/dashboard/BackupStats.svelte +117 -0
  119. package/dist/components/test/dashboard/BackupStats.svelte.d.ts +8 -0
  120. package/dist/components/test/dashboard/Dashboard.svelte +76 -0
  121. package/dist/components/test/dashboard/Dashboard.svelte.d.ts +6 -0
  122. package/dist/components/test/dashboard/RecentBackups.svelte +96 -0
  123. package/dist/components/test/dashboard/RecentBackups.svelte.d.ts +8 -0
  124. package/dist/components/ui/PageLayout.svelte +67 -0
  125. package/dist/components/ui/PageLayout.svelte.d.ts +10 -0
  126. package/dist/components/ui/PathListField.svelte +83 -0
  127. package/dist/components/ui/PathListField.svelte.d.ts +17 -0
  128. package/dist/components/ui/PathPickerField.svelte +74 -0
  129. package/dist/components/ui/PathPickerField.svelte.d.ts +15 -0
  130. package/dist/components/ui/PathPickerModal.svelte +219 -0
  131. package/dist/components/ui/PathPickerModal.svelte.d.ts +14 -0
  132. package/dist/components/ui/StackList.svelte +30 -0
  133. package/dist/components/ui/StackList.svelte.d.ts +30 -0
  134. package/dist/components/ui/StackListItem.svelte +64 -0
  135. package/dist/components/ui/StackListItem.svelte.d.ts +13 -0
  136. package/dist/components/ui/VisualisationGauge.svelte +25 -0
  137. package/dist/components/ui/VisualisationGauge.svelte.d.ts +10 -0
  138. package/dist/components/ui/VisualisationSegmentedBar.svelte +48 -0
  139. package/dist/components/ui/VisualisationSegmentedBar.svelte.d.ts +14 -0
  140. package/dist/components/util/OnEvents.svelte +31 -0
  141. package/dist/components/util/OnEvents.svelte.d.ts +7 -0
  142. package/dist/components/util/RelativeTime.svelte +21 -0
  143. package/dist/components/util/RelativeTime.svelte.d.ts +6 -0
  144. package/dist/components/util/Suspense.svelte +21 -0
  145. package/dist/components/util/Suspense.svelte.d.ts +29 -0
  146. package/dist/components/util/TimedButton.svelte +37 -0
  147. package/dist/components/util/TimedButton.svelte.d.ts +7 -0
  148. package/dist/components/util/YuccaContext.svelte +26 -0
  149. package/dist/components/util/YuccaContext.svelte.d.ts +8 -0
  150. package/dist/events.d.ts +6 -0
  151. package/dist/events.js +47 -0
  152. package/dist/fetch-client.d.ts +289 -0
  153. package/dist/fetch-client.js +233 -0
  154. package/dist/index.d.ts +28 -0
  155. package/dist/index.js +28 -0
  156. package/dist/options.d.ts +5 -0
  157. package/dist/options.js +6 -0
  158. package/dist/providers.d.ts +11 -0
  159. package/dist/providers.js +35 -0
  160. package/dist/query-client.d.ts +2 -0
  161. package/dist/query-client.js +2 -0
  162. package/dist/services/backend.service.d.ts +18 -0
  163. package/dist/services/backend.service.js +61 -0
  164. package/dist/services/filesystem.service.d.ts +2 -0
  165. package/dist/services/filesystem.service.js +11 -0
  166. package/dist/services/immich.integration.service.d.ts +6 -0
  167. package/dist/services/immich.integration.service.js +24 -0
  168. package/dist/services/integrations.service.d.ts +13 -0
  169. package/dist/services/integrations.service.js +42 -0
  170. package/dist/services/log.service.svelte.d.ts +53 -0
  171. package/dist/services/log.service.svelte.js +93 -0
  172. package/dist/services/metricsHistory.service.d.ts +4 -0
  173. package/dist/services/metricsHistory.service.js +12 -0
  174. package/dist/services/onboarding.service.d.ts +11 -0
  175. package/dist/services/onboarding.service.js +56 -0
  176. package/dist/services/repository.service.d.ts +45 -0
  177. package/dist/services/repository.service.js +157 -0
  178. package/dist/services/runHistory.service.d.ts +26 -0
  179. package/dist/services/runHistory.service.js +54 -0
  180. package/dist/services/schedule.service.d.ts +35 -0
  181. package/dist/services/schedule.service.js +126 -0
  182. package/dist/services/snapshot.service.d.ts +29 -0
  183. package/dist/services/snapshot.service.js +108 -0
  184. package/dist/services/task.service.d.ts +3 -0
  185. package/dist/services/task.service.js +20 -0
  186. package/dist/utils/actions.d.ts +2 -0
  187. package/dist/utils/actions.js +3 -0
  188. package/dist/utils/format.d.ts +2 -0
  189. package/dist/utils/format.js +24 -0
  190. package/dist/utils/handle-error.d.ts +9 -0
  191. package/dist/utils/handle-error.js +42 -0
  192. package/package.json +79 -0
@@ -0,0 +1,35 @@
1
+ import * as yuccaApiClient from '@futo-org/backups-api-client';
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
+ metrics: {
17
+ sizeBytes: 1337,
18
+ },
19
+ },
20
+ ],
21
+ };
22
+ }
23
+ }
24
+ /* eslint-enable @typescript-eslint/require-await */
25
+ const KEY = '__yucca_provider__';
26
+ export const setProvider = (provider) => {
27
+ globalThis[KEY] = provider;
28
+ };
29
+ export const getProvider = () => {
30
+ const provider = globalThis[KEY];
31
+ if (!provider) {
32
+ throw new Error('Provider not set — call setProvider() before getProvider()');
33
+ }
34
+ return provider;
35
+ };
@@ -0,0 +1,2 @@
1
+ import { QueryClient } from '@tanstack/svelte-query';
2
+ export declare const queryClient: QueryClient;
@@ -0,0 +1,2 @@
1
+ import { QueryClient } from '@tanstack/svelte-query';
2
+ export const queryClient = new QueryClient();
@@ -0,0 +1,18 @@
1
+ import { SocketEvent } from '../events';
2
+ import { type BackendDto, type CreateLocalBackendRequestDto } from '../fetch-client';
3
+ import { type ActionItem } from '@immich/ui';
4
+ export declare const backendKeys: {
5
+ all: readonly ["backends"];
6
+ };
7
+ export declare const useBackends: () => import("@tanstack/svelte-query").CreateQueryResult<BackendDto[], Error>;
8
+ export declare const useBackendEventHandler: () => {
9
+ onBackendCreate(event: SocketEvent<{
10
+ backend: BackendDto;
11
+ }>): void;
12
+ };
13
+ export declare const handleYuccaLogin: (onCreate?: (backendId: string) => void) => Promise<void>;
14
+ export declare const handleSetupLocalStorage: (onCreate?: (backendId: string) => void) => void;
15
+ export declare const useCreateLocalBackend: () => import("@tanstack/svelte-query").CreateMutationResult<import("../fetch-client").BackendResponseDto, Error, CreateLocalBackendRequestDto, unknown>;
16
+ export declare const getBackendActions: (backend: BackendDto) => {
17
+ LoginAgain: ActionItem;
18
+ };
@@ -0,0 +1,61 @@
1
+ import CreateLocalBackend from '../components/backends/CreateLocalBackend.svelte';
2
+ import OAuthDeviceFlow from '../components/backends/OAuthDeviceFlow.svelte';
3
+ import { SocketEvent } from '../events';
4
+ import { createLocalBackend, getBackends, oidcDeviceFlow, } from '../fetch-client';
5
+ import { queryClient } from '../query-client';
6
+ import { handleError } from '../utils/handle-error';
7
+ import { modalManager } from '@immich/ui';
8
+ import { mdiLogin } from '@mdi/js';
9
+ import { createMutation, createQuery } from '@tanstack/svelte-query';
10
+ export const backendKeys = {
11
+ all: ['backends'],
12
+ };
13
+ export const useBackends = () => createQuery(() => ({
14
+ queryKey: backendKeys.all,
15
+ queryFn: () => getBackends().then(({ backends }) => backends),
16
+ }), () => queryClient);
17
+ export const useBackendEventHandler = () => {
18
+ return {
19
+ onBackendCreate(event) {
20
+ queryClient.setQueryData(backendKeys.all, (data) => {
21
+ return data
22
+ ? [
23
+ ...data.filter((entry) => entry.id !== event.data.backend.id),
24
+ event.data.backend,
25
+ ]
26
+ : void 0;
27
+ });
28
+ },
29
+ };
30
+ };
31
+ export const handleYuccaLogin = async (onCreate) => {
32
+ try {
33
+ const response = await oidcDeviceFlow();
34
+ void modalManager.show(OAuthDeviceFlow, {
35
+ ...response,
36
+ onCreate,
37
+ });
38
+ window.open(response.verificationUri, '_blank');
39
+ }
40
+ catch (error) {
41
+ handleError(error, 'Failed to start login');
42
+ throw error;
43
+ }
44
+ };
45
+ export const handleSetupLocalStorage = (onCreate) => {
46
+ void modalManager.show(CreateLocalBackend, { onCreate });
47
+ };
48
+ export const useCreateLocalBackend = () => createMutation(() => ({
49
+ mutationFn: (dto) => createLocalBackend(dto),
50
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: backendKeys.all }),
51
+ onError: (error) => handleError(error, 'Failed to create local backend'),
52
+ }), () => queryClient);
53
+ export const getBackendActions = (backend) => {
54
+ const LoginAgain = {
55
+ icon: mdiLogin,
56
+ title: 'Login again',
57
+ onAction: () => void handleYuccaLogin(),
58
+ $if: () => backend.type === 'yucca' && !backend.isOnline,
59
+ };
60
+ return { LoginAgain };
61
+ };
@@ -0,0 +1,2 @@
1
+ import { sdk } from '..';
2
+ export declare const handleGetFileListing: (path?: string) => Promise<sdk.FilesystemListingResponseDto>;
@@ -0,0 +1,11 @@
1
+ import { sdk } from '..';
2
+ import { handleError } from '../utils/handle-error';
3
+ export const handleGetFileListing = async (path) => {
4
+ try {
5
+ return await sdk.getFileListing({ path });
6
+ }
7
+ catch (error) {
8
+ handleError(error, 'Failed to load directory listing');
9
+ throw error;
10
+ }
11
+ };
@@ -0,0 +1,6 @@
1
+ import { type ActionItem } from '@immich/ui';
2
+ export declare const getBackupPageActions: (repositoryId?: string) => {
3
+ Configure: ActionItem;
4
+ ViewRecoveryKey: ActionItem;
5
+ BackUpNow: ActionItem;
6
+ };
@@ -0,0 +1,24 @@
1
+ import ImmichConfigureBackup from '../components/integrations/immich/ImmichConfigureBackup.svelte';
2
+ import BackupsRecoveryKeyModal from '../components/onboarding/dialogs/BackupsRecoveryKeyModal.svelte';
3
+ import { modalManager } from '@immich/ui';
4
+ import { mdiCloudUploadOutline, mdiCogOutline, mdiKeyOutline } from '@mdi/js';
5
+ import { handleCreateBackup } from './repository.service';
6
+ export const getBackupPageActions = (repositoryId) => {
7
+ const Configure = {
8
+ title: 'Configure',
9
+ icon: mdiCogOutline,
10
+ onAction: () => modalManager.show(ImmichConfigureBackup, {}),
11
+ };
12
+ const ViewRecoveryKey = {
13
+ title: 'View recovery key',
14
+ icon: mdiKeyOutline,
15
+ onAction: () => void modalManager.show(BackupsRecoveryKeyModal, {}),
16
+ };
17
+ const BackUpNow = {
18
+ title: 'Back up now',
19
+ icon: mdiCloudUploadOutline,
20
+ onAction: () => void handleCreateBackup(repositoryId),
21
+ $if: () => typeof repositoryId === 'string',
22
+ };
23
+ return { Configure, ViewRecoveryKey, BackUpNow };
24
+ };
@@ -0,0 +1,13 @@
1
+ import { SocketEvent } from '../events';
2
+ import { type ConfigureImmichIntegrationRequestDto, type IntegrationsResponseDto } from '../fetch-client';
3
+ export declare const integrationsKeys: {
4
+ all: readonly ["integrations"];
5
+ };
6
+ export declare const useIntegrations: () => import("@tanstack/svelte-query").CreateQueryResult<IntegrationsResponseDto, Error>;
7
+ export declare const useIntegrationEventHandler: () => {
8
+ onIntegrationUpdate(event: SocketEvent<{
9
+ integrations: IntegrationsResponseDto;
10
+ }>): void;
11
+ };
12
+ export declare const useConfigureImmichIntegration: () => import("@tanstack/svelte-query").CreateMutationResult<never, Error, ConfigureImmichIntegrationRequestDto, unknown>;
13
+ export declare const useConfigureImmichDefaults: () => import("@tanstack/svelte-query").CreateMutationResult<void, Error, void, unknown>;
@@ -0,0 +1,42 @@
1
+ import { SocketEvent } from '../events';
2
+ import { configureImmichIntegration, getIntegrations, } from '../fetch-client';
3
+ import { queryClient } from '../query-client';
4
+ import { handleError } from '../utils/handle-error';
5
+ import { createMutation, createQuery } from '@tanstack/svelte-query';
6
+ export const integrationsKeys = {
7
+ all: ['integrations'],
8
+ };
9
+ export const useIntegrations = () => createQuery(() => ({
10
+ queryKey: integrationsKeys.all,
11
+ queryFn: () => getIntegrations(),
12
+ }), () => queryClient);
13
+ export const useIntegrationEventHandler = () => ({
14
+ onIntegrationUpdate(event) {
15
+ queryClient.setQueryData(integrationsKeys.all, event.data.integrations);
16
+ },
17
+ });
18
+ export const useConfigureImmichIntegration = () => createMutation(() => ({
19
+ mutationFn: (dto) => configureImmichIntegration(dto),
20
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: integrationsKeys.all }),
21
+ onError: (error) => handleError(error, 'Failed to save backup settings'),
22
+ }), () => queryClient);
23
+ export const useConfigureImmichDefaults = () => createMutation(() => ({
24
+ mutationFn: async () => {
25
+ const { immichState } = await getIntegrations();
26
+ if (!immichState) {
27
+ throw new Error('No Immich instance detected.');
28
+ }
29
+ await configureImmichIntegration({
30
+ name: 'Immich',
31
+ worm: false,
32
+ cron: '0 3 * * *',
33
+ dataFolders: immichState.dataFolders,
34
+ backupConfiguration: true,
35
+ libraries: 'all',
36
+ });
37
+ },
38
+ onSuccess: () => {
39
+ void queryClient.invalidateQueries({ queryKey: integrationsKeys.all });
40
+ },
41
+ onError: (error) => handleError(error, 'Failed to create backup'),
42
+ }), () => queryClient);
@@ -0,0 +1,53 @@
1
+ type SummaryEvent = {
2
+ message_type: 'summary';
3
+ files_new?: number;
4
+ files_changed?: number;
5
+ files_unmodified?: number;
6
+ data_added?: number;
7
+ total_files_processed?: number;
8
+ total_bytes_processed?: number;
9
+ total_duration?: number;
10
+ snapshot_id?: string;
11
+ files_restored?: number;
12
+ files_skipped?: number;
13
+ files_deleted?: number;
14
+ total_files?: number;
15
+ bytes_restored?: number;
16
+ bytes_skipped?: number;
17
+ total_bytes?: number;
18
+ seconds_elapsed?: number;
19
+ };
20
+ type LogEvent = SummaryEvent | {
21
+ message_type: 'error';
22
+ error?: string | {
23
+ message: string;
24
+ };
25
+ message?: string;
26
+ during?: string;
27
+ item?: string;
28
+ } | {
29
+ message_type: 'exit_error';
30
+ code: number;
31
+ message: string;
32
+ } | {
33
+ message_type: 'raw';
34
+ message: string;
35
+ } | {
36
+ message_type: 'status';
37
+ percent_done: number;
38
+ seconds_remaining?: number;
39
+ current_files?: string[];
40
+ };
41
+ export type LogStatus = {
42
+ progress: number;
43
+ text: string;
44
+ currentFiles: string[];
45
+ };
46
+ export declare function createLogObserver(logId: string): {
47
+ readonly status: LogStatus;
48
+ readonly errors: string[];
49
+ readonly events: LogEvent[];
50
+ readonly summary: SummaryEvent | undefined;
51
+ destroy(): void;
52
+ };
53
+ export {};
@@ -0,0 +1,93 @@
1
+ import { defaults } from '../fetch-client';
2
+ import debounce from 'lodash.debounce';
3
+ const formatErrorEvent = (event) => {
4
+ const text = (typeof event.error === 'string' ? event.error : event.error?.message) ??
5
+ event.message ??
6
+ 'Unknown error';
7
+ const context = [event.during, event.item].filter(Boolean).join(' ');
8
+ return context ? `${context}: ${text}` : text;
9
+ };
10
+ export function createLogObserver(logId) {
11
+ const state = $state({
12
+ status: {
13
+ progress: 0,
14
+ text: '',
15
+ currentFiles: [],
16
+ },
17
+ errors: [],
18
+ events: [],
19
+ summary: undefined,
20
+ });
21
+ const buffer = [];
22
+ const flush = debounce(() => {
23
+ state.status = { ...state.status };
24
+ state.errors = [...state.errors];
25
+ state.events = buffer.slice();
26
+ }, 50, { maxWait: 100 });
27
+ const onEvent = (event) => {
28
+ if (event.message_type !== 'status') {
29
+ buffer.unshift(event);
30
+ buffer.splice(50);
31
+ }
32
+ switch (event.message_type) {
33
+ case 'status': {
34
+ state.status = {
35
+ progress: event.percent_done,
36
+ text: event.seconds_remaining
37
+ ? `${event.seconds_remaining} seconds remaining`
38
+ : '',
39
+ currentFiles: event.current_files ?? [],
40
+ };
41
+ flush();
42
+ break;
43
+ }
44
+ case 'summary': {
45
+ state.summary = event;
46
+ state.status = {
47
+ progress: 1,
48
+ text: '',
49
+ currentFiles: [],
50
+ };
51
+ flush();
52
+ flush.flush();
53
+ break;
54
+ }
55
+ case 'error': {
56
+ state.errors.push(formatErrorEvent(event));
57
+ flush();
58
+ flush.flush();
59
+ break;
60
+ }
61
+ case 'exit_error': {
62
+ state.errors.push(`restic exited with code ${event.code}: ${event.message}`);
63
+ flush();
64
+ flush.flush();
65
+ break;
66
+ }
67
+ default: {
68
+ flush();
69
+ break;
70
+ }
71
+ }
72
+ };
73
+ const source = new EventSource(new URL(`/api/yucca/logs/${logId}/stream`, defaults.baseUrl));
74
+ source.addEventListener('message', ({ data }) => onEvent(JSON.parse(data)));
75
+ return {
76
+ get status() {
77
+ return state.status;
78
+ },
79
+ get errors() {
80
+ return state.errors;
81
+ },
82
+ get events() {
83
+ return state.events;
84
+ },
85
+ get summary() {
86
+ return state.summary;
87
+ },
88
+ destroy() {
89
+ flush.cancel();
90
+ source.close();
91
+ },
92
+ };
93
+ }
@@ -0,0 +1,4 @@
1
+ export declare const metricsHistoryKeys: {
2
+ byRepository: (repositoryId: string) => readonly ["metricsHistory", string];
3
+ };
4
+ export declare const useMetricsHistory: (repositoryId: string) => import("@tanstack/svelte-query").CreateInfiniteQueryResult<import("@tanstack/svelte-query").InfiniteData<import("@futo-org/backups-api-client").RepositoryMetricsHistoryListResponseDto, unknown>, Error>;
@@ -0,0 +1,12 @@
1
+ import { listMetricsHistory } from '@futo-org/backups-api-client';
2
+ import { createInfiniteQuery } from '@tanstack/svelte-query';
3
+ import { queryClient } from '../query-client';
4
+ export const metricsHistoryKeys = {
5
+ byRepository: (repositoryId) => ['metricsHistory', repositoryId],
6
+ };
7
+ export const useMetricsHistory = (repositoryId) => createInfiniteQuery(() => ({
8
+ queryKey: metricsHistoryKeys.byRepository(repositoryId),
9
+ queryFn: ({ pageParam }) => listMetricsHistory(repositoryId, { cursor: pageParam }),
10
+ initialPageParam: undefined,
11
+ getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
12
+ }), () => queryClient);
@@ -0,0 +1,11 @@
1
+ import { sdk } from '..';
2
+ import type { ImportRecoveryKeyRequest } from '../fetch-client';
3
+ export declare const recoveryKeyKeys: {
4
+ all: readonly ["recovery-key"];
5
+ };
6
+ export declare const useRecoveryKey: () => import("@tanstack/svelte-query").CreateQueryResult<sdk.CurrentRecoveryKeyResponse, Error>;
7
+ export declare const handleOnboardingStatus: () => Promise<sdk.OnboardingStatusResponseDto>;
8
+ export declare const handleCurrentRecoveryKey: () => Promise<sdk.CurrentRecoveryKeyResponse>;
9
+ export declare const handleConfirmRecoveryKey: () => Promise<void>;
10
+ export declare const handleImportRecoveryKey: (dto: ImportRecoveryKeyRequest) => Promise<void>;
11
+ export declare const handleSkipOnboardingExtraConfig: () => Promise<void>;
@@ -0,0 +1,56 @@
1
+ import { sdk } from '..';
2
+ import { queryClient } from '../query-client';
3
+ import { handleError } from '../utils/handle-error';
4
+ import { createQuery } from '@tanstack/svelte-query';
5
+ export const recoveryKeyKeys = {
6
+ all: ['recovery-key'],
7
+ };
8
+ export const useRecoveryKey = () => createQuery(() => ({
9
+ queryKey: recoveryKeyKeys.all,
10
+ queryFn: () => sdk.currentRecoveryKey(),
11
+ }), () => queryClient);
12
+ export const handleOnboardingStatus = async () => {
13
+ try {
14
+ return await sdk.onboardingStatus();
15
+ }
16
+ catch (error) {
17
+ handleError(error, 'Failed to load onboarding status');
18
+ throw error;
19
+ }
20
+ };
21
+ export const handleCurrentRecoveryKey = async () => {
22
+ try {
23
+ return await sdk.currentRecoveryKey();
24
+ }
25
+ catch (error) {
26
+ handleError(error, 'Failed to load recovery key');
27
+ throw error;
28
+ }
29
+ };
30
+ export const handleConfirmRecoveryKey = async () => {
31
+ try {
32
+ await sdk.confirmRecoveryKey();
33
+ }
34
+ catch (error) {
35
+ handleError(error, 'Failed to confirm recovery key');
36
+ throw error;
37
+ }
38
+ };
39
+ export const handleImportRecoveryKey = async (dto) => {
40
+ try {
41
+ await sdk.importRecoveryKey(dto);
42
+ }
43
+ catch (error) {
44
+ handleError(error, 'Failed to import recovery key');
45
+ throw error;
46
+ }
47
+ };
48
+ export const handleSkipOnboardingExtraConfig = async () => {
49
+ try {
50
+ await sdk.skipOnboardingExtraConfig();
51
+ }
52
+ catch (error) {
53
+ handleError(error, 'Failed to save preferences');
54
+ throw error;
55
+ }
56
+ };
@@ -0,0 +1,45 @@
1
+ import { sdk } from '..';
2
+ import { SocketEvent } from '../events';
3
+ import { type InspectedLocalRepositoryDto, type LocalRepositoryDto, type RepositoryUpdateRequestDto } from '../fetch-client';
4
+ import { type ActionItem } from '@immich/ui';
5
+ export declare const repositoryKeys: {
6
+ all: readonly ["repositories"];
7
+ inspectAll: readonly ["repositories", "inspect"];
8
+ };
9
+ export declare const useRepositories: (initialData?: LocalRepositoryDto[]) => import("@tanstack/svelte-query").CreateQueryResult<sdk.LocalRepositoryDto[], Error>;
10
+ export declare const useInspectRepositories: (initialData?: InspectedLocalRepositoryDto[]) => import("@tanstack/svelte-query").CreateQueryResult<sdk.InspectedLocalRepositoryDto[], Error>;
11
+ export declare const useRepositoryEventHandler: () => {
12
+ onRepositoryCreate(event: SocketEvent<{
13
+ repository: LocalRepositoryDto;
14
+ }>): void;
15
+ onRepositoryUpdate(event: SocketEvent<{
16
+ repositoryId: string;
17
+ repository: Partial<LocalRepositoryDto>;
18
+ }>): void;
19
+ onRepositoryDelete(): void;
20
+ };
21
+ export declare const handleCheckImportRepository: (id: string, backendId: string) => Promise<sdk.RepositoryCheckImportResponseDto>;
22
+ export declare const useCreateRepository: () => import("@tanstack/svelte-query").CreateMutationResult<sdk.RepositoryCreateResponseDto, Error, sdk.RepositoryCreateRequestDto, unknown>;
23
+ export declare const useImportRepository: () => import("@tanstack/svelte-query").CreateMutationResult<sdk.RepositoryCreateResponseDto, Error, {
24
+ id: string;
25
+ backendId: string;
26
+ }, unknown>;
27
+ export declare const useUpdateRepository: () => import("@tanstack/svelte-query").CreateMutationResult<sdk.RepositoryUpdateResponseDto, Error, {
28
+ id: string;
29
+ dto: RepositoryUpdateRequestDto;
30
+ local?: boolean;
31
+ }, unknown>;
32
+ export declare const useRemoveRepository: () => import("@tanstack/svelte-query").CreateMutationResult<never, Error, {
33
+ id: string;
34
+ local?: boolean;
35
+ }, unknown>;
36
+ export declare const handleCreateBackup: (id: string) => Promise<sdk.LogResponseDto>;
37
+ export declare const handlePruneRepository: (id: string) => Promise<sdk.LogResponseDto>;
38
+ export declare const getRepositoryActions: (repository: LocalRepositoryDto) => {
39
+ BackupNow: ActionItem;
40
+ Snapshots: ActionItem;
41
+ History: ActionItem;
42
+ Configure: ActionItem;
43
+ Import: ActionItem;
44
+ MetricsHistory: ActionItem;
45
+ };
@@ -0,0 +1,157 @@
1
+ import { sdk } from '..';
2
+ import ConfigureRepositoryModal from '../components/backups/dialogs/ConfigureRepositoryModal.svelte';
3
+ import ImportRepositoryModal from '../components/backups/dialogs/ImportRepositoryModal.svelte';
4
+ import ViewLogModal from '../components/backups/dialogs/ViewLogModal.svelte';
5
+ import MetricsHistoryModal from '../components/backups/metrics-history/MetricsHistoryModal.svelte';
6
+ import RunHistoryModal from '../components/backups/run-history/RunHistoryModal.svelte';
7
+ import SnapshotsListModal from '../components/backups/snapshots-list/SnapshotsListModal.svelte';
8
+ import { SocketEvent } from '../events';
9
+ import { deleteRepository, inspectRepositories, updateRepository, } from '../fetch-client';
10
+ import { getProvider } from '../providers';
11
+ import { queryClient } from '../query-client';
12
+ import { handleError } from '../utils/handle-error';
13
+ import { modalManager, toastManager } from '@immich/ui';
14
+ import { mdiCog, mdiFormatListBulletedType, mdiHistory, mdiImport, mdiListStatus, mdiPlay, } from '@mdi/js';
15
+ import { createMutation, createQuery } from '@tanstack/svelte-query';
16
+ export const repositoryKeys = {
17
+ all: ['repositories'],
18
+ inspectAll: ['repositories', 'inspect'],
19
+ };
20
+ export const useRepositories = (initialData) => createQuery(() => ({
21
+ queryKey: repositoryKeys.all,
22
+ queryFn: () => getProvider()
23
+ .getRepositories()
24
+ .then(({ repositories }) => repositories),
25
+ initialData,
26
+ }), () => queryClient);
27
+ export const useInspectRepositories = (initialData) => createQuery(() => ({
28
+ queryKey: repositoryKeys.inspectAll,
29
+ queryFn: () => inspectRepositories().then(({ repositories }) => repositories),
30
+ initialData,
31
+ }), () => queryClient);
32
+ export const useRepositoryEventHandler = () => {
33
+ return {
34
+ onRepositoryCreate(event) {
35
+ queryClient.setQueryData(repositoryKeys.all, (data) => {
36
+ return data
37
+ ? [
38
+ ...data.filter((entry) => entry.id !== event.data.repository.id),
39
+ event.data.repository,
40
+ ]
41
+ : void 0;
42
+ });
43
+ },
44
+ onRepositoryUpdate(event) {
45
+ queryClient.setQueryData(repositoryKeys.all, (data) => {
46
+ return data
47
+ ? data.map((entry) => entry.id === event.data.repositoryId
48
+ ? { ...entry, ...event.data.repository }
49
+ : entry)
50
+ : void 0;
51
+ });
52
+ },
53
+ onRepositoryDelete() {
54
+ queryClient
55
+ .invalidateQueries({
56
+ queryKey: repositoryKeys.all,
57
+ })
58
+ .catch(() => void 0);
59
+ },
60
+ };
61
+ };
62
+ export const handleCheckImportRepository = async (id, backendId) => {
63
+ try {
64
+ return await sdk.checkImportRepository(id, backendId);
65
+ }
66
+ catch (error) {
67
+ handleError(error, 'Failed to check repository');
68
+ throw error;
69
+ }
70
+ };
71
+ export const useCreateRepository = () => createMutation(() => ({
72
+ mutationFn: (dto) => sdk.createRepository(dto),
73
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: repositoryKeys.all }),
74
+ onError: (error) => handleError(error, 'Failed to create repository'),
75
+ }), () => queryClient);
76
+ export const useImportRepository = () => createMutation(() => ({
77
+ mutationFn: ({ id, backendId }) => sdk.importRepository(id, backendId),
78
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: repositoryKeys.all }),
79
+ onError: (error) => handleError(error, 'Failed to import repository'),
80
+ }), () => queryClient);
81
+ export const useUpdateRepository = () => createMutation(() => ({
82
+ mutationFn: ({ id, dto, local = false, }) => (local ? sdk.updateRepository(id, dto) : updateRepository(id, dto)),
83
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: repositoryKeys.all }),
84
+ onError: (error) => handleError(error, 'Failed to update repository'),
85
+ }), () => queryClient);
86
+ export const useRemoveRepository = () => createMutation(() => ({
87
+ mutationFn: ({ id, local = false }) => local ? sdk.deleteRepository(id) : deleteRepository(id),
88
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: repositoryKeys.all }),
89
+ onError: (error) => handleError(error, 'Failed to delete repository'),
90
+ }), () => queryClient);
91
+ export const handleCreateBackup = async (id) => {
92
+ try {
93
+ toastManager.info('Started backup');
94
+ const response = await sdk.createBackup(id);
95
+ void modalManager.open(ViewLogModal, { logId: response.logId });
96
+ return response;
97
+ }
98
+ catch (error) {
99
+ handleError(error, 'Failed to start backup');
100
+ throw error;
101
+ }
102
+ };
103
+ export const handlePruneRepository = async (id) => {
104
+ try {
105
+ toastManager.info('Cleaning up old backups');
106
+ const response = await sdk.pruneRepository(id);
107
+ void modalManager.open(ViewLogModal, { logId: response.logId });
108
+ return response;
109
+ }
110
+ catch (error) {
111
+ handleError(error, 'Failed to start cleanup');
112
+ throw error;
113
+ }
114
+ };
115
+ export const getRepositoryActions = (repository) => {
116
+ const online = Boolean(repository.configuration && repository.backends?.primary.online);
117
+ const configured = Boolean(repository.configuration);
118
+ const BackupNow = {
119
+ title: 'Back up now',
120
+ icon: mdiPlay,
121
+ onAction: () => void handleCreateBackup(repository.id),
122
+ $if: () => online,
123
+ };
124
+ const Snapshots = {
125
+ title: 'Snapshots',
126
+ icon: mdiFormatListBulletedType,
127
+ onAction: () => void modalManager.open(SnapshotsListModal, { repository }),
128
+ $if: () => online,
129
+ };
130
+ const History = {
131
+ title: 'Logs',
132
+ icon: mdiListStatus,
133
+ onAction: () => void modalManager.open(RunHistoryModal, { repository }),
134
+ $if: () => configured,
135
+ };
136
+ const Configure = {
137
+ title: 'Configure',
138
+ icon: mdiCog,
139
+ onAction: () => void modalManager.open(ConfigureRepositoryModal, {
140
+ repository: { ...repository, configuration: repository.configuration },
141
+ }),
142
+ $if: () => configured,
143
+ };
144
+ const Import = {
145
+ title: 'Import',
146
+ icon: mdiImport,
147
+ onAction: () => void modalManager.open(ImportRepositoryModal, { repository }),
148
+ $if: () => Boolean(repository.backends && !repository.configuration),
149
+ };
150
+ const MetricsHistory = {
151
+ title: 'Metrics history',
152
+ icon: mdiHistory,
153
+ onAction: () => void modalManager.open(MetricsHistoryModal, { repository }),
154
+ $if: () => !repository.backends,
155
+ };
156
+ return { BackupNow, Snapshots, History, Configure, Import, MetricsHistory };
157
+ };