@plutonhq/core-frontend 0.1.24 → 0.1.26
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/backups.d.ts +1 -1
- package/dist-lib/@types/backups.d.ts.map +1 -1
- package/dist-lib/components/Plan/BackupEvents/BackupEvents.d.ts.map +1 -1
- package/dist-lib/components/Plan/BackupEvents/BackupEvents.js +27 -27
- package/dist-lib/components/Plan/BackupEvents/BackupEvents.js.map +1 -1
- package/dist-lib/components/Plan/Backups/Backups.d.ts.map +1 -1
- package/dist-lib/components/Plan/Backups/Backups.js +189 -159
- package/dist-lib/components/Plan/Backups/Backups.js.map +1 -1
- package/dist-lib/components/Plan/PlanPruneModal/PlanPruneModal.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanPruneModal/PlanPruneModal.js +62 -26
- package/dist-lib/components/Plan/PlanPruneModal/PlanPruneModal.js.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanPruneSettings.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanPruneSettings.js +138 -62
- package/dist-lib/components/Plan/PlanSettings/PlanPruneSettings.js.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanSettings.module.scss.js +42 -42
- package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewer.d.ts +32 -0
- package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewer.d.ts.map +1 -0
- package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewer.js +252 -0
- package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewer.js.map +1 -0
- package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewerFile.d.ts +23 -0
- package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewerFile.d.ts.map +1 -0
- package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewerFile.js +72 -0
- package/dist-lib/components/Plan/SnapshotViewer/SnapshotViewerFile.js.map +1 -0
- package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.d.ts.map +1 -1
- package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.js +188 -198
- package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.js.map +1 -1
- package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.module.scss.js +20 -64
- package/dist-lib/components/Restore/RestoreFileSelector/RestoreFileSelector.module.scss.js.map +1 -1
- package/dist-lib/components/Restore/RestoredFileBrowser/RestoredFileBrowser.d.ts.map +1 -1
- package/dist-lib/components/Restore/RestoredFileBrowser/RestoredFileBrowser.js +125 -159
- package/dist-lib/components/Restore/RestoredFileBrowser/RestoredFileBrowser.js.map +1 -1
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowser.module.scss.js +74 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowser.module.scss.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserDirectories.d.ts +17 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserDirectories.d.ts.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserDirectories.js +57 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserDirectories.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileList.d.ts +18 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileList.d.ts.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileList.js +24 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileList.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileRow.d.ts +18 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileRow.d.ts.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileRow.js +37 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserFileRow.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.d.ts +9 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.d.ts.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.js +15 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserToolbar.d.ts +11 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserToolbar.d.ts.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserToolbar.js +18 -0
- package/dist-lib/components/common/SnapshotBrowser/SnapshotBrowserToolbar.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.d.ts +12 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.d.ts.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.js +70 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.d.ts +22 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.d.ts.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.js +79 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotSort.d.ts +6 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotSort.d.ts.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotSort.js +18 -0
- package/dist-lib/components/common/SnapshotBrowser/hooks/useSnapshotSort.js.map +1 -0
- package/dist-lib/components/common/SnapshotBrowser/index.d.ts +11 -0
- package/dist-lib/components/common/SnapshotBrowser/index.d.ts.map +1 -0
- package/dist-lib/components/common/StatusLabel/StatusLabel.d.ts +1 -1
- package/dist-lib/components/common/StatusLabel/StatusLabel.d.ts.map +1 -1
- package/dist-lib/components/common/StatusLabel/StatusLabel.js +17 -12
- package/dist-lib/components/common/StatusLabel/StatusLabel.js.map +1 -1
- package/dist-lib/components/common/form/NumberInput/NumberInput.module.scss.js +4 -4
- package/dist-lib/components/index.d.ts +10 -0
- package/dist-lib/components/index.d.ts.map +1 -1
- package/dist-lib/components.js +152 -132
- package/dist-lib/components.js.map +1 -1
- package/dist-lib/hooks/usePlanSingleActions.d.ts.map +1 -1
- package/dist-lib/hooks/usePlanSingleActions.js +21 -21
- package/dist-lib/hooks/usePlanSingleActions.js.map +1 -1
- package/dist-lib/services/backups.d.ts +4 -0
- package/dist-lib/services/backups.d.ts.map +1 -1
- package/dist-lib/services/backups.js +34 -25
- package/dist-lib/services/backups.js.map +1 -1
- package/dist-lib/services.js +113 -112
- package/dist-lib/styles/core-frontend.css +1 -1
- package/dist-lib/utils/index.d.ts +1 -0
- package/dist-lib/utils/index.d.ts.map +1 -1
- package/dist-lib/utils/snapshotDatabase.d.ts +16 -0
- package/dist-lib/utils/snapshotDatabase.d.ts.map +1 -0
- package/dist-lib/utils/snapshotDatabase.js +105 -0
- package/dist-lib/utils/snapshotDatabase.js.map +1 -0
- package/dist-lib/utils.js +24 -22
- package/dist-lib/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/@types/backups.ts +1 -1
- package/src/components/Plan/BackupEvents/BackupEvents.tsx +5 -3
- package/src/components/Plan/Backups/Backups.tsx +40 -1
- package/src/components/Plan/PlanPruneModal/PlanPruneModal.tsx +54 -11
- package/src/components/Plan/PlanSettings/PlanPruneSettings.tsx +145 -61
- package/src/components/Plan/PlanSettings/PlanSettings.module.scss +5 -0
- package/src/components/Plan/SnapshotViewer/SnapshotViewer.tsx +344 -0
- package/src/components/Plan/SnapshotViewer/SnapshotViewerFile.tsx +89 -0
- package/src/components/Restore/RestoreFileSelector/RestoreFileSelector.tsx +82 -145
- package/src/components/Restore/RestoredFileBrowser/RestoredFileBrowser.tsx +71 -156
- package/src/components/common/SnapshotBrowser/SnapshotBrowser.module.scss +376 -0
- package/src/components/common/SnapshotBrowser/SnapshotBrowserDirectories.tsx +84 -0
- package/src/components/common/SnapshotBrowser/SnapshotBrowserFileList.tsx +52 -0
- package/src/components/common/SnapshotBrowser/SnapshotBrowserFileRow.tsx +44 -0
- package/src/components/common/SnapshotBrowser/SnapshotBrowserGoUpRow.tsx +22 -0
- package/src/components/common/SnapshotBrowser/SnapshotBrowserToolbar.tsx +29 -0
- package/src/components/common/SnapshotBrowser/hooks/useSnapshotDatabase.ts +130 -0
- package/src/components/common/SnapshotBrowser/hooks/useSnapshotNavigation.ts +154 -0
- package/src/components/common/SnapshotBrowser/hooks/useSnapshotSort.ts +24 -0
- package/src/components/common/SnapshotBrowser/index.ts +13 -0
- package/src/components/common/StatusLabel/StatusLabel.tsx +7 -1
- package/src/components/common/form/NumberInput/NumberInput.module.scss +1 -0
- package/src/components/index.ts +13 -0
- package/src/hooks/usePlanSingleActions.tsx +5 -3
- package/src/services/backups.ts +12 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/snapshotDatabase.ts +201 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FileItem } from '../@types/system';
|
|
2
|
+
declare class SnapshotDatabase {
|
|
3
|
+
private backupId;
|
|
4
|
+
private db;
|
|
5
|
+
private isInitialized;
|
|
6
|
+
constructor(backupId: string);
|
|
7
|
+
initialize(): Promise<void>;
|
|
8
|
+
initializeFromFiles(files: FileItem[]): Promise<void>;
|
|
9
|
+
getTopLevelDirectories(maxDepth?: number): Promise<string[]>;
|
|
10
|
+
hasSubdirectories(dirPath: string): Promise<boolean>;
|
|
11
|
+
getSubdirectories(parentPath: string): Promise<string[]>;
|
|
12
|
+
getDirectoryContents(dirPath: string, sortField?: string, sortDirection?: string, searchTerm?: string): Promise<FileItem[]>;
|
|
13
|
+
cleanup(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export default SnapshotDatabase;
|
|
16
|
+
//# sourceMappingURL=snapshotDatabase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotDatabase.d.ts","sourceRoot":"","sources":["../../src/utils/snapshotDatabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAoB5C,cAAM,gBAAgB;IAIP,OAAO,CAAC,QAAQ;IAH5B,OAAO,CAAC,EAAE,CAAa;IACvB,OAAO,CAAC,aAAa,CAAS;gBAEV,QAAQ,EAAE,MAAM;IAE9B,UAAU;IAaV,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE;IAyCrC,sBAAsB,CAAC,QAAQ,GAAE,MAAU,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAoB/D,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBpD,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAoBxD,oBAAoB,CACvB,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,MAAe,EAC1B,aAAa,GAAE,MAAc,EAC7B,UAAU,GAAE,MAAW,GACvB,OAAO,CAAC,QAAQ,EAAE,CAAC;IAkDhB,OAAO;CAKf;AAED,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
var f = Object.defineProperty;
|
|
2
|
+
var g = (c, i, e) => i in c ? f(c, i, { enumerable: !0, configurable: !0, writable: !0, value: e }) : c[i] = e;
|
|
3
|
+
var d = (c, i, e) => g(c, typeof i != "symbol" ? i + "" : i, e);
|
|
4
|
+
let h = null;
|
|
5
|
+
async function p() {
|
|
6
|
+
return h || (h = await import("dexie")), h;
|
|
7
|
+
}
|
|
8
|
+
class b {
|
|
9
|
+
constructor(i) {
|
|
10
|
+
d(this, "db", null);
|
|
11
|
+
d(this, "isInitialized", !1);
|
|
12
|
+
this.backupId = i;
|
|
13
|
+
}
|
|
14
|
+
async initialize() {
|
|
15
|
+
if (this.isInitialized) return;
|
|
16
|
+
const { Dexie: i } = await p();
|
|
17
|
+
this.db = new i(`SnapshotDB_${this.backupId}`), this.db.version(1).stores({
|
|
18
|
+
files: "++id, path, parentPath, fileName, isDirFlag, size, modifiedAt, depth"
|
|
19
|
+
}), this.isInitialized = !0;
|
|
20
|
+
}
|
|
21
|
+
async initializeFromFiles(i) {
|
|
22
|
+
await this.initialize(), await this.db.files.clear();
|
|
23
|
+
const e = 1e3, t = [];
|
|
24
|
+
for (let r = 0; r < i.length; r++) {
|
|
25
|
+
const s = i[r], l = s.path.lastIndexOf("/"), a = s.path.substring(0, l), n = (s.name || s.path.split("/").pop() || "").toLowerCase(), o = s.path.split("/").filter(Boolean).length, u = s.isDirectory ? 1 : 0;
|
|
26
|
+
t.push({
|
|
27
|
+
...s,
|
|
28
|
+
parentPath: a,
|
|
29
|
+
fileName: n,
|
|
30
|
+
depth: o,
|
|
31
|
+
isDirFlag: u
|
|
32
|
+
}), t.length >= e && (await this.db.files.bulkAdd(t), t.length = 0);
|
|
33
|
+
}
|
|
34
|
+
t.length > 0 && await this.db.files.bulkAdd(t), console.log("Database initialized with", await this.db.files.count(), "total records");
|
|
35
|
+
}
|
|
36
|
+
// Get only top-level directories (depth <= maxDepth)
|
|
37
|
+
async getTopLevelDirectories(i = 3) {
|
|
38
|
+
this.isInitialized || await this.initialize();
|
|
39
|
+
try {
|
|
40
|
+
const t = (await this.db.files.where("isDirFlag").equals(1).and((r) => r.depth <= i).toArray()).map((r) => r.path);
|
|
41
|
+
return console.log(`getTopLevelDirectories (depth <= ${i}) returning:`, t.length, "directories"), t;
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return console.error("Error getting top-level directories:", e), [];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Check if a directory has subdirectories
|
|
47
|
+
async hasSubdirectories(i) {
|
|
48
|
+
this.isInitialized || await this.initialize();
|
|
49
|
+
try {
|
|
50
|
+
return await this.db.files.where("parentPath").equals(i).and((t) => t.isDirFlag === 1).count() > 0;
|
|
51
|
+
} catch (e) {
|
|
52
|
+
return console.error("Error checking subdirectories:", e), !1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Get immediate subdirectories of a given directory
|
|
56
|
+
async getSubdirectories(i) {
|
|
57
|
+
this.isInitialized || await this.initialize();
|
|
58
|
+
try {
|
|
59
|
+
const t = (await this.db.files.where("parentPath").equals(i).and((r) => r.isDirFlag === 1).toArray()).map((r) => r.path);
|
|
60
|
+
return console.log(`getSubdirectories for ${i}:`, t.length, "subdirectories"), t;
|
|
61
|
+
} catch (e) {
|
|
62
|
+
return console.error("Error getting subdirectories:", e), [];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Get directory contents (files and folders in a specific directory)
|
|
66
|
+
async getDirectoryContents(i, e = "name", t = "asc", r = "") {
|
|
67
|
+
this.isInitialized || await this.initialize();
|
|
68
|
+
try {
|
|
69
|
+
let s;
|
|
70
|
+
if (r.trim()) {
|
|
71
|
+
const l = await this.db.files.where("parentPath").equals(i).toArray(), a = r.toLowerCase();
|
|
72
|
+
s = l.filter((n) => n.fileName.includes(a) || n.path.toLowerCase().includes(a));
|
|
73
|
+
} else
|
|
74
|
+
s = await this.db.files.where("parentPath").equals(i).toArray();
|
|
75
|
+
return s.sort((l, a) => {
|
|
76
|
+
if (l.isDirFlag && !a.isDirFlag) return -1;
|
|
77
|
+
if (!l.isDirFlag && a.isDirFlag) return 1;
|
|
78
|
+
let n, o;
|
|
79
|
+
switch (e) {
|
|
80
|
+
case "name":
|
|
81
|
+
n = l.fileName, o = a.fileName;
|
|
82
|
+
break;
|
|
83
|
+
case "modifiedAt":
|
|
84
|
+
n = new Date(l.modifiedAt).getTime(), o = new Date(a.modifiedAt).getTime();
|
|
85
|
+
break;
|
|
86
|
+
case "size":
|
|
87
|
+
n = l.size || 0, o = a.size || 0;
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
return n < o ? t === "asc" ? -1 : 1 : n > o ? t === "asc" ? 1 : -1 : 0;
|
|
93
|
+
}), s;
|
|
94
|
+
} catch (s) {
|
|
95
|
+
return console.error("Error getting directory contents:", s), [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async cleanup() {
|
|
99
|
+
this.db && await this.db.delete();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
b as default
|
|
104
|
+
};
|
|
105
|
+
//# sourceMappingURL=snapshotDatabase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotDatabase.js","sources":["../../src/utils/snapshotDatabase.ts"],"sourcesContent":["import { FileItem } from '../@types/system';\r\n\r\n// Lazy load Dexie only when needed\r\nlet DexieModule: any = null;\r\n\r\nasync function loadDexie() {\r\n if (!DexieModule) {\r\n DexieModule = await import('dexie');\r\n }\r\n return DexieModule;\r\n}\r\n\r\ninterface FileRecord extends FileItem {\r\n id?: number;\r\n parentPath: string;\r\n fileName: string;\r\n depth: number;\r\n isDirFlag: number; // 1 for directory, 0 for file\r\n}\r\n\r\nclass SnapshotDatabase {\r\n private db: any = null;\r\n private isInitialized = false;\r\n\r\n constructor(private backupId: string) {}\r\n\r\n async initialize() {\r\n if (this.isInitialized) return;\r\n\r\n const { Dexie } = await loadDexie();\r\n\r\n this.db = new Dexie(`SnapshotDB_${this.backupId}`);\r\n this.db.version(1).stores({\r\n files: '++id, path, parentPath, fileName, isDirFlag, size, modifiedAt, depth',\r\n });\r\n\r\n this.isInitialized = true;\r\n }\r\n\r\n async initializeFromFiles(files: FileItem[]) {\r\n await this.initialize();\r\n\r\n // Clear existing data\r\n await this.db.files.clear();\r\n\r\n // Process and insert files in batches\r\n const batchSize = 1000;\r\n const records: FileRecord[] = [];\r\n\r\n for (let i = 0; i < files.length; i++) {\r\n const file = files[i];\r\n const lastSlashIndex = file.path.lastIndexOf('/');\r\n const parentPath = file.path.substring(0, lastSlashIndex);\r\n const fileName = (file.name || file.path.split('/').pop() || '').toLowerCase();\r\n const depth = file.path.split('/').filter(Boolean).length;\r\n\r\n const isDirFlag = file.isDirectory ? 1 : 0;\r\n\r\n records.push({\r\n ...file,\r\n parentPath,\r\n fileName,\r\n depth,\r\n isDirFlag,\r\n });\r\n\r\n if (records.length >= batchSize) {\r\n await this.db.files.bulkAdd(records);\r\n records.length = 0;\r\n }\r\n }\r\n\r\n if (records.length > 0) {\r\n await this.db.files.bulkAdd(records);\r\n }\r\n\r\n console.log('Database initialized with', await this.db.files.count(), 'total records');\r\n }\r\n\r\n // Get only top-level directories (depth <= maxDepth)\r\n async getTopLevelDirectories(maxDepth: number = 3): Promise<string[]> {\r\n if (!this.isInitialized) await this.initialize();\r\n\r\n try {\r\n const dirRecords = await this.db.files\r\n .where('isDirFlag')\r\n .equals(1)\r\n .and((record: FileRecord) => record.depth <= maxDepth)\r\n .toArray();\r\n\r\n const paths = dirRecords.map((record: FileRecord) => record.path);\r\n console.log(`getTopLevelDirectories (depth <= ${maxDepth}) returning:`, paths.length, 'directories');\r\n return paths;\r\n } catch (error) {\r\n console.error('Error getting top-level directories:', error);\r\n return [];\r\n }\r\n }\r\n\r\n // Check if a directory has subdirectories\r\n async hasSubdirectories(dirPath: string): Promise<boolean> {\r\n if (!this.isInitialized) await this.initialize();\r\n\r\n try {\r\n const count = await this.db.files\r\n .where('parentPath')\r\n .equals(dirPath)\r\n .and((record: FileRecord) => record.isDirFlag === 1)\r\n .count();\r\n\r\n return count > 0;\r\n } catch (error) {\r\n console.error('Error checking subdirectories:', error);\r\n return false;\r\n }\r\n }\r\n\r\n // Get immediate subdirectories of a given directory\r\n async getSubdirectories(parentPath: string): Promise<string[]> {\r\n if (!this.isInitialized) await this.initialize();\r\n\r\n try {\r\n const subdirs = await this.db.files\r\n .where('parentPath')\r\n .equals(parentPath)\r\n .and((record: FileRecord) => record.isDirFlag === 1)\r\n .toArray();\r\n\r\n const paths = subdirs.map((record: FileRecord) => record.path);\r\n console.log(`getSubdirectories for ${parentPath}:`, paths.length, 'subdirectories');\r\n return paths;\r\n } catch (error) {\r\n console.error('Error getting subdirectories:', error);\r\n return [];\r\n }\r\n }\r\n\r\n // Get directory contents (files and folders in a specific directory)\r\n async getDirectoryContents(\r\n dirPath: string,\r\n sortField: string = 'name',\r\n sortDirection: string = 'asc',\r\n searchTerm: string = '',\r\n ): Promise<FileItem[]> {\r\n if (!this.isInitialized) await this.initialize();\r\n\r\n try {\r\n let results: FileRecord[];\r\n\r\n if (searchTerm.trim()) {\r\n const allInDir = await this.db.files.where('parentPath').equals(dirPath).toArray();\r\n const lowerSearch = searchTerm.toLowerCase();\r\n results = allInDir.filter((item: FileRecord) => item.fileName.includes(lowerSearch) || item.path.toLowerCase().includes(lowerSearch));\r\n } else {\r\n results = await this.db.files.where('parentPath').equals(dirPath).toArray();\r\n }\r\n\r\n // Sort results\r\n results.sort((a: FileRecord, b: FileRecord) => {\r\n // Directories first\r\n if (a.isDirFlag && !b.isDirFlag) return -1;\r\n if (!a.isDirFlag && b.isDirFlag) return 1;\r\n\r\n let aVal: any, bVal: any;\r\n switch (sortField) {\r\n case 'name':\r\n aVal = a.fileName;\r\n bVal = b.fileName;\r\n break;\r\n case 'modifiedAt':\r\n aVal = new Date(a.modifiedAt).getTime();\r\n bVal = new Date(b.modifiedAt).getTime();\r\n break;\r\n case 'size':\r\n aVal = a.size || 0;\r\n bVal = b.size || 0;\r\n break;\r\n default:\r\n return 0;\r\n }\r\n\r\n if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1;\r\n if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1;\r\n return 0;\r\n });\r\n\r\n return results;\r\n } catch (error) {\r\n console.error('Error getting directory contents:', error);\r\n return [];\r\n }\r\n }\r\n\r\n async cleanup() {\r\n if (this.db) {\r\n await this.db.delete();\r\n }\r\n }\r\n}\r\n\r\nexport default SnapshotDatabase;\r\n"],"names":["DexieModule","loadDexie","SnapshotDatabase","backupId","__publicField","Dexie","files","batchSize","records","i","file","lastSlashIndex","parentPath","fileName","depth","isDirFlag","maxDepth","paths","record","error","dirPath","sortField","sortDirection","searchTerm","results","allInDir","lowerSearch","item","a","b","aVal","bVal"],"mappings":"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"}
|
package/dist-lib/utils.js
CHANGED
|
@@ -1,48 +1,50 @@
|
|
|
1
|
-
import { API_URL as
|
|
2
|
-
import { calculateDirectorySizes as
|
|
1
|
+
import { API_URL as a, APP_NAME as r, DEFAULT_PLAN_SETTINGS as o, DEV_MODE as s } from "./utils/constants.js";
|
|
2
|
+
import { calculateDirectorySizes as n, clipPath as g, compareVersions as l, formatBytes as m, formatDateTime as p, formatDuration as c, formatNumberToK as P, formatPermissions as f, formatSeconds as A, getAvailableCliApps as d, getLogLevelName as D, getOSIcon as M, getProcessorIcon as S, getUpdateDocLink as E, isMobile as u, isServerEdition as x, isValidEmail as I, isValidURL as N, secondsToMinutes as h, shouldDisplayStorageField as v, sortFileItems as L, timeAgo as T } from "./utils/helpers.js";
|
|
3
3
|
import { isPlanSettingsValid as R, planIntervalAgeName as V, planIntervalName as _ } from "./utils/plans.js";
|
|
4
|
-
import { extractResticData as
|
|
4
|
+
import { extractResticData as U, generateBackupProgressMessage as k, generateMirrorProgressMessage as y, generateRestoreProgressMessage as B, getBackupEventActionMessage as z, getRestoreEventActionMessage as O } from "./utils/progressHelpers.js";
|
|
5
5
|
import { getParentPath as G, getPathSeparator as K, normalizePath as j, splitPath as q } from "./utils/restore.js";
|
|
6
6
|
import { getIconNameForFile as H } from "./utils/getIconNameforFile.js";
|
|
7
|
+
import { default as Q } from "./utils/snapshotDatabase.js";
|
|
7
8
|
export {
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
a as API_URL,
|
|
10
|
+
r as APP_NAME,
|
|
10
11
|
o as DEFAULT_PLAN_SETTINGS,
|
|
11
12
|
s as DEV_MODE,
|
|
12
|
-
|
|
13
|
-
n as
|
|
13
|
+
Q as SnapshotDatabase,
|
|
14
|
+
n as calculateDirectorySizes,
|
|
15
|
+
g as clipPath,
|
|
14
16
|
l as compareVersions,
|
|
15
|
-
|
|
17
|
+
U as extractResticData,
|
|
16
18
|
m as formatBytes,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
p as formatDateTime,
|
|
20
|
+
c as formatDuration,
|
|
19
21
|
P as formatNumberToK,
|
|
20
22
|
f as formatPermissions,
|
|
21
23
|
A as formatSeconds,
|
|
22
24
|
k as generateBackupProgressMessage,
|
|
23
25
|
y as generateMirrorProgressMessage,
|
|
24
26
|
B as generateRestoreProgressMessage,
|
|
25
|
-
|
|
27
|
+
d as getAvailableCliApps,
|
|
26
28
|
z as getBackupEventActionMessage,
|
|
27
29
|
H as getIconNameForFile,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
D as getLogLevelName,
|
|
31
|
+
M as getOSIcon,
|
|
30
32
|
G as getParentPath,
|
|
31
33
|
K as getPathSeparator,
|
|
32
|
-
|
|
34
|
+
S as getProcessorIcon,
|
|
33
35
|
O as getRestoreEventActionMessage,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
E as getUpdateDocLink,
|
|
37
|
+
u as isMobile,
|
|
36
38
|
R as isPlanSettingsValid,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
x as isServerEdition,
|
|
40
|
+
I as isValidEmail,
|
|
41
|
+
N as isValidURL,
|
|
40
42
|
j as normalizePath,
|
|
41
43
|
V as planIntervalAgeName,
|
|
42
44
|
_ as planIntervalName,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
h as secondsToMinutes,
|
|
46
|
+
v as shouldDisplayStorageField,
|
|
47
|
+
L as sortFileItems,
|
|
46
48
|
q as splitPath,
|
|
47
49
|
T as timeAgo
|
|
48
50
|
};
|
package/dist-lib/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
|
package/package.json
CHANGED
package/src/@types/backups.ts
CHANGED
|
@@ -55,7 +55,7 @@ export type Backup = {
|
|
|
55
55
|
started: number;
|
|
56
56
|
ended: number;
|
|
57
57
|
duration: number;
|
|
58
|
-
status: 'completed' | 'cancelled' | 'failed' | 'started';
|
|
58
|
+
status: 'completed' | 'cancelled' | 'failed' | 'started' | 'retrying' | 'initializing';
|
|
59
59
|
inProgress: boolean;
|
|
60
60
|
totalFiles: number;
|
|
61
61
|
totalSize: number;
|
|
@@ -57,9 +57,11 @@ const BackupEvents = ({ id, type = 'backup', sourceId, sourceType, planId, inPro
|
|
|
57
57
|
<Icon type={inProgress ? 'loading' : hasFinished === 'Completed' ? 'check-circle-filled' : 'error-circle-filled'} size={14} />{' '}
|
|
58
58
|
{inProgress ? `${type} In Progress` : `${type} ${hasFinished}` || 'Unknown'}
|
|
59
59
|
</div>
|
|
60
|
-
|
|
61
|
-
<
|
|
62
|
-
|
|
60
|
+
{!inProgress && hasFinished && (
|
|
61
|
+
<div title="Duration" className={classes.duration}>
|
|
62
|
+
<Icon type="clock" size={14} /> {(progressDataToUse?.duration && formatDuration(progressDataToUse.duration / 1000)) || 'N/A'}
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
63
65
|
</div>
|
|
64
66
|
{isLoading && (
|
|
65
67
|
<div className={classes.loading}>
|
|
@@ -6,7 +6,14 @@ import { formatBytes, formatDateTime, formatDuration, formatNumberToK, timeAgo }
|
|
|
6
6
|
import Icon from '../../common/Icon/Icon';
|
|
7
7
|
import classes from './Backups.module.scss';
|
|
8
8
|
import ActionModal from '../../common/ActionModal/ActionModal';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
useCancelBackupDownload,
|
|
11
|
+
useDeleteBackup,
|
|
12
|
+
useDownloadBackup,
|
|
13
|
+
useGetBackupDownload,
|
|
14
|
+
useUpdateBackup,
|
|
15
|
+
useBrowseSnapshot,
|
|
16
|
+
} from '../../../services/backups';
|
|
10
17
|
import { useQueryClient } from '@tanstack/react-query';
|
|
11
18
|
import RestoreWizard from '../../Restore/RestoreWizard/RestoreWizard';
|
|
12
19
|
import StatusLabel from '../../common/StatusLabel/StatusLabel';
|
|
@@ -14,6 +21,7 @@ import BackupEvents from '../BackupEvents/BackupEvents';
|
|
|
14
21
|
import Input from '../../common/form/Input/Input';
|
|
15
22
|
import MirrorStatusBadge from '../Mirrors/MirrorStatusBadge';
|
|
16
23
|
import MirrorStorageSelectorModal from '../Mirrors/MirrorStorageSelectorModal';
|
|
24
|
+
import { SidePanel, SnapshotViewer } from '../..';
|
|
17
25
|
|
|
18
26
|
const DownloadLabel = ({ download, downloadBackup }: { download: Backup['download']; downloadBackup: () => void }) => {
|
|
19
27
|
if (download?.status === 'started') {
|
|
@@ -92,6 +100,7 @@ const Backups = ({
|
|
|
92
100
|
const [showDeleteModal, setShowDeleteModal] = useState<Backup | false>(false);
|
|
93
101
|
const [showRestoreModal, setShowRestoreModal] = useState<Backup | false>(false);
|
|
94
102
|
const [showBackupEvents, setShowBackupEvents] = useState<false | string>(false);
|
|
103
|
+
const [showSnapshotViewer, setShowSnapshotViewer] = useState<Backup | false>(false);
|
|
95
104
|
const [showEditModal, setShowEditModal] = useState<Backup | false>(false);
|
|
96
105
|
const [showStorageSelector, setShowStorageSelector] = useState<Backup | false>(false);
|
|
97
106
|
const queryClient = useQueryClient();
|
|
@@ -100,8 +109,11 @@ const Backups = ({
|
|
|
100
109
|
const downloadBackupMutation = useDownloadBackup();
|
|
101
110
|
const cancelDownloadMutation = useCancelBackupDownload();
|
|
102
111
|
const getDownloadMutation = useGetBackupDownload();
|
|
112
|
+
const browseSnapshotMutation = useBrowseSnapshot();
|
|
103
113
|
const isSync = method === 'sync';
|
|
104
114
|
|
|
115
|
+
const snapshotFiles = browseSnapshotMutation.data?.result;
|
|
116
|
+
|
|
105
117
|
console.log('backups :', backups);
|
|
106
118
|
|
|
107
119
|
const removeBackup = (backup: Backup) => {
|
|
@@ -289,6 +301,20 @@ const Backups = ({
|
|
|
289
301
|
<Icon type={isDownloading ? 'close' : 'download'} size={14} /> {isDownloading ? 'Cancel Download' : 'Download'}
|
|
290
302
|
</button>
|
|
291
303
|
)}
|
|
304
|
+
{!isSync && status === 'completed' && active && (
|
|
305
|
+
<button
|
|
306
|
+
onClick={() => {
|
|
307
|
+
toast.promise(browseSnapshotMutation.mutateAsync({ backupId: id }), {
|
|
308
|
+
pending: 'Getting Snapshot Data...',
|
|
309
|
+
error: 'Failed to Get Snapshot Data.',
|
|
310
|
+
});
|
|
311
|
+
setShowSnapOptions(false);
|
|
312
|
+
setShowSnapshotViewer(snapshot);
|
|
313
|
+
}}
|
|
314
|
+
>
|
|
315
|
+
<Icon type="folders" size={14} /> Browse
|
|
316
|
+
</button>
|
|
317
|
+
)}
|
|
292
318
|
{status === 'completed' && active && (
|
|
293
319
|
<button
|
|
294
320
|
className={isDownloading ? 'notAllowed' : ''}
|
|
@@ -414,6 +440,19 @@ const Backups = ({
|
|
|
414
440
|
onClose={() => setShowStorageSelector(false)}
|
|
415
441
|
/>
|
|
416
442
|
)}
|
|
443
|
+
{showSnapshotViewer && showSnapshotViewer.id && browseSnapshotMutation.isSuccess && (
|
|
444
|
+
<SidePanel width="100%" title="Browse Snapshot" icon={<Icon type={'folders'} size={20} />} close={() => setShowSnapshotViewer(false)}>
|
|
445
|
+
<SnapshotViewer
|
|
446
|
+
files={snapshotFiles || []}
|
|
447
|
+
backup={showSnapshotViewer}
|
|
448
|
+
planId={planId}
|
|
449
|
+
isSync={isSync}
|
|
450
|
+
onClose={() => setShowSnapshotViewer(false)}
|
|
451
|
+
mirrors={replicationSettings?.enabled ? showSnapshotViewer.mirrors : undefined}
|
|
452
|
+
primaryStorage={replicationSettings?.enabled ? storage : undefined}
|
|
453
|
+
/>
|
|
454
|
+
</SidePanel>
|
|
455
|
+
)}
|
|
417
456
|
</div>
|
|
418
457
|
);
|
|
419
458
|
};
|
|
@@ -15,6 +15,56 @@ interface PlanPruneModalProps {
|
|
|
15
15
|
const PlanPruneModal = ({ planId, method, prune, snapshotsCount, taskPending, close }: PlanPruneModalProps) => {
|
|
16
16
|
const pruneMutation = usePrunePlan();
|
|
17
17
|
|
|
18
|
+
const getBackupMessage = () => {
|
|
19
|
+
switch (prune.policy) {
|
|
20
|
+
case 'keepLast':
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
Your plan is set to keep the last {prune.snapCount} backups.{' '}
|
|
24
|
+
{prune.snapCount < snapshotsCount
|
|
25
|
+
? `Running prune now will remove ${snapshotsCount - prune.snapCount} older snapshots, freeing up some storage space.`
|
|
26
|
+
: 'There are no excess snapshots to clean up.'}
|
|
27
|
+
</>
|
|
28
|
+
);
|
|
29
|
+
case 'forgetByAge':
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
Your plan is set to remove backups older than {planIntervalAgeName(prune.forgetAge || '3m')}, while always keeping at least{' '}
|
|
33
|
+
{prune.snapCount || 1} backup{(prune.snapCount || 1) > 1 ? 's' : ''}. This action will remove any backups that exceed the retention
|
|
34
|
+
policy.
|
|
35
|
+
</>
|
|
36
|
+
);
|
|
37
|
+
case 'custom':
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
Your plan uses an advanced retention policy
|
|
41
|
+
{prune.keepDailySnaps ? `, keeping daily backups for ${prune.keepDailySnaps} days` : ''}
|
|
42
|
+
{prune.keepWeeklySnaps ? `, weekly backups for ${prune.keepWeeklySnaps} weeks` : ''}
|
|
43
|
+
{prune.keepMonthlySnaps ? `, monthly backups for ${prune.keepMonthlySnaps} months` : ''}, while always keeping at least{' '}
|
|
44
|
+
{prune.snapCount || 1} backup{(prune.snapCount || 1) > 1 ? 's' : ''}. This action will remove any backups that exceed the retention
|
|
45
|
+
policy.
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
case 'disable':
|
|
49
|
+
return <>Pruning is disabled for this plan. No backups will be removed.</>;
|
|
50
|
+
default:
|
|
51
|
+
return (
|
|
52
|
+
<>
|
|
53
|
+
Your plan is set to maintain {prune.snapCount} recent backups.{' '}
|
|
54
|
+
{prune.snapCount < snapshotsCount
|
|
55
|
+
? `Running prune now will remove ${snapshotsCount - prune.snapCount} older snapshots, freeing up some storage space.`
|
|
56
|
+
: 'There are no excess snapshots to clean up.'}
|
|
57
|
+
</>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const canPrune = () => {
|
|
63
|
+
if (prune.policy === 'disable') return false;
|
|
64
|
+
if (prune.policy === 'keepLast' && prune.snapCount >= snapshotsCount) return false;
|
|
65
|
+
return true;
|
|
66
|
+
};
|
|
67
|
+
|
|
18
68
|
return (
|
|
19
69
|
<ActionModal
|
|
20
70
|
title={method === 'sync' ? 'Clean Up Old Revisions' : 'Clean Up Old Backups'}
|
|
@@ -23,25 +73,18 @@ const PlanPruneModal = ({ planId, method, prune, snapshotsCount, taskPending, cl
|
|
|
23
73
|
{method === 'sync' ? (
|
|
24
74
|
<>
|
|
25
75
|
Your plan is set to maintain{' '}
|
|
26
|
-
{prune.policy === 'forgetByAge' ? `file revisions newer than ${planIntervalAgeName(prune.forgetAge || '3m')}.` : ''}
|
|
27
|
-
|
|
28
|
-
remove any excess file revisions older than the retention policy.
|
|
76
|
+
{prune.policy === 'forgetByAge' ? `file revisions newer than ${planIntervalAgeName(prune.forgetAge || '3m')}.` : ''} This action
|
|
77
|
+
will remove any excess file revisions older than the retention policy.
|
|
29
78
|
</>
|
|
30
79
|
) : (
|
|
31
|
-
|
|
32
|
-
Your plan is set to maintain {prune.snapCount} recent backups(snapshots).{' '}
|
|
33
|
-
{prune.snapCount < snapshotsCount
|
|
34
|
-
? `Running prune now will remove ${' '}
|
|
35
|
-
${snapshotsCount - prune.snapCount} older snapshots, freeing up some storage space`
|
|
36
|
-
: 'There are no excess snapshots to clean up.'}
|
|
37
|
-
</>
|
|
80
|
+
getBackupMessage()
|
|
38
81
|
)}
|
|
39
82
|
</>
|
|
40
83
|
}
|
|
41
84
|
closeModal={() => !pruneMutation.isPending && close()}
|
|
42
85
|
width="500px"
|
|
43
86
|
primaryAction={{
|
|
44
|
-
title: method === 'sync' ?
|
|
87
|
+
title: method === 'sync' ? 'Yes, Remove Old Revisions' : canPrune() ? 'Yes, Remove Old Backups' : '',
|
|
45
88
|
type: 'default',
|
|
46
89
|
isPending: pruneMutation.isPending,
|
|
47
90
|
action: () =>
|