@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,26 @@
1
+ import { sdk } from '..';
2
+ import { SocketEvent } from '../events';
3
+ import { type RunDto } from '../fetch-client';
4
+ import { type ActionItem } from '@immich/ui';
5
+ export declare const runHistoryKeys: {
6
+ byRepository: (id: string) => readonly ["runHistory", string];
7
+ };
8
+ export declare const runKeys: {
9
+ byId: (id: string) => readonly ["run", string];
10
+ };
11
+ export declare const useRunHistory: (repositoryId: string) => import("@tanstack/svelte-query").CreateQueryResult<sdk.RunDto[], Error>;
12
+ export declare const useRun: (logId: string) => import("@tanstack/svelte-query").CreateQueryResult<sdk.RunDto, Error>;
13
+ export declare const useRunEventHandler: () => {
14
+ onRunCreate(event: SocketEvent<{
15
+ run: RunDto;
16
+ }>): void;
17
+ onRunUpdate(event: SocketEvent<{
18
+ runId: string;
19
+ repositoryId: string;
20
+ run: Partial<RunDto>;
21
+ }>): void;
22
+ };
23
+ export declare const handleGetRunHistory: (id: string) => Promise<sdk.RunHistoryResponseDto>;
24
+ export declare const getRunActions: (run: RunDto) => {
25
+ ViewLog: ActionItem;
26
+ };
@@ -0,0 +1,54 @@
1
+ import { sdk } from '..';
2
+ import ViewLogModal from '../components/backups/dialogs/ViewLogModal.svelte';
3
+ import { SocketEvent } from '../events';
4
+ import { getRun, getRunHistory } from '../fetch-client';
5
+ import { queryClient } from '../query-client';
6
+ import { handleError } from '../utils/handle-error';
7
+ import { modalManager } from '@immich/ui';
8
+ import { createQuery } from '@tanstack/svelte-query';
9
+ export const runHistoryKeys = {
10
+ byRepository: (id) => ['runHistory', id],
11
+ };
12
+ export const runKeys = {
13
+ byId: (id) => ['run', id],
14
+ };
15
+ export const useRunHistory = (repositoryId) => createQuery(() => ({
16
+ queryKey: runHistoryKeys.byRepository(repositoryId),
17
+ queryFn: () => getRunHistory(repositoryId).then(({ runs }) => runs.toSorted((a, b) => b.start.localeCompare(a.start))),
18
+ }), () => queryClient);
19
+ export const useRun = (logId) => createQuery(() => ({
20
+ queryKey: runKeys.byId(logId),
21
+ queryFn: () => getRun(logId).then(({ run }) => run),
22
+ }), () => queryClient);
23
+ export const useRunEventHandler = () => {
24
+ return {
25
+ onRunCreate(event) {
26
+ queryClient.setQueryData(runKeys.byId(event.data.run.id), event.data.run);
27
+ queryClient.setQueryData(runHistoryKeys.byRepository(event.data.run.repositoryId), (data) => data ? [event.data.run, ...data] : void 0);
28
+ },
29
+ onRunUpdate(event) {
30
+ queryClient.setQueryData(runKeys.byId(event.data.runId), (data) => data ? { ...data, ...event.data.run } : void 0);
31
+ queryClient.setQueryData(runHistoryKeys.byRepository(event.data.repositoryId), (data) => data
32
+ ? data.map((entry) => entry.id === event.data.runId
33
+ ? { ...entry, ...event.data.run }
34
+ : entry)
35
+ : void 0);
36
+ },
37
+ };
38
+ };
39
+ export const handleGetRunHistory = async (id) => {
40
+ try {
41
+ return await sdk.getRunHistory(id);
42
+ }
43
+ catch (error) {
44
+ handleError(error, 'Failed to load run history');
45
+ throw error;
46
+ }
47
+ };
48
+ export const getRunActions = (run) => {
49
+ const ViewLog = {
50
+ title: 'View Log',
51
+ onAction: () => void modalManager.open(ViewLogModal, { logId: run.id }),
52
+ };
53
+ return { ViewLog };
54
+ };
@@ -0,0 +1,35 @@
1
+ import { sdk } from '..';
2
+ import { type ScheduleDto, type ScheduleUpdateRequestDto } from '../fetch-client';
3
+ import { SocketEvent } from '../events';
4
+ import { type ActionItem } from '@immich/ui';
5
+ export declare const scheduleKeys: {
6
+ all: readonly ["schedules"];
7
+ };
8
+ export declare const useSchedules: () => import("@tanstack/svelte-query").CreateQueryResult<sdk.ScheduleDto[], Error>;
9
+ export declare const useScheduleEventHandler: () => {
10
+ onScheduleCreate(event: SocketEvent<{
11
+ schedule: ScheduleDto;
12
+ }>): void;
13
+ onScheduleUpdate(event: SocketEvent<{
14
+ scheduleId: string;
15
+ schedule: Partial<ScheduleDto>;
16
+ }>): void;
17
+ onScheduleDelete(event: SocketEvent<{
18
+ scheduleId: string;
19
+ }>): void;
20
+ };
21
+ export declare const handleGetSchedules: () => Promise<sdk.ScheduleListResponseDto>;
22
+ export declare const useCreateSchedule: () => import("@tanstack/svelte-query").CreateMutationResult<sdk.ScheduleCreateResponseDto, Error, sdk.ScheduleCreateRequestDto, unknown>;
23
+ export declare const useUpdateSchedule: () => import("@tanstack/svelte-query").CreateMutationResult<sdk.ScheduleUpdateResponseDto, Error, {
24
+ id: string;
25
+ dto: ScheduleUpdateRequestDto;
26
+ }, unknown>;
27
+ export declare const handlePauseSchedule: (id: string, name: string) => Promise<void>;
28
+ export declare const handleResumeSchedule: (id: string, name: string) => Promise<void>;
29
+ export declare const handleRemoveSchedule: (id: string, name: string) => Promise<void>;
30
+ export declare const getScheduleActions: (schedule: ScheduleDto) => {
31
+ Resume: ActionItem;
32
+ Pause: ActionItem;
33
+ Configure: ActionItem;
34
+ Delete: ActionItem;
35
+ };
@@ -0,0 +1,126 @@
1
+ import { sdk } from '..';
2
+ import ConfigureScheduleModal from '../components/schedules/dialogs/ConfigureScheduleModal.svelte';
3
+ import { getSchedules, } from '../fetch-client';
4
+ import { SocketEvent } from '../events';
5
+ import { modalManager, toastManager } from '@immich/ui';
6
+ import { mdiCog, mdiDelete, mdiPause, mdiPlay } from '@mdi/js';
7
+ import { handleError } from '../utils/handle-error';
8
+ import { queryClient } from '../query-client';
9
+ import { createMutation, createQuery } from '@tanstack/svelte-query';
10
+ export const scheduleKeys = {
11
+ all: ['schedules'],
12
+ };
13
+ export const useSchedules = () => createQuery(() => ({
14
+ queryKey: scheduleKeys.all,
15
+ queryFn: () => getSchedules().then(({ schedules }) => schedules),
16
+ }), () => queryClient);
17
+ export const useScheduleEventHandler = () => {
18
+ return {
19
+ onScheduleCreate(event) {
20
+ queryClient.setQueryData(scheduleKeys.all, (data) => {
21
+ return data ? [...data, event.data.schedule] : void 0;
22
+ });
23
+ },
24
+ onScheduleUpdate(event) {
25
+ queryClient.setQueryData(scheduleKeys.all, (data) => {
26
+ return data
27
+ ? data.map((entry) => entry.id === event.data.scheduleId
28
+ ? { ...entry, ...event.data.schedule }
29
+ : entry)
30
+ : void 0;
31
+ });
32
+ },
33
+ onScheduleDelete(event) {
34
+ queryClient.setQueryData(scheduleKeys.all, (data) => {
35
+ return data
36
+ ? data.filter((entry) => entry.id !== event.data.scheduleId)
37
+ : void 0;
38
+ });
39
+ },
40
+ };
41
+ };
42
+ export const handleGetSchedules = async () => {
43
+ try {
44
+ return await sdk.getSchedules();
45
+ }
46
+ catch (error) {
47
+ handleError(error, 'Failed to load schedules');
48
+ throw error;
49
+ }
50
+ };
51
+ export const useCreateSchedule = () => createMutation(() => ({
52
+ mutationFn: (dto) => sdk.createSchedule(dto),
53
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: scheduleKeys.all }),
54
+ onError: (error) => handleError(error, 'Failed to create schedule'),
55
+ }), () => queryClient);
56
+ export const useUpdateSchedule = () => createMutation(() => ({
57
+ mutationFn: ({ id, dto, }) => sdk.updateSchedule(id, dto),
58
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: scheduleKeys.all }),
59
+ onError: (error) => handleError(error, 'Failed to update schedule'),
60
+ }), () => queryClient);
61
+ export const handlePauseSchedule = async (id, name) => {
62
+ try {
63
+ await sdk.updateSchedule(id, { paused: true });
64
+ toastManager.info(`Paused schedule "${name}"`);
65
+ }
66
+ catch (error) {
67
+ handleError(error, 'Failed to pause schedule');
68
+ throw error;
69
+ }
70
+ };
71
+ export const handleResumeSchedule = async (id, name) => {
72
+ try {
73
+ await sdk.updateSchedule(id, { paused: false });
74
+ toastManager.success(`Resumed schedule "${name}"`);
75
+ }
76
+ catch (error) {
77
+ handleError(error, 'Failed to resume schedule');
78
+ throw error;
79
+ }
80
+ };
81
+ export const handleRemoveSchedule = async (id, name) => {
82
+ try {
83
+ await sdk.removeSchedule(id);
84
+ toastManager.info(`Deleted schedule "${name}"`);
85
+ }
86
+ catch (error) {
87
+ handleError(error, 'Failed to delete schedule');
88
+ throw error;
89
+ }
90
+ };
91
+ export const getScheduleActions = (schedule) => {
92
+ const Resume = {
93
+ title: 'Resume',
94
+ icon: mdiPlay,
95
+ onAction: () => void handleResumeSchedule(schedule.id, schedule.name),
96
+ $if: () => schedule.paused,
97
+ };
98
+ const Pause = {
99
+ title: 'Pause',
100
+ icon: mdiPause,
101
+ onAction: () => void handlePauseSchedule(schedule.id, schedule.name),
102
+ $if: () => !schedule.paused,
103
+ };
104
+ const Configure = {
105
+ title: 'Configure',
106
+ icon: mdiCog,
107
+ onAction: () => void modalManager.open(ConfigureScheduleModal, { schedule }),
108
+ };
109
+ const Delete = {
110
+ title: 'Delete',
111
+ icon: mdiDelete,
112
+ color: 'danger',
113
+ onAction: async () => {
114
+ const confirm = await modalManager.showDialog({
115
+ confirmText: 'Delete',
116
+ title: 'Delete Schedule',
117
+ prompt: 'This schedule will be removed.',
118
+ });
119
+ if (!confirm) {
120
+ return;
121
+ }
122
+ await handleRemoveSchedule(schedule.id, schedule.name);
123
+ },
124
+ };
125
+ return { Resume, Pause, Configure, Delete };
126
+ };
@@ -0,0 +1,29 @@
1
+ import { sdk } from '..';
2
+ import { SocketEvent } from '../events';
3
+ import { type RepositorySnapshotRestoreFromPointRequestDto, type RepositorySnapshotRestoreRequestDto, type RunDto, type SnapshotDto } from '../fetch-client';
4
+ import { type ActionItem } from '@immich/ui';
5
+ export declare const snapshotKeys: {
6
+ byRepository: (id: string) => readonly ["snapshots", string];
7
+ };
8
+ export declare const useSnapshots: (repositoryId: string) => import("@tanstack/svelte-query").CreateQueryResult<sdk.SnapshotDto[], Error>;
9
+ export declare const useSnapshotEventHandler: () => {
10
+ onRunUpdate(event: SocketEvent<{
11
+ runId: string;
12
+ repositoryId: string;
13
+ run: Partial<RunDto>;
14
+ }>): void;
15
+ };
16
+ export declare const useRemoveSnapshot: (repositoryId: string) => (snapshotId: string) => void;
17
+ export declare const handleGetSnapshots: (repositoryId: string) => Promise<sdk.ListSnapshotsResponseDto>;
18
+ export declare const useRestoreSnapshot: () => import("@tanstack/svelte-query").CreateMutationResult<sdk.LogResponseDto, Error, {
19
+ repositoryId: string;
20
+ snapshotId: string;
21
+ options: RepositorySnapshotRestoreRequestDto;
22
+ }, unknown>;
23
+ export declare const handleRestoreFromPoint: (repositoryId: string, snapshotId: string, backendId: string, options: RepositorySnapshotRestoreFromPointRequestDto) => Promise<sdk.LogResponseDto>;
24
+ export declare const handleForgetSnapshot: (repositoryId: string, snapshotId: string) => Promise<void>;
25
+ export declare const handleGetSnapshotListing: (id: string, snapshotId: string, path?: string) => Promise<sdk.FilesystemListingResponseDto>;
26
+ export declare const getSnapshotActions: (repositoryId: string, snapshot: SnapshotDto) => {
27
+ Restore: ActionItem;
28
+ Delete: ActionItem;
29
+ };
@@ -0,0 +1,108 @@
1
+ import { sdk } from '..';
2
+ import RestoreSnapshotModal from '../components/backups/dialogs/RestoreSnapshotModal.svelte';
3
+ import { SocketEvent } from '../events';
4
+ import { getSnapshots, } from '../fetch-client';
5
+ import { queryClient } from '../query-client';
6
+ import { handleError } from '../utils/handle-error';
7
+ import { modalManager, toastManager } from '@immich/ui';
8
+ import { mdiBackupRestore, mdiDeleteOutline } from '@mdi/js';
9
+ import { createMutation, createQuery } from '@tanstack/svelte-query';
10
+ export const snapshotKeys = {
11
+ byRepository: (id) => ['snapshots', id],
12
+ };
13
+ export const useSnapshots = (repositoryId) => createQuery(() => ({
14
+ queryKey: snapshotKeys.byRepository(repositoryId),
15
+ queryFn: () => getSnapshots(repositoryId).then(({ snapshots }) => snapshots.toSorted((a, b) => b.time.localeCompare(a.time))),
16
+ }), () => queryClient);
17
+ export const useSnapshotEventHandler = () => {
18
+ return {
19
+ onRunUpdate(event) {
20
+ if (event.data.run.status === 'complete') {
21
+ void queryClient.invalidateQueries({
22
+ queryKey: snapshotKeys.byRepository(event.data.repositoryId),
23
+ });
24
+ }
25
+ },
26
+ };
27
+ };
28
+ export const useRemoveSnapshot = (repositoryId) => {
29
+ return (snapshotId) => {
30
+ queryClient.setQueryData(snapshotKeys.byRepository(repositoryId), (data) => {
31
+ return data ? data.filter((entry) => entry.id !== snapshotId) : void 0;
32
+ });
33
+ };
34
+ };
35
+ export const handleGetSnapshots = async (repositoryId) => {
36
+ try {
37
+ return await sdk.getSnapshots(repositoryId);
38
+ }
39
+ catch (error) {
40
+ handleError(error, 'Failed to load snapshots');
41
+ throw error;
42
+ }
43
+ };
44
+ export const useRestoreSnapshot = () => createMutation(() => ({
45
+ mutationFn: ({ repositoryId, snapshotId, options, }) => sdk.restoreSnapshot(repositoryId, snapshotId, options),
46
+ onError: (error) => handleError(error, 'Failed to start restore'),
47
+ }), () => queryClient);
48
+ export const handleRestoreFromPoint = async (repositoryId, snapshotId, backendId, options) => {
49
+ return await sdk.restoreFromPoint(repositoryId, snapshotId, backendId, options);
50
+ };
51
+ export const handleForgetSnapshot = async (repositoryId, snapshotId) => {
52
+ toastManager.info('Deleting snapshot', {
53
+ id: snapshotId,
54
+ closable: false,
55
+ timeout: null,
56
+ });
57
+ try {
58
+ await sdk.forgetSnapshot(repositoryId, snapshotId);
59
+ toastManager.success('Deleted snapshot');
60
+ }
61
+ catch (error) {
62
+ handleError(error, 'Failed to delete snapshot');
63
+ throw error;
64
+ }
65
+ finally {
66
+ toastManager.remove({
67
+ id: snapshotId,
68
+ });
69
+ }
70
+ };
71
+ export const handleGetSnapshotListing = async (id, snapshotId, path) => {
72
+ try {
73
+ return await sdk.getSnapshotListing(id, snapshotId, { path });
74
+ }
75
+ catch (error) {
76
+ handleError(error, 'Failed to load directory listing');
77
+ throw error;
78
+ }
79
+ };
80
+ export const getSnapshotActions = (repositoryId, snapshot) => {
81
+ const removeSnapshot = useRemoveSnapshot(repositoryId);
82
+ const Restore = {
83
+ title: 'Restore',
84
+ icon: mdiBackupRestore,
85
+ onAction: () => void modalManager.open(RestoreSnapshotModal, {
86
+ repository: repositoryId,
87
+ snapshot: snapshot.id,
88
+ }),
89
+ };
90
+ const Delete = {
91
+ title: 'Delete',
92
+ icon: mdiDeleteOutline,
93
+ color: 'danger',
94
+ onAction: async () => {
95
+ const confirm = await modalManager.showDialog({
96
+ confirmText: 'Delete',
97
+ title: 'Delete Snapshot',
98
+ prompt: 'This snapshot will be permanently removed.',
99
+ });
100
+ if (!confirm) {
101
+ return;
102
+ }
103
+ await handleForgetSnapshot(repositoryId, snapshot.id);
104
+ removeSnapshot(snapshot.id);
105
+ },
106
+ };
107
+ return { Restore, Delete };
108
+ };
@@ -0,0 +1,3 @@
1
+ import { sdk } from '..';
2
+ export declare const handleGetRunningTasks: () => Promise<sdk.RunningTaskListResponse>;
3
+ export declare const handleCancelTask: (parentId: string) => Promise<void>;
@@ -0,0 +1,20 @@
1
+ import { sdk } from '..';
2
+ import { handleError } from '../utils/handle-error';
3
+ export const handleGetRunningTasks = async () => {
4
+ try {
5
+ return await sdk.getRunningTasks();
6
+ }
7
+ catch (error) {
8
+ handleError(error, 'Failed to load running tasks');
9
+ throw error;
10
+ }
11
+ };
12
+ export const handleCancelTask = async (parentId) => {
13
+ try {
14
+ await sdk.cancelTask(parentId);
15
+ }
16
+ catch (error) {
17
+ handleError(error, 'Failed to cancel task');
18
+ throw error;
19
+ }
20
+ };
@@ -0,0 +1,2 @@
1
+ import type { ActionItem } from '@immich/ui';
2
+ export declare function hasActions(actions: ActionItem[]): boolean;
@@ -0,0 +1,3 @@
1
+ export function hasActions(actions) {
2
+ return actions.some((action) => !action.$if || action.$if?.());
3
+ }
@@ -0,0 +1,2 @@
1
+ export declare const formatDuration: (ms: number) => string;
2
+ export declare const humanizeBackupPath: (path: string) => string;
@@ -0,0 +1,24 @@
1
+ import { Duration } from 'luxon';
2
+ export const formatDuration = (ms) => {
3
+ const seconds = Math.round(ms / 1000);
4
+ return seconds < 1
5
+ ? '<1s'
6
+ : Duration.fromMillis(seconds * 1000)
7
+ .rescale()
8
+ .toHuman({ unitDisplay: 'narrow' });
9
+ };
10
+ const IMMICH_FOLDER_LABELS = {
11
+ upload: 'Photos and videos',
12
+ profile: 'Photos and videos',
13
+ library: 'Photos and videos',
14
+ backups: 'Database backups',
15
+ thumbs: 'Thumbnails and previews',
16
+ 'encoded-video': 'Encoded videos',
17
+ };
18
+ export const humanizeBackupPath = (path) => {
19
+ if (path.includes('yucca')) {
20
+ return 'Backup configuration';
21
+ }
22
+ const basename = path.replace(/\/+$/, '').split('/').pop() ?? path;
23
+ return IMMICH_FOLDER_LABELS[basename] ?? basename;
24
+ };
@@ -0,0 +1,9 @@
1
+ export declare function getServerErrorMessage(error: {
2
+ data?: {
3
+ message?: string;
4
+ };
5
+ message?: string;
6
+ }): string | undefined;
7
+ export declare function standardizeError(error: unknown): Error;
8
+ export declare function getReadableErrorMessage(error: unknown): string;
9
+ export declare function handleError(error: unknown, localizedMessage: string): string | undefined;
@@ -0,0 +1,42 @@
1
+ import { toastManager } from '@immich/ui';
2
+ export function getServerErrorMessage(error) {
3
+ // errors for endpoints without return types aren't parsed as json
4
+ let data = error.data;
5
+ if (typeof data === 'string') {
6
+ try {
7
+ data = JSON.parse(data);
8
+ }
9
+ catch {
10
+ // Not a JSON string
11
+ }
12
+ }
13
+ return data?.message || error.message;
14
+ }
15
+ export function standardizeError(error) {
16
+ return error instanceof Error ? error : new Error(String(error));
17
+ }
18
+ export function getReadableErrorMessage(error) {
19
+ return (getServerErrorMessage(error) ||
20
+ standardizeError(error).message ||
21
+ 'An unknown error occurred');
22
+ }
23
+ export function handleError(error, localizedMessage) {
24
+ const standardizedError = standardizeError(error);
25
+ if (standardizedError.name === 'AbortError') {
26
+ return;
27
+ }
28
+ console.error(`[handleError]: ${standardizedError}`, error, standardizedError.stack);
29
+ try {
30
+ let serverMessage = getServerErrorMessage(error);
31
+ if (serverMessage) {
32
+ serverMessage = `${String(serverMessage).slice(0, 75)}\n(Yucca Server Error)`;
33
+ }
34
+ const errorMessage = serverMessage || localizedMessage;
35
+ toastManager.danger(errorMessage);
36
+ return errorMessage;
37
+ }
38
+ catch (error) {
39
+ console.error(error);
40
+ return localizedMessage;
41
+ }
42
+ }
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@futo-org/backups-orchestrator-ui",
3
+ "version": "0.1.71",
4
+ "license": "Source First License 1.1",
5
+ "files": [
6
+ "dist",
7
+ "!dist/**/*.test.*",
8
+ "!dist/**/*.spec.*"
9
+ ],
10
+ "sideEffects": [
11
+ "**/*.css"
12
+ ],
13
+ "svelte": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "svelte": "./dist/index.js"
20
+ },
21
+ "./sdk": {
22
+ "types": "./dist/fetch-client.d.ts",
23
+ "default": "./dist/fetch-client.js"
24
+ }
25
+ },
26
+ "peerDependencies": {
27
+ "svelte": "^5.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "@playwright/test": "^1.57.0",
31
+ "@sveltejs/adapter-auto": "^7.0.0",
32
+ "@sveltejs/kit": "^2.50.1",
33
+ "@sveltejs/package": "^2.5.7",
34
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
35
+ "@tailwindcss/vite": "^4.1.18",
36
+ "@types/d3": "^7.4.3",
37
+ "@types/lodash.debounce": "^4.0.9",
38
+ "@types/luxon": "^3.7.1",
39
+ "@vitest/browser-playwright": "^4.0.18",
40
+ "oazapfts": "^7.0.1",
41
+ "playwright": "^1.57.0",
42
+ "prettier-plugin-tailwindcss": "^0.7.2",
43
+ "publint": "^0.3.17",
44
+ "svelte": "^5.48.2",
45
+ "svelte-check": "^4.3.5",
46
+ "tailwindcss": "^4.1.18",
47
+ "typescript": "^5.9.3",
48
+ "vite": "^7.3.1",
49
+ "vitest": "^4.0.18",
50
+ "vitest-browser-svelte": "^2.0.2"
51
+ },
52
+ "keywords": [
53
+ "svelte"
54
+ ],
55
+ "dependencies": {
56
+ "@immich/ui": "^0.59.0",
57
+ "@mdi/js": "^7.4.47",
58
+ "@oazapfts/runtime": "^1.1.0",
59
+ "@tanstack/svelte-query": "^6.1.3",
60
+ "cron-validate": "^1.5.3",
61
+ "cronstrue": "^3.14.0",
62
+ "lodash.debounce": "^4.0.8",
63
+ "luxon": "^3.7.2",
64
+ "socket.io-client": "^4.8.3",
65
+ "@futo-org/backups-api-client": "^0.1.71"
66
+ },
67
+ "scripts": {
68
+ "dev": "vite dev --port 5174",
69
+ "build": "vite build && npm run prepack",
70
+ "preview": "vite preview --port 4174",
71
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
72
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
73
+ "test:unit": "vitest --run",
74
+ "test": "npm run test:unit && npm run test:e2e",
75
+ "test:e2e": "playwright test",
76
+ "test:e2e:create": "playwright codegen http://localhost:36066",
77
+ "generate:fetch-client": "oazapfts --optimistic ../orchestration-api/openapi-specs.json src/lib/fetch-client.ts"
78
+ }
79
+ }