@plutonhq/core-frontend 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/@types/devices.d.ts +4 -4
- package/dist-lib/@types/devices.d.ts.map +1 -1
- package/dist-lib/@types/plans.d.ts +1 -0
- package/dist-lib/@types/plans.d.ts.map +1 -1
- package/dist-lib/components/App/Footer/Footer.d.ts +2 -1
- package/dist-lib/components/App/Footer/Footer.d.ts.map +1 -1
- package/dist-lib/components/App/Footer/Footer.js +10 -8
- package/dist-lib/components/App/Footer/Footer.js.map +1 -1
- package/dist-lib/components/Device/EditDevice/EditDevice.d.ts.map +1 -1
- package/dist-lib/components/Device/EditDevice/EditDevice.js +70 -61
- package/dist-lib/components/Device/EditDevice/EditDevice.js.map +1 -1
- package/dist-lib/components/Plan/BackupEvents/BackupEvents.js +33 -33
- package/dist-lib/components/Plan/BackupEvents/BackupEvents.js.map +1 -1
- package/dist-lib/components/Plan/BackupEvents/BackupEvents.module.scss.js +36 -36
- package/dist-lib/components/Plan/BackupProgress/BackupProgress.d.ts.map +1 -1
- package/dist-lib/components/Plan/BackupProgress/BackupProgress.js +79 -72
- package/dist-lib/components/Plan/BackupProgress/BackupProgress.js.map +1 -1
- package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js +27 -25
- package/dist-lib/components/Plan/BackupProgress/BackupProgress.module.scss.js.map +1 -1
- package/dist-lib/components/Plan/Backups/Backups.d.ts.map +1 -1
- package/dist-lib/components/Plan/Backups/Backups.js +148 -144
- package/dist-lib/components/Plan/Backups/Backups.js.map +1 -1
- package/dist-lib/components/Plan/Backups/Backups.module.scss.js +34 -32
- package/dist-lib/components/Plan/Backups/Backups.module.scss.js.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.js +89 -101
- package/dist-lib/components/Plan/PlanSettings/PlanScriptsSettings.js.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.js +31 -25
- package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.js.map +1 -1
- package/dist-lib/components/common/form/StoragePicker/StoragePicker.d.ts.map +1 -1
- package/dist-lib/components/common/form/StoragePicker/StoragePicker.js +52 -46
- package/dist-lib/components/common/form/StoragePicker/StoragePicker.js.map +1 -1
- package/dist-lib/routes/PlanSingle/PlanSingle.d.ts.map +1 -1
- package/dist-lib/routes/PlanSingle/PlanSingle.js +76 -72
- package/dist-lib/routes/PlanSingle/PlanSingle.js.map +1 -1
- package/dist-lib/routes/PlanSingle/PlanSingle.module.scss.js +14 -12
- package/dist-lib/routes/PlanSingle/PlanSingle.module.scss.js.map +1 -1
- package/dist-lib/styles/core-frontend.css +1 -1
- package/dist-lib/styles/global.scss +4 -0
- package/dist-lib/utils/progressHelpers.d.ts.map +1 -1
- package/dist-lib/utils/progressHelpers.js +1 -0
- package/dist-lib/utils/progressHelpers.js.map +1 -1
- package/package.json +1 -1
- package/src/@types/devices.ts +4 -4
- package/src/@types/plans.ts +1 -0
- package/src/components/App/Footer/Footer.tsx +3 -2
- package/src/components/Device/EditDevice/EditDevice.tsx +11 -4
- package/src/components/Plan/BackupEvents/BackupEvents.module.scss +2 -0
- package/src/components/Plan/BackupEvents/BackupEvents.tsx +2 -2
- package/src/components/Plan/BackupProgress/BackupProgress.module.scss +4 -0
- package/src/components/Plan/BackupProgress/BackupProgress.tsx +11 -4
- package/src/components/Plan/Backups/Backups.module.scss +16 -0
- package/src/components/Plan/Backups/Backups.tsx +13 -2
- package/src/components/Plan/PlanSettings/PlanScriptsSettings.tsx +29 -31
- package/src/components/Plan/PlanSettings/PlanSourceSettings.tsx +11 -3
- package/src/components/common/form/StoragePicker/StoragePicker.tsx +10 -2
- package/src/routes/PlanSingle/PlanSingle.module.scss +10 -0
- package/src/routes/PlanSingle/PlanSingle.tsx +6 -1
- package/src/styles/global.scss +4 -0
- package/src/utils/progressHelpers.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progressHelpers.d.ts","sourceRoot":"","sources":["../../src/utils/progressHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAqC7F;AAoCD,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,EAAE,cAAc,MAAM,KAAG,
|
|
1
|
+
{"version":3,"file":"progressHelpers.d.ts","sourceRoot":"","sources":["../../src/utils/progressHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAqC7F;AAoCD,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,EAAE,cAAc,MAAM,KAAG,MA6HlF,CAAC;AAEF,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAqC9F;AAED,eAAO,MAAM,4BAA4B,GAAI,QAAQ,MAAM,KAAG,MAiD7D,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,OAYxE;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;CAAE,GAAG,MAAM,CAkBxI"}
|
|
@@ -63,6 +63,7 @@ const c = (t) => {
|
|
|
63
63
|
PRE_BACKUP_START: "Preparing Backup...",
|
|
64
64
|
PRE_BACKUP_DRY_RUN_START: "Performing Dry Run...",
|
|
65
65
|
PRE_BACKUP_DRY_RUN_COMPLETE: "Dry Run Complete",
|
|
66
|
+
PRE_BACKUP_DRY_RUN_ERROR: "Dry Run Encountered Errors",
|
|
66
67
|
PRE_BACKUP_CHECKS_START: "Running Checks...",
|
|
67
68
|
PRE_BACKUP_CHECKS_COMPLETE: "Checks Complete",
|
|
68
69
|
PRE_BACKUP_SCRIPTS_START: "Running Scripts...",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progressHelpers.js","sources":["../../src/utils/progressHelpers.ts"],"sourcesContent":["import { BackupProgressData } from '../@types/backups';\r\n\r\nexport function generateBackupProgressMessage(progressData: BackupProgressData | null): string {\r\n if (!progressData?.events?.length) {\r\n return 'Initializing...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || progressData.events[progressData.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Initializing...';\r\n }\r\n\r\n const { phase, action } = currentEvent;\r\n\r\n // Map phases to display names\r\n const phaseDisplayNames: Record<string, string> = {\r\n initializing: 'Initializing',\r\n 'pre-backup': 'Pre-Backup',\r\n backup: 'Backup',\r\n 'post-backup': 'Post-Backup',\r\n replicating: 'Replicating',\r\n finished: 'Complete',\r\n };\r\n\r\n const phaseDisplay = phaseDisplayNames[phase] || phase;\r\n const actionDisplay = getBackupEventActionMessage(action);\r\n\r\n if (phase === 'finished') {\r\n return actionDisplay;\r\n }\r\n\r\n return `${phaseDisplay}: ${actionDisplay}`;\r\n}\r\n\r\n// Helper function to handle script actions dynamically\r\nconst handleScriptAction = (action: string): string | null => {\r\n const scriptTypes = {\r\n ONBACKUPSTART: 'Start Script',\r\n ONBACKUPCOMPLETE: 'Complete Script',\r\n ONBACKUPERROR: 'Error Script',\r\n ONBACKUPFAILURE: 'Failure Script',\r\n ONBACKUPEND: 'End Script',\r\n };\r\n\r\n const actionStates = {\r\n START: (scriptType: string, num: string) => `Running ${scriptType} ${num}...`,\r\n COMPLETE: (_: string, num: string) => `Script ${num} Complete`,\r\n FAIL: (_: string, num: string) => `Script ${num} Failed`,\r\n ERROR: (scriptType: string, num: string) => `Error in ${scriptType} ${num}`,\r\n };\r\n\r\n // Generic pattern for all script actions: SCRIPTTYPE_SCRIPT_NUMBER_STATE\r\n const scriptMatch = action.match(/^(ONBACKUP\\w+)_SCRIPT_(\\d+)_(START|COMPLETE|FAIL|ERROR)$/);\r\n\r\n if (scriptMatch) {\r\n const [, scriptTypeKey, scriptNum, state] = scriptMatch;\r\n const scriptType = scriptTypes[scriptTypeKey as keyof typeof scriptTypes] || 'Script';\r\n const stateHandler = actionStates[state as keyof typeof actionStates];\r\n\r\n if (stateHandler) {\r\n return stateHandler(scriptType, scriptNum);\r\n }\r\n }\r\n\r\n return null;\r\n};\r\n\r\n// Function to extract numbers from action strings and create meaningful messages\r\nexport const getBackupEventActionMessage = (action: string, storageName?: string): string => {\r\n // Handle script actions first\r\n const scriptMessage = handleScriptAction(action);\r\n if (scriptMessage) {\r\n return scriptMessage;\r\n }\r\n\r\n // Handle retry attempts\r\n const retryAttemptMatch = action.match(/RETRY_ATTEMPT_(\\d+)_OF_(\\d+)_START/);\r\n if (retryAttemptMatch) {\r\n const [, current, total] = retryAttemptMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle backup retry scheduling\r\n const backupRetryMatch = action.match(/BACKUP_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (backupRetryMatch) {\r\n const [, current, total] = backupRetryMatch;\r\n return `Scheduling Retry (${parseInt(current)}/${total})...`;\r\n }\r\n\r\n // Handle replication retry scheduling\r\n const replicationRetryScheduledMatch = action.match(/REPLICATION_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (replicationRetryScheduledMatch) {\r\n const [, current, total] = replicationRetryScheduledMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle replication retry start\r\n const replicationRetryStartMatch = action.match(/REPLICATION_RETRY_(\\d+)_OF_(\\d+)_START/);\r\n if (replicationRetryStartMatch) {\r\n const [, current, total] = replicationRetryStartMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Static action mappings\r\n const staticMessages: Record<string, string> = {\r\n INITIALIZE: 'Starting Backup...',\r\n PRE_BACKUP_START: 'Preparing Backup...',\r\n PRE_BACKUP_DRY_RUN_START: 'Performing Dry Run...',\r\n PRE_BACKUP_DRY_RUN_COMPLETE: 'Dry Run Complete',\r\n PRE_BACKUP_CHECKS_START: 'Running Checks...',\r\n PRE_BACKUP_CHECKS_COMPLETE: 'Checks Complete',\r\n PRE_BACKUP_SCRIPTS_START: 'Running Scripts...',\r\n PRE_BACKUP_SCRIPTS_COMPLETE: 'Scripts Complete',\r\n PRE_BACKUP_UNLOCK_STALE_LOCKS: 'Unlocking Repository...',\r\n PRE_BACKUP_COMPLETE: 'Pre-Backup Complete',\r\n BACKUP_OPERATION_START: 'Backing Up Files...',\r\n BACKUP_OPERATION_COMPLETE: 'Backup Complete',\r\n BACKUP_OPERATION_ERROR: 'Backup Encountered Errors',\r\n POST_BACKUP_START: 'Finalizing...',\r\n POST_BACKUP_SCRIPTS_START: 'Running Cleanup Scripts...',\r\n POST_BACKUP_SCRIPTS_COMPLETE: 'Cleanup Scripts Complete',\r\n POST_BACKUP_PRUNE_START: 'Pruning Old Backups...',\r\n POST_BACKUP_PRUNE_COMPLETE: 'Pruning Complete',\r\n POST_BACKUP_PRUNE_FAILED: 'Pruning Failed',\r\n POST_BACKUP_COMPLETE: 'Post-Backup Complete',\r\n POST_BACKUP_REPO_STATS_START: 'Updating Repository Statistics...',\r\n POST_BACKUP_REPO_STATS_COMPLETE: 'Repository Statistics Updated',\r\n POST_BACKUP_REPO_STATS_FAILED: 'Failed to Update Repository Statistics',\r\n TASK_COMPLETED: 'Completed Successfully',\r\n TASK_CANCELLED: 'Backup Cancelled by User.',\r\n TASK_FAILED: 'Backup Failed with Error.',\r\n FAILED_PERMANENTLY: 'Failed Permanently',\r\n ISO_CREATION_START: 'Creating ISO...',\r\n ISO_CREATION_COMPLETE: 'ISO Created Successfully',\r\n ISO_CREATION_FAILED: 'ISO Creation Failed',\r\n REMOTE_BACKUP_START: 'Backing Up Data...',\r\n REMOTE_BACKUP_COMPLETE: 'Data Backup Complete',\r\n REMOTE_BACKUP_FAILED: 'Data Backup Failed',\r\n ISO_ENCRYPTION_START: 'Encrypting ISO...',\r\n ISO_ENCRYPTION_COMPLETE: 'ISO Encryption Complete',\r\n ISO_UPLOAD_START: 'Uploading ISO...',\r\n ISO_UPLOAD_COMPLETE: 'ISO Upload Complete',\r\n ISO_UPLOAD_FAILED: 'ISO Upload Failed',\r\n BACKUP_WARNING: 'Hiccup Detected During Backup',\r\n // Replication actions\r\n REPLICATION_START: 'Replicating Snapshots...',\r\n REPLICATION_INIT_START: 'Initializing Replication Repository...',\r\n REPLICATION_INIT_COMPLETE: 'Replication Repository Initialized',\r\n REPLICATION_INIT_FAILED: 'Replication Repository Init Failed',\r\n REPLICATION_UNLOCK_START: 'Unlocking Replication Repository...',\r\n REPLICATION_UNLOCK_COMPLETE: 'Replication Repository Unlocked',\r\n REPLICATION_COPY_START: 'Copying Snapshot to Replication...',\r\n REPLICATION_COPY_COMPLETE: 'Snapshot Copied to Replication',\r\n REPLICATION_COPY_FAILED: 'Replication Copy Failed',\r\n REPLICATION_PRUNE_START: 'Pruning Replication Repository...',\r\n REPLICATION_PRUNE_COMPLETE: 'Replication Prune Complete',\r\n REPLICATION_PRUNE_FAILED: 'Replication Prune Failed',\r\n REPLICATION_STATS_START: 'Updating Replication Stats...',\r\n REPLICATION_STATS_COMPLETE: 'Replication Stats Updated',\r\n REPLICATION_COMPLETE: 'Replication Complete',\r\n REPLICATION_FAILED: 'Replication Failed',\r\n REPLICATION_PARTIAL_FAILURE: 'Some Replications Failed',\r\n REPLICATION_MANUAL_RETRY_START: 'Retrying Failed Replications...',\r\n REPLICATION_MANUAL_RETRY_COMPLETE: 'Replication Retry Complete',\r\n REPLICATION_MANUAL_RETRY_PARTIAL_FAILURE: 'Some Replication Retries Still Failed',\r\n };\r\n\r\n // Storage-name-aware replication action mappings\r\n const replicationMessagesWithStorage: Record<string, (name: string) => string> = {\r\n REPLICATION_START: (name) => `Replicating Snapshot to ${name}...`,\r\n REPLICATION_INIT_START: (name) => `Initializing Replication Repository in ${name}...`,\r\n REPLICATION_INIT_COMPLETE: (name) => `Replication Repository Initialized in ${name}`,\r\n REPLICATION_INIT_FAILED: (name) => `Replication Repository Init Failed in ${name}`,\r\n REPLICATION_UNLOCK_START: (name) => `Unlocking Replication Repository in ${name}...`,\r\n REPLICATION_UNLOCK_COMPLETE: (name) => `Replication Repository Unlocked in ${name}`,\r\n REPLICATION_COPY_START: (name) => `Copying Snapshot to ${name}...`,\r\n REPLICATION_COPY_COMPLETE: (name) => `Snapshot Copied to ${name}`,\r\n REPLICATION_COPY_FAILED: (name) => `Replication Copy Failed for ${name}`,\r\n REPLICATION_PRUNE_START: (name) => `Pruning Replication Repository in ${name}...`,\r\n REPLICATION_PRUNE_COMPLETE: (name) => `Replication Prune Complete for ${name}`,\r\n REPLICATION_PRUNE_FAILED: (name) => `Replication Prune Failed for ${name}`,\r\n REPLICATION_STATS_START: (name) => `Updating Replication Stats for ${name}...`,\r\n REPLICATION_STATS_COMPLETE: (name) => `Replication Stats Updated for ${name}`,\r\n REPLICATION_COMPLETE: (name) => `Replication Complete for ${name}`,\r\n REPLICATION_FAILED: (name) => `Replication Failed for ${name}`,\r\n };\r\n\r\n if (storageName && replicationMessagesWithStorage[action]) {\r\n return replicationMessagesWithStorage[action](storageName);\r\n }\r\n\r\n return staticMessages[action] || action;\r\n};\r\n\r\nexport function generateRestoreProgressMessage(progressData: BackupProgressData | null): string {\r\n if (!progressData?.events?.length) {\r\n return 'Initializing...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || progressData.events[progressData.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Initializing...';\r\n }\r\n\r\n const { phase, action } = currentEvent;\r\n\r\n // Map phases to display names\r\n const phaseDisplayNames: Record<string, string> = {\r\n initializing: 'Initializing',\r\n 'pre-restore': 'Pre-Restore',\r\n restore: 'Restore',\r\n 'post-restore': 'Post-Restore',\r\n retry: 'Retrying',\r\n finished: 'Complete',\r\n };\r\n\r\n const phaseDisplay = phaseDisplayNames[phase] || phase;\r\n const actionDisplay = getRestoreEventActionMessage(action);\r\n\r\n if (phase === 'finished') {\r\n return actionDisplay;\r\n }\r\n\r\n return `${phaseDisplay}: ${actionDisplay}`;\r\n}\r\n\r\nexport const getRestoreEventActionMessage = (action: string): string => {\r\n // Handle script actions first\r\n const scriptMessage = handleScriptAction(action);\r\n if (scriptMessage) {\r\n return scriptMessage;\r\n }\r\n\r\n // Handle retry attempts\r\n const retryAttemptMatch = action.match(/RETRY_ATTEMPT_(\\d+)_OF_(\\d+)_START/);\r\n if (retryAttemptMatch) {\r\n const [, current, total] = retryAttemptMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle backup retry scheduling\r\n const backupRetryMatch = action.match(/RESTORE_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (backupRetryMatch) {\r\n const [, current, total] = backupRetryMatch;\r\n return `Scheduling Retry (${parseInt(current)}/${total})...`;\r\n }\r\n\r\n // Static action mappings\r\n const staticMessages: Record<string, string> = {\r\n INITIALIZE: 'Starting Restore...',\r\n PRE_RESTORE_START: 'Preparing Restore...',\r\n PRE_RESTORE_GET_SNAPSHOT: 'Getting Snapshot to Restore...',\r\n PRE_RESTORE_GET_SNAPSHOT_COMPLETE: 'Snapshot Retrieved',\r\n PRE_RESTORE_GET_SNAPSHOT_FAILED: 'Failed to Retrieve Snapshot',\r\n PRE_RESTORE_DRY_RUN_START: 'Performing Dry Run...',\r\n PRE_RESTORE_DRY_RUN_COMPLETE: 'Dry Run Complete',\r\n PRE_RESTORE_CHECKS_START: 'Running Checks...',\r\n PRE_RESTORE_CHECKS_COMPLETE: 'Checks Complete',\r\n PRE_RESTORE_UNLOCK_STALE_LOCKS: 'Unlocking Repository...',\r\n PRE_RESTORE_COMPLETE: 'Pre-Restore Complete',\r\n RESTORE_OPERATION_START: 'Restoring Files...',\r\n RESTORE_OPERATION_COMPLETE: 'Restore Complete',\r\n POST_RESTORE_START: 'Finalizing...',\r\n POST_RESTORE_COMPLETE: 'Post-Restore Complete',\r\n POST_RESTORE_REPO_STATS_START: 'Updating Repository Statistics...',\r\n POST_RESTORE_REPO_STATS_COMPLETE: 'Repository Statistics Updated',\r\n POST_RESTORE_WINDOWS_MOVE_START: 'Moving Restored files from temp directory to target path...',\r\n POST_RESTORE_WINDOWS_MOVE_ERROR: 'Failed to Move Restored files from temp directory to target path',\r\n POST_RESTORE_WINDOWS_MOVE_COMPLETE: 'Moved Restored files to target path',\r\n TASK_COMPLETED: 'Completed Successfully',\r\n TASK_FAILED: 'Restore Failed with Error.',\r\n TASK_CANCELLED: 'Restore Cancelled by User.',\r\n FAILED_PERMANENTLY: 'Failed Permanently',\r\n };\r\n return staticMessages[action] || action;\r\n};\r\n\r\nexport function extractResticData(progressData: BackupProgressData | null) {\r\n if (!progressData?.events?.length) {\r\n return null;\r\n }\r\n\r\n // Find the most recent event with resticData\r\n const eventWithResticData = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => event.resticData);\r\n\r\n return eventWithResticData?.resticData || null;\r\n}\r\n\r\n/**\r\n * Generates a human-readable progress message for a specific replication mirror.\r\n * Uses the mirror's latest event action and includes the storage name.\r\n */\r\nexport function generateMirrorProgressMessage(mirror: { storageName: string; events?: { action: string; completed: boolean }[] }): string {\r\n if (!mirror.events?.length) {\r\n return 'Pending...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = mirror.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || mirror.events[mirror.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Pending...';\r\n }\r\n\r\n return getBackupEventActionMessage(currentEvent.action, mirror.storageName);\r\n}\r\n"],"names":["generateBackupProgressMessage","progressData","_a","currentEvent","event","phase","action","phaseDisplay","actionDisplay","getBackupEventActionMessage","handleScriptAction","scriptTypes","actionStates","scriptType","num","_","scriptMatch","scriptTypeKey","scriptNum","state","stateHandler","storageName","scriptMessage","retryAttemptMatch","current","total","backupRetryMatch","replicationRetryScheduledMatch","replicationRetryStartMatch","staticMessages","replicationMessagesWithStorage","name","generateRestoreProgressMessage","getRestoreEventActionMessage","extractResticData","eventWithResticData","generateMirrorProgressMessage","mirror"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"progressHelpers.js","sources":["../../src/utils/progressHelpers.ts"],"sourcesContent":["import { BackupProgressData } from '../@types/backups';\r\n\r\nexport function generateBackupProgressMessage(progressData: BackupProgressData | null): string {\r\n if (!progressData?.events?.length) {\r\n return 'Initializing...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || progressData.events[progressData.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Initializing...';\r\n }\r\n\r\n const { phase, action } = currentEvent;\r\n\r\n // Map phases to display names\r\n const phaseDisplayNames: Record<string, string> = {\r\n initializing: 'Initializing',\r\n 'pre-backup': 'Pre-Backup',\r\n backup: 'Backup',\r\n 'post-backup': 'Post-Backup',\r\n replicating: 'Replicating',\r\n finished: 'Complete',\r\n };\r\n\r\n const phaseDisplay = phaseDisplayNames[phase] || phase;\r\n const actionDisplay = getBackupEventActionMessage(action);\r\n\r\n if (phase === 'finished') {\r\n return actionDisplay;\r\n }\r\n\r\n return `${phaseDisplay}: ${actionDisplay}`;\r\n}\r\n\r\n// Helper function to handle script actions dynamically\r\nconst handleScriptAction = (action: string): string | null => {\r\n const scriptTypes = {\r\n ONBACKUPSTART: 'Start Script',\r\n ONBACKUPCOMPLETE: 'Complete Script',\r\n ONBACKUPERROR: 'Error Script',\r\n ONBACKUPFAILURE: 'Failure Script',\r\n ONBACKUPEND: 'End Script',\r\n };\r\n\r\n const actionStates = {\r\n START: (scriptType: string, num: string) => `Running ${scriptType} ${num}...`,\r\n COMPLETE: (_: string, num: string) => `Script ${num} Complete`,\r\n FAIL: (_: string, num: string) => `Script ${num} Failed`,\r\n ERROR: (scriptType: string, num: string) => `Error in ${scriptType} ${num}`,\r\n };\r\n\r\n // Generic pattern for all script actions: SCRIPTTYPE_SCRIPT_NUMBER_STATE\r\n const scriptMatch = action.match(/^(ONBACKUP\\w+)_SCRIPT_(\\d+)_(START|COMPLETE|FAIL|ERROR)$/);\r\n\r\n if (scriptMatch) {\r\n const [, scriptTypeKey, scriptNum, state] = scriptMatch;\r\n const scriptType = scriptTypes[scriptTypeKey as keyof typeof scriptTypes] || 'Script';\r\n const stateHandler = actionStates[state as keyof typeof actionStates];\r\n\r\n if (stateHandler) {\r\n return stateHandler(scriptType, scriptNum);\r\n }\r\n }\r\n\r\n return null;\r\n};\r\n\r\n// Function to extract numbers from action strings and create meaningful messages\r\nexport const getBackupEventActionMessage = (action: string, storageName?: string): string => {\r\n // Handle script actions first\r\n const scriptMessage = handleScriptAction(action);\r\n if (scriptMessage) {\r\n return scriptMessage;\r\n }\r\n\r\n // Handle retry attempts\r\n const retryAttemptMatch = action.match(/RETRY_ATTEMPT_(\\d+)_OF_(\\d+)_START/);\r\n if (retryAttemptMatch) {\r\n const [, current, total] = retryAttemptMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle backup retry scheduling\r\n const backupRetryMatch = action.match(/BACKUP_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (backupRetryMatch) {\r\n const [, current, total] = backupRetryMatch;\r\n return `Scheduling Retry (${parseInt(current)}/${total})...`;\r\n }\r\n\r\n // Handle replication retry scheduling\r\n const replicationRetryScheduledMatch = action.match(/REPLICATION_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (replicationRetryScheduledMatch) {\r\n const [, current, total] = replicationRetryScheduledMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle replication retry start\r\n const replicationRetryStartMatch = action.match(/REPLICATION_RETRY_(\\d+)_OF_(\\d+)_START/);\r\n if (replicationRetryStartMatch) {\r\n const [, current, total] = replicationRetryStartMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Static action mappings\r\n const staticMessages: Record<string, string> = {\r\n INITIALIZE: 'Starting Backup...',\r\n PRE_BACKUP_START: 'Preparing Backup...',\r\n PRE_BACKUP_DRY_RUN_START: 'Performing Dry Run...',\r\n PRE_BACKUP_DRY_RUN_COMPLETE: 'Dry Run Complete',\r\n PRE_BACKUP_DRY_RUN_ERROR: 'Dry Run Encountered Errors',\r\n PRE_BACKUP_CHECKS_START: 'Running Checks...',\r\n PRE_BACKUP_CHECKS_COMPLETE: 'Checks Complete',\r\n PRE_BACKUP_SCRIPTS_START: 'Running Scripts...',\r\n PRE_BACKUP_SCRIPTS_COMPLETE: 'Scripts Complete',\r\n PRE_BACKUP_UNLOCK_STALE_LOCKS: 'Unlocking Repository...',\r\n PRE_BACKUP_COMPLETE: 'Pre-Backup Complete',\r\n BACKUP_OPERATION_START: 'Backing Up Files...',\r\n BACKUP_OPERATION_COMPLETE: 'Backup Complete',\r\n BACKUP_OPERATION_ERROR: 'Backup Encountered Errors',\r\n POST_BACKUP_START: 'Finalizing...',\r\n POST_BACKUP_SCRIPTS_START: 'Running Cleanup Scripts...',\r\n POST_BACKUP_SCRIPTS_COMPLETE: 'Cleanup Scripts Complete',\r\n POST_BACKUP_PRUNE_START: 'Pruning Old Backups...',\r\n POST_BACKUP_PRUNE_COMPLETE: 'Pruning Complete',\r\n POST_BACKUP_PRUNE_FAILED: 'Pruning Failed',\r\n POST_BACKUP_COMPLETE: 'Post-Backup Complete',\r\n POST_BACKUP_REPO_STATS_START: 'Updating Repository Statistics...',\r\n POST_BACKUP_REPO_STATS_COMPLETE: 'Repository Statistics Updated',\r\n POST_BACKUP_REPO_STATS_FAILED: 'Failed to Update Repository Statistics',\r\n TASK_COMPLETED: 'Completed Successfully',\r\n TASK_CANCELLED: 'Backup Cancelled by User.',\r\n TASK_FAILED: 'Backup Failed with Error.',\r\n FAILED_PERMANENTLY: 'Failed Permanently',\r\n ISO_CREATION_START: 'Creating ISO...',\r\n ISO_CREATION_COMPLETE: 'ISO Created Successfully',\r\n ISO_CREATION_FAILED: 'ISO Creation Failed',\r\n REMOTE_BACKUP_START: 'Backing Up Data...',\r\n REMOTE_BACKUP_COMPLETE: 'Data Backup Complete',\r\n REMOTE_BACKUP_FAILED: 'Data Backup Failed',\r\n ISO_ENCRYPTION_START: 'Encrypting ISO...',\r\n ISO_ENCRYPTION_COMPLETE: 'ISO Encryption Complete',\r\n ISO_UPLOAD_START: 'Uploading ISO...',\r\n ISO_UPLOAD_COMPLETE: 'ISO Upload Complete',\r\n ISO_UPLOAD_FAILED: 'ISO Upload Failed',\r\n BACKUP_WARNING: 'Hiccup Detected During Backup',\r\n // Replication actions\r\n REPLICATION_START: 'Replicating Snapshots...',\r\n REPLICATION_INIT_START: 'Initializing Replication Repository...',\r\n REPLICATION_INIT_COMPLETE: 'Replication Repository Initialized',\r\n REPLICATION_INIT_FAILED: 'Replication Repository Init Failed',\r\n REPLICATION_UNLOCK_START: 'Unlocking Replication Repository...',\r\n REPLICATION_UNLOCK_COMPLETE: 'Replication Repository Unlocked',\r\n REPLICATION_COPY_START: 'Copying Snapshot to Replication...',\r\n REPLICATION_COPY_COMPLETE: 'Snapshot Copied to Replication',\r\n REPLICATION_COPY_FAILED: 'Replication Copy Failed',\r\n REPLICATION_PRUNE_START: 'Pruning Replication Repository...',\r\n REPLICATION_PRUNE_COMPLETE: 'Replication Prune Complete',\r\n REPLICATION_PRUNE_FAILED: 'Replication Prune Failed',\r\n REPLICATION_STATS_START: 'Updating Replication Stats...',\r\n REPLICATION_STATS_COMPLETE: 'Replication Stats Updated',\r\n REPLICATION_COMPLETE: 'Replication Complete',\r\n REPLICATION_FAILED: 'Replication Failed',\r\n REPLICATION_PARTIAL_FAILURE: 'Some Replications Failed',\r\n REPLICATION_MANUAL_RETRY_START: 'Retrying Failed Replications...',\r\n REPLICATION_MANUAL_RETRY_COMPLETE: 'Replication Retry Complete',\r\n REPLICATION_MANUAL_RETRY_PARTIAL_FAILURE: 'Some Replication Retries Still Failed',\r\n };\r\n\r\n // Storage-name-aware replication action mappings\r\n const replicationMessagesWithStorage: Record<string, (name: string) => string> = {\r\n REPLICATION_START: (name) => `Replicating Snapshot to ${name}...`,\r\n REPLICATION_INIT_START: (name) => `Initializing Replication Repository in ${name}...`,\r\n REPLICATION_INIT_COMPLETE: (name) => `Replication Repository Initialized in ${name}`,\r\n REPLICATION_INIT_FAILED: (name) => `Replication Repository Init Failed in ${name}`,\r\n REPLICATION_UNLOCK_START: (name) => `Unlocking Replication Repository in ${name}...`,\r\n REPLICATION_UNLOCK_COMPLETE: (name) => `Replication Repository Unlocked in ${name}`,\r\n REPLICATION_COPY_START: (name) => `Copying Snapshot to ${name}...`,\r\n REPLICATION_COPY_COMPLETE: (name) => `Snapshot Copied to ${name}`,\r\n REPLICATION_COPY_FAILED: (name) => `Replication Copy Failed for ${name}`,\r\n REPLICATION_PRUNE_START: (name) => `Pruning Replication Repository in ${name}...`,\r\n REPLICATION_PRUNE_COMPLETE: (name) => `Replication Prune Complete for ${name}`,\r\n REPLICATION_PRUNE_FAILED: (name) => `Replication Prune Failed for ${name}`,\r\n REPLICATION_STATS_START: (name) => `Updating Replication Stats for ${name}...`,\r\n REPLICATION_STATS_COMPLETE: (name) => `Replication Stats Updated for ${name}`,\r\n REPLICATION_COMPLETE: (name) => `Replication Complete for ${name}`,\r\n REPLICATION_FAILED: (name) => `Replication Failed for ${name}`,\r\n };\r\n\r\n if (storageName && replicationMessagesWithStorage[action]) {\r\n return replicationMessagesWithStorage[action](storageName);\r\n }\r\n\r\n return staticMessages[action] || action;\r\n};\r\n\r\nexport function generateRestoreProgressMessage(progressData: BackupProgressData | null): string {\r\n if (!progressData?.events?.length) {\r\n return 'Initializing...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || progressData.events[progressData.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Initializing...';\r\n }\r\n\r\n const { phase, action } = currentEvent;\r\n\r\n // Map phases to display names\r\n const phaseDisplayNames: Record<string, string> = {\r\n initializing: 'Initializing',\r\n 'pre-restore': 'Pre-Restore',\r\n restore: 'Restore',\r\n 'post-restore': 'Post-Restore',\r\n retry: 'Retrying',\r\n finished: 'Complete',\r\n };\r\n\r\n const phaseDisplay = phaseDisplayNames[phase] || phase;\r\n const actionDisplay = getRestoreEventActionMessage(action);\r\n\r\n if (phase === 'finished') {\r\n return actionDisplay;\r\n }\r\n\r\n return `${phaseDisplay}: ${actionDisplay}`;\r\n}\r\n\r\nexport const getRestoreEventActionMessage = (action: string): string => {\r\n // Handle script actions first\r\n const scriptMessage = handleScriptAction(action);\r\n if (scriptMessage) {\r\n return scriptMessage;\r\n }\r\n\r\n // Handle retry attempts\r\n const retryAttemptMatch = action.match(/RETRY_ATTEMPT_(\\d+)_OF_(\\d+)_START/);\r\n if (retryAttemptMatch) {\r\n const [, current, total] = retryAttemptMatch;\r\n return `Retrying (${current}/${total})...`;\r\n }\r\n\r\n // Handle backup retry scheduling\r\n const backupRetryMatch = action.match(/RESTORE_RETRY_(\\d+)_OF_(\\d+)_SCHEDULED/);\r\n if (backupRetryMatch) {\r\n const [, current, total] = backupRetryMatch;\r\n return `Scheduling Retry (${parseInt(current)}/${total})...`;\r\n }\r\n\r\n // Static action mappings\r\n const staticMessages: Record<string, string> = {\r\n INITIALIZE: 'Starting Restore...',\r\n PRE_RESTORE_START: 'Preparing Restore...',\r\n PRE_RESTORE_GET_SNAPSHOT: 'Getting Snapshot to Restore...',\r\n PRE_RESTORE_GET_SNAPSHOT_COMPLETE: 'Snapshot Retrieved',\r\n PRE_RESTORE_GET_SNAPSHOT_FAILED: 'Failed to Retrieve Snapshot',\r\n PRE_RESTORE_DRY_RUN_START: 'Performing Dry Run...',\r\n PRE_RESTORE_DRY_RUN_COMPLETE: 'Dry Run Complete',\r\n PRE_RESTORE_CHECKS_START: 'Running Checks...',\r\n PRE_RESTORE_CHECKS_COMPLETE: 'Checks Complete',\r\n PRE_RESTORE_UNLOCK_STALE_LOCKS: 'Unlocking Repository...',\r\n PRE_RESTORE_COMPLETE: 'Pre-Restore Complete',\r\n RESTORE_OPERATION_START: 'Restoring Files...',\r\n RESTORE_OPERATION_COMPLETE: 'Restore Complete',\r\n POST_RESTORE_START: 'Finalizing...',\r\n POST_RESTORE_COMPLETE: 'Post-Restore Complete',\r\n POST_RESTORE_REPO_STATS_START: 'Updating Repository Statistics...',\r\n POST_RESTORE_REPO_STATS_COMPLETE: 'Repository Statistics Updated',\r\n POST_RESTORE_WINDOWS_MOVE_START: 'Moving Restored files from temp directory to target path...',\r\n POST_RESTORE_WINDOWS_MOVE_ERROR: 'Failed to Move Restored files from temp directory to target path',\r\n POST_RESTORE_WINDOWS_MOVE_COMPLETE: 'Moved Restored files to target path',\r\n TASK_COMPLETED: 'Completed Successfully',\r\n TASK_FAILED: 'Restore Failed with Error.',\r\n TASK_CANCELLED: 'Restore Cancelled by User.',\r\n FAILED_PERMANENTLY: 'Failed Permanently',\r\n };\r\n return staticMessages[action] || action;\r\n};\r\n\r\nexport function extractResticData(progressData: BackupProgressData | null) {\r\n if (!progressData?.events?.length) {\r\n return null;\r\n }\r\n\r\n // Find the most recent event with resticData\r\n const eventWithResticData = progressData.events\r\n .slice()\r\n .reverse()\r\n .find((event) => event.resticData);\r\n\r\n return eventWithResticData?.resticData || null;\r\n}\r\n\r\n/**\r\n * Generates a human-readable progress message for a specific replication mirror.\r\n * Uses the mirror's latest event action and includes the storage name.\r\n */\r\nexport function generateMirrorProgressMessage(mirror: { storageName: string; events?: { action: string; completed: boolean }[] }): string {\r\n if (!mirror.events?.length) {\r\n return 'Pending...';\r\n }\r\n\r\n // Find the last incomplete event or the most recent event\r\n const lastIncompleteEvent = mirror.events\r\n .slice()\r\n .reverse()\r\n .find((event) => !event.completed);\r\n\r\n const currentEvent = lastIncompleteEvent || mirror.events[mirror.events.length - 1];\r\n\r\n if (!currentEvent) {\r\n return 'Pending...';\r\n }\r\n\r\n return getBackupEventActionMessage(currentEvent.action, mirror.storageName);\r\n}\r\n"],"names":["generateBackupProgressMessage","progressData","_a","currentEvent","event","phase","action","phaseDisplay","actionDisplay","getBackupEventActionMessage","handleScriptAction","scriptTypes","actionStates","scriptType","num","_","scriptMatch","scriptTypeKey","scriptNum","state","stateHandler","storageName","scriptMessage","retryAttemptMatch","current","total","backupRetryMatch","replicationRetryScheduledMatch","replicationRetryStartMatch","staticMessages","replicationMessagesWithStorage","name","generateRestoreProgressMessage","getRestoreEventActionMessage","extractResticData","eventWithResticData","generateMirrorProgressMessage","mirror"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/@types/devices.ts
CHANGED
|
@@ -34,9 +34,7 @@ export interface Device {
|
|
|
34
34
|
plans: PlanChildItem[];
|
|
35
35
|
tags: string[];
|
|
36
36
|
metrics: DeviceMetrics | null;
|
|
37
|
-
settings:
|
|
38
|
-
tempDir: string;
|
|
39
|
-
};
|
|
37
|
+
settings: DeviceSettings | null;
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
export interface DeviceMetrics {
|
|
@@ -195,7 +193,9 @@ export interface DeviceMetrics {
|
|
|
195
193
|
}
|
|
196
194
|
|
|
197
195
|
export interface DeviceSettings {
|
|
198
|
-
|
|
196
|
+
general?: {
|
|
197
|
+
tempDir?: string; // Temporary directory for both restic and rclone. If set, it overrides the individual tempDir settings for restic and rclone.
|
|
198
|
+
};
|
|
199
199
|
restic?: {
|
|
200
200
|
maxProcessor?: number | ''; // GOMAXPROCS
|
|
201
201
|
cacheDir?: string; // RESTIC_CACHE_DIR
|
package/src/@types/plans.ts
CHANGED
|
@@ -4,18 +4,19 @@ import classes from './Footer.module.scss';
|
|
|
4
4
|
|
|
5
5
|
interface FooterProps {
|
|
6
6
|
version: string;
|
|
7
|
+
edition?: string;
|
|
7
8
|
latestVersion?: string;
|
|
8
9
|
hideUpgradeLink?: boolean;
|
|
9
10
|
changeLogUrl?: string;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
const Footer = ({ version = '1.0.0', latestVersion, hideUpgradeLink = false, changeLogUrl }: FooterProps) => {
|
|
13
|
+
const Footer = ({ version = '1.0.0', latestVersion, hideUpgradeLink = false, changeLogUrl, edition = '' }: FooterProps) => {
|
|
13
14
|
const updateDocLink = getUpdateDocLink(hideUpgradeLink);
|
|
14
15
|
return (
|
|
15
16
|
<>
|
|
16
17
|
<div className={classes.footer}>
|
|
17
18
|
<span>
|
|
18
|
-
Pluton v{version}{' '}
|
|
19
|
+
Pluton {edition} v{version}{' '}
|
|
19
20
|
{latestVersion && (
|
|
20
21
|
<span className={classes.newVersion} title={`Latest version: ${latestVersion}`}>
|
|
21
22
|
<Icon type="arrow-up" size={12} />{' '}
|
|
@@ -107,8 +107,13 @@ const EditDevice = ({ close, device }: EditDeviceProps) => {
|
|
|
107
107
|
<div className={classes.field}>
|
|
108
108
|
<Input
|
|
109
109
|
label="Temporary Working Directory"
|
|
110
|
-
fieldValue={newDevice.settings?.tempDir || ''}
|
|
111
|
-
onUpdate={(val) =>
|
|
110
|
+
fieldValue={newDevice.settings?.general?.tempDir || ''}
|
|
111
|
+
onUpdate={(val) =>
|
|
112
|
+
setNewDevice({
|
|
113
|
+
...newDevice,
|
|
114
|
+
settings: { ...newDevice.settings, general: { ...newDevice.settings?.general, tempDir: val } },
|
|
115
|
+
})
|
|
116
|
+
}
|
|
112
117
|
full={true}
|
|
113
118
|
hint="Where the backup, restores and downloads will be temporarily stored."
|
|
114
119
|
/>
|
|
@@ -142,9 +147,11 @@ const EditDevice = ({ close, device }: EditDeviceProps) => {
|
|
|
142
147
|
<FolderPicker
|
|
143
148
|
deviceId={device.id}
|
|
144
149
|
title="Select Temporary Working Directory"
|
|
145
|
-
selected={newDevice.settings?.tempDir || ''}
|
|
150
|
+
selected={newDevice.settings?.general?.tempDir || ''}
|
|
146
151
|
close={() => setShowFileManager(false)}
|
|
147
|
-
onSelect={(val) =>
|
|
152
|
+
onSelect={(val) =>
|
|
153
|
+
setNewDevice({ ...newDevice, settings: { ...newDevice.settings, general: { ...newDevice.settings?.general, tempDir: val } } })
|
|
154
|
+
}
|
|
148
155
|
/>
|
|
149
156
|
)}
|
|
150
157
|
</SidePanel>
|
|
@@ -154,8 +154,8 @@ const BackupEvents = ({ id, type = 'backup', sourceId, sourceType, planId, inPro
|
|
|
154
154
|
)}
|
|
155
155
|
</div>
|
|
156
156
|
{showError && (
|
|
157
|
-
<Modal title="Error Details" closeModal={() => setShowError(false)} width=
|
|
158
|
-
<div className={classes.errorDetails}>
|
|
157
|
+
<Modal title="Error Details" closeModal={() => setShowError(false)} width={'400px'}>
|
|
158
|
+
<div className={classes.errorDetails} data-length={showError.length}>
|
|
159
159
|
{(() => {
|
|
160
160
|
if (typeof showError === 'string') {
|
|
161
161
|
if (showError.startsWith('main-')) {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
color: var(--error-button-color);
|
|
41
41
|
margin-left: 0.5rem;
|
|
42
42
|
font-size: 0.9em;
|
|
43
|
+
cursor: pointer;
|
|
43
44
|
i {
|
|
44
45
|
font-style: normal;
|
|
45
46
|
}
|
|
@@ -116,6 +117,9 @@
|
|
|
116
117
|
color: #fff;
|
|
117
118
|
}
|
|
118
119
|
}
|
|
120
|
+
&.progressBarDryRun {
|
|
121
|
+
background-color: #b7cbf5;
|
|
122
|
+
}
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
}
|
|
@@ -37,11 +37,13 @@ const BackupProgress = ({ item, sourceId, sourceType, planId, type = 'backup' }:
|
|
|
37
37
|
const { data: progressData } =
|
|
38
38
|
type === 'backup' ? useGetBackupProgress({ id, sourceId, sourceType, planId }) : useGetRestoreProgress({ id, sourceId, sourceType, planId });
|
|
39
39
|
|
|
40
|
-
console.log('#### data :', progressData);
|
|
41
|
-
|
|
42
40
|
const resticData = extractResticData(progressData);
|
|
43
41
|
const progressMessage = type === 'backup' ? generateBackupProgressMessage(progressData) : generateRestoreProgressMessage(progressData);
|
|
44
42
|
|
|
43
|
+
// Check if DryRun progress tracking phase
|
|
44
|
+
const actualBackupStarted = progressData?.events.find((e: any) => e.action === 'BACKUP_OPERATION_START' && e.resticData);
|
|
45
|
+
const isDryRun = type === 'backup' && !actualBackupStarted;
|
|
46
|
+
|
|
45
47
|
// Extract progress values from restic data or use defaults
|
|
46
48
|
const {
|
|
47
49
|
total_files_processed: total_files = resticData?.total_files || 0,
|
|
@@ -146,7 +148,12 @@ const BackupProgress = ({ item, sourceId, sourceType, planId, type = 'backup' }:
|
|
|
146
148
|
{item.errorMsg && (
|
|
147
149
|
<span className={classes.backupError}>
|
|
148
150
|
<Icon type="error-circle-filled" size={13} />{' '}
|
|
149
|
-
<i
|
|
151
|
+
<i
|
|
152
|
+
data-tooltip-id="htmlToolTip"
|
|
153
|
+
data-tooltip-place="top"
|
|
154
|
+
data-tooltip-html={`<div class="linebreak-tooltip-content">${item.errorMsg.slice(0, 120) + (item.errorMsg.length > 120 ? '...' : '')}</div>`}
|
|
155
|
+
onClick={() => setShowProgressDetails(true)}
|
|
156
|
+
>
|
|
150
157
|
<u>Error</u> Occurred.
|
|
151
158
|
</i>{' '}
|
|
152
159
|
Retrying...
|
|
@@ -180,7 +187,7 @@ const BackupProgress = ({ item, sourceId, sourceType, planId, type = 'backup' }:
|
|
|
180
187
|
</div>
|
|
181
188
|
<div className={classes.progressBar}>
|
|
182
189
|
<div
|
|
183
|
-
className={`${classes.progressBarFill} ${progressPercent > 3 ? classes.progressBarFilled : ''}`}
|
|
190
|
+
className={`${classes.progressBarFill} ${progressPercent > 3 ? classes.progressBarFilled : ''} ${isDryRun ? classes.progressBarDryRun : ''}`}
|
|
184
191
|
style={{ width: progressPercent + '%' }}
|
|
185
192
|
>
|
|
186
193
|
<span>{progressPercent}%</span>
|
|
@@ -233,6 +233,22 @@
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
.errorDetailsModal {
|
|
237
|
+
width: 100%;
|
|
238
|
+
max-height: 500px;
|
|
239
|
+
overflow: auto;
|
|
240
|
+
font-family: monospace;
|
|
241
|
+
box-sizing: border-box;
|
|
242
|
+
border-radius: 6px;
|
|
243
|
+
background-color: var(--primary-color-lighter);
|
|
244
|
+
p {
|
|
245
|
+
border-bottom: 1px solid;
|
|
246
|
+
padding: 12px;
|
|
247
|
+
margin: 0;
|
|
248
|
+
border-color: var(--line-color);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
236
252
|
@media only screen and (max-width: 768px) {
|
|
237
253
|
.backupsTable {
|
|
238
254
|
overflow: auto;
|
|
@@ -21,7 +21,7 @@ import BackupEvents from '../BackupEvents/BackupEvents';
|
|
|
21
21
|
import Input from '../../common/form/Input/Input';
|
|
22
22
|
import MirrorStatusBadge from '../Mirrors/MirrorStatusBadge';
|
|
23
23
|
import MirrorStorageSelectorModal from '../Mirrors/MirrorStorageSelectorModal';
|
|
24
|
-
import { SidePanel, SnapshotViewer } from '../..';
|
|
24
|
+
import { Modal, SidePanel, SnapshotViewer } from '../..';
|
|
25
25
|
|
|
26
26
|
const DownloadLabel = ({ download, downloadBackup }: { download: Backup['download']; downloadBackup: () => void }) => {
|
|
27
27
|
if (download?.status === 'started') {
|
|
@@ -101,6 +101,7 @@ const Backups = ({
|
|
|
101
101
|
const [showRestoreModal, setShowRestoreModal] = useState<Backup | false>(false);
|
|
102
102
|
const [showBackupEvents, setShowBackupEvents] = useState<false | string>(false);
|
|
103
103
|
const [showSnapshotViewer, setShowSnapshotViewer] = useState<Backup | false>(false);
|
|
104
|
+
const [showBackupError, setShowBackupError] = useState<string | false>(false);
|
|
104
105
|
const [showEditModal, setShowEditModal] = useState<Backup | false>(false);
|
|
105
106
|
const [showStorageSelector, setShowStorageSelector] = useState<Backup | false>(false);
|
|
106
107
|
const queryClient = useQueryClient();
|
|
@@ -240,8 +241,9 @@ const Backups = ({
|
|
|
240
241
|
<div
|
|
241
242
|
className={`${classes.status} ${errorMsg ? classes.statusHasError : ''}`}
|
|
242
243
|
data-tooltip-id="htmlToolTip"
|
|
243
|
-
data-tooltip-html={`<div><string>Error</string>: ${errorMsg}</div>`}
|
|
244
|
+
data-tooltip-html={`<div class="linebreak-tooltip-content"><string>Error</string>: ${errorMsg?.slice(0, 120) + (errorMsg && errorMsg.length > 120 ? '...' : '')}</div>`}
|
|
244
245
|
data-tooltip-hidden={!errorMsg}
|
|
246
|
+
onClick={() => errorMsg && errorMsg.length > 120 && setShowBackupError(errorMsg)}
|
|
245
247
|
>
|
|
246
248
|
<StatusLabel status={status} hasError={!!errorMsg} />
|
|
247
249
|
</div>
|
|
@@ -453,6 +455,15 @@ const Backups = ({
|
|
|
453
455
|
/>
|
|
454
456
|
</SidePanel>
|
|
455
457
|
)}
|
|
458
|
+
{showBackupError && (
|
|
459
|
+
<Modal title="Error Details" closeModal={() => setShowBackupError(false)} width={'600px'}>
|
|
460
|
+
<div className={`${classes.errorDetailsModal} styled__scrollbar`}>
|
|
461
|
+
{showBackupError.split('\n').map((line, index) => (
|
|
462
|
+
<p key={index}>{line}</p>
|
|
463
|
+
))}
|
|
464
|
+
</div>
|
|
465
|
+
</Modal>
|
|
466
|
+
)}
|
|
456
467
|
</div>
|
|
457
468
|
);
|
|
458
469
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import classes from './PlanSettings.module.scss';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import { NewPlanSettings } from '../../../@types/plans';
|
|
3
|
+
import { NewPlanSettings, PlanScript } from '../../../@types/plans';
|
|
4
4
|
import Icon from '../../common/Icon/Icon';
|
|
5
5
|
import Toggle from '../../common/form/Toggle/Toggle';
|
|
6
6
|
import { secondsToMinutes } from '../../../utils/helpers';
|
|
@@ -45,7 +45,7 @@ const PlanScriptsSettings = ({
|
|
|
45
45
|
onBackupFailure: [],
|
|
46
46
|
onBackupComplete: [],
|
|
47
47
|
},
|
|
48
|
-
platform,
|
|
48
|
+
platform = '',
|
|
49
49
|
onUpdate,
|
|
50
50
|
}: PlanScriptsSettingsProps) => {
|
|
51
51
|
const [showTimeoutSettings, setShowTimeoutSettings] = useState<string | false>(false);
|
|
@@ -56,8 +56,18 @@ const PlanScriptsSettings = ({
|
|
|
56
56
|
onBackupFailure: false,
|
|
57
57
|
onBackupComplete: false,
|
|
58
58
|
});
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
|
|
60
|
+
const showRootOption = platform.toLowerCase().includes('linux');
|
|
61
|
+
// const showRootOption = true;
|
|
62
|
+
|
|
63
|
+
const updateScriptBool = (eventKey: ScriptEventKey, index: number, key: keyof PlanScript, value: boolean) => {
|
|
64
|
+
const updatedScripts = [...(settings[eventKey] || [])];
|
|
65
|
+
updatedScripts[index] = {
|
|
66
|
+
...updatedScripts[index],
|
|
67
|
+
[key]: value,
|
|
68
|
+
};
|
|
69
|
+
onUpdate({ ...settings, [eventKey]: updatedScripts });
|
|
70
|
+
};
|
|
61
71
|
|
|
62
72
|
return (
|
|
63
73
|
<div className={classes.eventTabs}>
|
|
@@ -171,48 +181,36 @@ const PlanScriptsSettings = ({
|
|
|
171
181
|
// label="Enabled"
|
|
172
182
|
fieldValue={script?.enabled || false}
|
|
173
183
|
inline={true}
|
|
174
|
-
onUpdate={(checked: boolean) =>
|
|
175
|
-
const updatedScripts = [...(scripts || [])];
|
|
176
|
-
updatedScripts[index] = {
|
|
177
|
-
...updatedScripts[index],
|
|
178
|
-
enabled: checked,
|
|
179
|
-
};
|
|
180
|
-
onUpdate({ ...settings, [eventKey]: updatedScripts });
|
|
181
|
-
}}
|
|
184
|
+
onUpdate={(checked: boolean) => updateScriptBool(key, index, 'enabled', checked)}
|
|
182
185
|
/>
|
|
183
186
|
<span>Enabled</span>
|
|
184
187
|
</div>
|
|
185
|
-
<div className={classes.scriptOptionCheckbox}>
|
|
188
|
+
{/* <div className={classes.scriptOptionCheckbox}>
|
|
186
189
|
<Toggle
|
|
187
190
|
fieldValue={script?.logOutput || false}
|
|
188
191
|
inline={true}
|
|
189
|
-
onUpdate={(checked: boolean) =>
|
|
190
|
-
const updatedScripts = [...(scripts || [])];
|
|
191
|
-
updatedScripts[index] = {
|
|
192
|
-
...updatedScripts[index],
|
|
193
|
-
logOutput: checked,
|
|
194
|
-
};
|
|
195
|
-
onUpdate({ ...settings, [eventKey]: updatedScripts });
|
|
196
|
-
}}
|
|
192
|
+
onUpdate={(checked: boolean) => updateScriptBool(key, index, 'logOutput', checked)}
|
|
197
193
|
/>
|
|
198
194
|
<span>Log Output</span>
|
|
199
|
-
</div>
|
|
195
|
+
</div> */}
|
|
200
196
|
<div className={classes.scriptOptionCheckbox}>
|
|
201
197
|
<Toggle
|
|
202
|
-
// label="Abort On Error"
|
|
203
198
|
fieldValue={script?.abortOnError || false}
|
|
204
199
|
inline={true}
|
|
205
|
-
onUpdate={(checked: boolean) =>
|
|
206
|
-
const updatedScripts = [...(scripts || [])];
|
|
207
|
-
updatedScripts[index] = {
|
|
208
|
-
...updatedScripts[index],
|
|
209
|
-
abortOnError: checked,
|
|
210
|
-
};
|
|
211
|
-
onUpdate({ ...settings, [eventKey]: updatedScripts });
|
|
212
|
-
}}
|
|
200
|
+
onUpdate={(checked: boolean) => updateScriptBool(key, index, 'abortOnError', checked)}
|
|
213
201
|
/>
|
|
214
202
|
<span>Abort on Error</span>
|
|
215
203
|
</div>
|
|
204
|
+
{showRootOption && (
|
|
205
|
+
<div className={classes.scriptOptionCheckbox}>
|
|
206
|
+
<Toggle
|
|
207
|
+
fieldValue={script?.runAsRoot || false}
|
|
208
|
+
inline={true}
|
|
209
|
+
onUpdate={(checked: boolean) => updateScriptBool(key, index, 'runAsRoot', checked)}
|
|
210
|
+
/>
|
|
211
|
+
<span>Run as Root</span>
|
|
212
|
+
</div>
|
|
213
|
+
)}
|
|
216
214
|
<i className="pipe">|</i>
|
|
217
215
|
<div
|
|
218
216
|
title="Timeout Settings"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
2
|
import classes from './PlanSettings.module.scss';
|
|
3
3
|
import PathPicker from '../../common/PathPicker/PathPicker';
|
|
4
4
|
import { NewPlanSettings } from '../../../@types/plans';
|
|
@@ -28,9 +28,16 @@ const PlanSourceSettings = ({ plan, onUpdate, error, isEditing }: PlanSourceSett
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// When the device changes, reset the sourceConfig paths to prevent invalid paths from being submitted
|
|
31
|
+
// When the device changes, reset the sourceConfig paths to prevent invalid paths from being submitted.
|
|
32
|
+
// Use a ref to track the previous deviceId so we only reset on an actual change (not on mount/remount,
|
|
33
|
+
// e.g. when navigating between steps in the Add Plan form).
|
|
34
|
+
const prevDeviceIdRef = useRef<string | null>(null);
|
|
32
35
|
useEffect(() => {
|
|
33
|
-
if (
|
|
36
|
+
if (isEditing) {
|
|
37
|
+
prevDeviceIdRef.current = deviceId;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (prevDeviceIdRef.current !== null && prevDeviceIdRef.current !== deviceId) {
|
|
34
41
|
onUpdate({
|
|
35
42
|
...plan,
|
|
36
43
|
sourceConfig: {
|
|
@@ -39,6 +46,7 @@ const PlanSourceSettings = ({ plan, onUpdate, error, isEditing }: PlanSourceSett
|
|
|
39
46
|
},
|
|
40
47
|
});
|
|
41
48
|
}
|
|
49
|
+
prevDeviceIdRef.current = deviceId;
|
|
42
50
|
}, [isEditing, deviceId]);
|
|
43
51
|
|
|
44
52
|
return (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import classes from './StoragePicker.module.scss';
|
|
3
3
|
import Select from '../Select/Select';
|
|
4
4
|
import { useGetStorages } from '../../../../services/storage';
|
|
@@ -70,11 +70,19 @@ const StoragePicker = ({ onUpdate, storagePath = '', storageId, disabled = false
|
|
|
70
70
|
}
|
|
71
71
|
}, [selectedStorage, path]);
|
|
72
72
|
|
|
73
|
+
// Reset the storage selection only when the device actually changes (not on mount/remount,
|
|
74
|
+
// e.g. when navigating between steps in the Add Plan form).
|
|
75
|
+
const prevDeviceIdRef = useRef<string | undefined | null>(null);
|
|
73
76
|
useEffect(() => {
|
|
74
|
-
if (
|
|
77
|
+
if (disabled) {
|
|
78
|
+
prevDeviceIdRef.current = deviceId;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (prevDeviceIdRef.current !== null && prevDeviceIdRef.current !== deviceId) {
|
|
75
82
|
setSelectedStorage(null);
|
|
76
83
|
setPath('');
|
|
77
84
|
}
|
|
85
|
+
prevDeviceIdRef.current = deviceId;
|
|
78
86
|
}, [deviceId, disabled]);
|
|
79
87
|
|
|
80
88
|
// console.log('Storage path :', path, !disabled && isLocalStorage && !path);
|
|
@@ -65,7 +65,7 @@ const PlanSingle = () => {
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const { isActive, method, title, description, stats, settings } = plan;
|
|
68
|
+
const { isActive, method, title, description, verified, stats, settings } = plan;
|
|
69
69
|
const prune = settings.prune;
|
|
70
70
|
const snapshotsCount = stats.snapshots?.length || 0;
|
|
71
71
|
const isSync = method === 'sync';
|
|
@@ -86,6 +86,11 @@ const PlanSingle = () => {
|
|
|
86
86
|
<Icon size={14} type={'pause'} color="#bf8d20" /> Paused
|
|
87
87
|
</span>
|
|
88
88
|
)}
|
|
89
|
+
{verified?.hasError && (
|
|
90
|
+
<span className={`label error ${classes.planError}`} onClick={() => setShowIntegrityModal(true)}>
|
|
91
|
+
<Icon size={14} type={'error-circle-filled'} color="#dd6b6b" /> Integrity Error
|
|
92
|
+
</span>
|
|
93
|
+
)}
|
|
89
94
|
</>
|
|
90
95
|
}
|
|
91
96
|
pageTitle={title}
|
package/src/styles/global.scss
CHANGED
|
@@ -114,6 +114,7 @@ export const getBackupEventActionMessage = (action: string, storageName?: string
|
|
|
114
114
|
PRE_BACKUP_START: 'Preparing Backup...',
|
|
115
115
|
PRE_BACKUP_DRY_RUN_START: 'Performing Dry Run...',
|
|
116
116
|
PRE_BACKUP_DRY_RUN_COMPLETE: 'Dry Run Complete',
|
|
117
|
+
PRE_BACKUP_DRY_RUN_ERROR: 'Dry Run Encountered Errors',
|
|
117
118
|
PRE_BACKUP_CHECKS_START: 'Running Checks...',
|
|
118
119
|
PRE_BACKUP_CHECKS_COMPLETE: 'Checks Complete',
|
|
119
120
|
PRE_BACKUP_SCRIPTS_START: 'Running Scripts...',
|