@filen/sync 0.1.1 → 0.1.4
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/LICENSE +661 -661
- package/README.md +1 -1
- package/SECURITY.md +17 -17
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +51 -1
- package/dist/constants.js.map +1 -1
- package/dist/ignorer.d.ts +14 -0
- package/dist/ignorer.js +69 -0
- package/dist/ignorer.js.map +1 -0
- package/dist/index.d.ts +31 -17
- package/dist/index.js +125 -24
- package/dist/index.js.map +1 -1
- package/dist/lib/deltas.d.ts +9 -31
- package/dist/lib/deltas.js +194 -99
- package/dist/lib/deltas.js.map +1 -1
- package/dist/lib/filesystems/local.d.ts +32 -43
- package/dist/lib/filesystems/local.js +383 -106
- package/dist/lib/filesystems/local.js.map +1 -1
- package/dist/lib/filesystems/remote.d.ts +21 -7
- package/dist/lib/filesystems/remote.js +531 -67
- package/dist/lib/filesystems/remote.js.map +1 -1
- package/dist/lib/ipc.d.ts +9 -0
- package/dist/lib/ipc.js +60 -0
- package/dist/lib/ipc.js.map +1 -0
- package/dist/lib/lock.d.ts +13 -0
- package/dist/lib/lock.js +73 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/logger.d.ts +14 -0
- package/dist/lib/logger.js +93 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/state.d.ts +4 -15
- package/dist/lib/state.js +106 -87
- package/dist/lib/state.js.map +1 -1
- package/dist/lib/sync.d.ts +30 -10
- package/dist/lib/sync.js +363 -36
- package/dist/lib/sync.js.map +1 -1
- package/dist/lib/tasks.d.ts +27 -40
- package/dist/lib/tasks.js +397 -48
- package/dist/lib/tasks.js.map +1 -1
- package/dist/types.d.ts +340 -0
- package/dist/utils.d.ts +42 -0
- package/dist/utils.js +164 -1
- package/dist/utils.js.map +1 -1
- package/index.d.ts +6 -295
- package/jest.config.js +5 -0
- package/package.json +62 -58
- package/tests/utils.test.ts +33 -0
- package/tsconfig.json +24 -24
|
@@ -4,43 +4,286 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.RemoteFileSystem = void 0;
|
|
7
|
+
const sdk_1 = require("@filen/sdk");
|
|
7
8
|
const path_1 = __importDefault(require("path"));
|
|
8
9
|
const semaphore_1 = require("../../semaphore");
|
|
9
10
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const ipc_1 = require("../ipc");
|
|
12
|
+
const utils_1 = require("../../utils");
|
|
13
|
+
const uuid_1 = require("uuid");
|
|
14
|
+
const constants_1 = require("../../constants");
|
|
10
15
|
class RemoteFileSystem {
|
|
11
|
-
constructor(
|
|
16
|
+
constructor(sync) {
|
|
12
17
|
this.getDirectoryTreeCache = {
|
|
13
18
|
timestamp: 0,
|
|
14
19
|
tree: {},
|
|
15
20
|
uuids: {}
|
|
16
21
|
};
|
|
22
|
+
this.previousTreeRawResponse = "";
|
|
17
23
|
this.mutex = new semaphore_1.Semaphore(1);
|
|
18
24
|
this.mkdirMutex = new semaphore_1.Semaphore(1);
|
|
25
|
+
this.itemsMutex = new semaphore_1.Semaphore(1);
|
|
26
|
+
this.deviceIdCache = "";
|
|
19
27
|
this.sync = sync;
|
|
20
28
|
}
|
|
21
|
-
async
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
async getDeviceId() {
|
|
30
|
+
if (this.deviceIdCache.length > 0) {
|
|
31
|
+
return this.deviceIdCache;
|
|
32
|
+
}
|
|
33
|
+
const deviceIdFile = path_1.default.join(this.sync.dbPath, this.sync.syncPair.uuid, "deviceId");
|
|
34
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(deviceIdFile));
|
|
35
|
+
let deviceId = "";
|
|
36
|
+
if (!(await fs_extra_1.default.exists(deviceIdFile))) {
|
|
37
|
+
deviceId = (0, uuid_1.v4)();
|
|
38
|
+
await fs_extra_1.default.writeFile(deviceIdFile, deviceId, {
|
|
39
|
+
encoding: "utf-8"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
deviceId = await fs_extra_1.default.readFile(deviceIdFile, {
|
|
44
|
+
encoding: "utf-8"
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
this.deviceIdCache = deviceId;
|
|
48
|
+
return deviceId;
|
|
49
|
+
}
|
|
50
|
+
async getDirectoryTree(skipCache = false) {
|
|
51
|
+
var _a;
|
|
52
|
+
const deviceId = await this.getDeviceId();
|
|
53
|
+
const dir = await this.sync.sdk.api(3).dir().tree({
|
|
54
|
+
uuid: this.sync.syncPair.remoteParentUUID,
|
|
55
|
+
deviceId,
|
|
56
|
+
skipCache,
|
|
57
|
+
includeRaw: true
|
|
58
|
+
});
|
|
59
|
+
// Data did not change, use cache
|
|
60
|
+
if (dir.files.length === 0 && dir.folders.length === 0) {
|
|
61
|
+
if (this.getDirectoryTreeCache.timestamp === 0) {
|
|
62
|
+
// Re-run but with API caching disabled since our internal client cache is empty
|
|
63
|
+
return await this.getDirectoryTree(true);
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
result: this.getDirectoryTreeCache,
|
|
67
|
+
ignored: [],
|
|
68
|
+
changed: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// eslint-disable-next-line quotes
|
|
72
|
+
const rawEx = dir.raw.split('"randomBytes"');
|
|
73
|
+
// Compare API response with the previous dataset, this way we can save time computing the tree if it's the same
|
|
74
|
+
if (rawEx.length === 2 && this.previousTreeRawResponse === rawEx[0] && this.getDirectoryTreeCache.timestamp !== 0) {
|
|
75
|
+
return {
|
|
76
|
+
result: this.getDirectoryTreeCache,
|
|
77
|
+
ignored: [],
|
|
78
|
+
changed: false
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const baseFolder = dir.folders[0];
|
|
82
|
+
if (!baseFolder) {
|
|
83
|
+
throw new Error("Could not get base folder.");
|
|
84
|
+
}
|
|
85
|
+
const baseFolderParent = baseFolder[2];
|
|
86
|
+
if (baseFolderParent !== "base") {
|
|
87
|
+
throw new Error("Invalid base folder parent.");
|
|
88
|
+
}
|
|
89
|
+
const folderNames = { base: "/" };
|
|
24
90
|
const tree = {};
|
|
25
|
-
const dir = await this.sync.sdk.cloud().getDirectoryTree({ uuid: this.sync.syncPair.remoteParentUUID });
|
|
26
91
|
const uuids = {};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
92
|
+
const pathsAdded = {};
|
|
93
|
+
const ignored = [];
|
|
94
|
+
for (const folder of dir.folders) {
|
|
95
|
+
try {
|
|
96
|
+
const decrypted = await this.sync.sdk.crypto().decrypt().folderMetadata({ metadata: folder[1] });
|
|
97
|
+
if (folder[2] !== "base" && decrypted.name.length === 0) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const parentPath = folder[2] === "base" ? "" : `${folderNames[folder[2]]}/`;
|
|
101
|
+
const folderPath = folder[2] === "base" ? "" : `${parentPath}${decrypted.name}`;
|
|
102
|
+
const localPath = path_1.default.join(this.sync.syncPair.localPath, folderPath);
|
|
103
|
+
folderNames[folder[0]] = folderPath;
|
|
104
|
+
if (folderPath.startsWith(constants_1.LOCAL_TRASH_NAME) || decrypted.name.startsWith(constants_1.LOCAL_TRASH_NAME)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (this.sync.remoteIgnorer.ignores(folderPath)) {
|
|
108
|
+
ignored.push({
|
|
109
|
+
localPath,
|
|
110
|
+
relativePath: folderPath,
|
|
111
|
+
reason: "remoteIgnore"
|
|
112
|
+
});
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (this.sync.excludeDotFiles && (0, utils_1.pathIncludesDotFile)(folderPath)) {
|
|
116
|
+
ignored.push({
|
|
117
|
+
localPath,
|
|
118
|
+
relativePath: folderPath,
|
|
119
|
+
reason: "dotFile"
|
|
120
|
+
});
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if ((0, utils_1.isPathOverMaxLength)(localPath)) {
|
|
124
|
+
ignored.push({
|
|
125
|
+
localPath,
|
|
126
|
+
relativePath: folderPath,
|
|
127
|
+
reason: "pathLength"
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if ((0, utils_1.isNameOverMaxLength)(decrypted.name)) {
|
|
132
|
+
ignored.push({
|
|
133
|
+
localPath,
|
|
134
|
+
relativePath: folderPath,
|
|
135
|
+
reason: "nameLength"
|
|
136
|
+
});
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (!(0, utils_1.isValidPath)(localPath)) {
|
|
140
|
+
ignored.push({
|
|
141
|
+
localPath,
|
|
142
|
+
relativePath: folderPath,
|
|
143
|
+
reason: "invalidPath"
|
|
144
|
+
});
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if ((0, utils_1.isDirectoryPathIgnoredByDefault)(folderPath) || (0, utils_1.isRelativePathIgnoredByDefault)(folderPath)) {
|
|
148
|
+
ignored.push({
|
|
149
|
+
localPath,
|
|
150
|
+
relativePath: folderPath,
|
|
151
|
+
reason: "defaultIgnore"
|
|
152
|
+
});
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const lowercasePath = folderPath.toLowerCase();
|
|
156
|
+
if (pathsAdded[lowercasePath]) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
pathsAdded[lowercasePath] = true;
|
|
160
|
+
const item = {
|
|
161
|
+
type: "directory",
|
|
162
|
+
uuid: folder[0],
|
|
163
|
+
name: decrypted.name,
|
|
164
|
+
size: 0,
|
|
165
|
+
path: folderPath
|
|
166
|
+
};
|
|
167
|
+
tree[folderPath] = item;
|
|
168
|
+
uuids[folder[0]] = item;
|
|
30
169
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const treeItem = tree[path];
|
|
34
|
-
if (treeItem) {
|
|
35
|
-
uuids[treeItem.uuid] = item;
|
|
170
|
+
catch (e) {
|
|
171
|
+
this.sync.worker.logger.log("error", e, "filesystems.remote.getDirectoryTree");
|
|
36
172
|
}
|
|
37
173
|
}
|
|
174
|
+
if (Object.keys(folderNames).length === 0) {
|
|
175
|
+
throw new Error("Could not build directory tree.");
|
|
176
|
+
}
|
|
177
|
+
await (0, utils_1.promiseAllSettledChunked)(dir.files.map(async (file) => {
|
|
178
|
+
try {
|
|
179
|
+
const decrypted = await this.sync.sdk.crypto().decrypt().fileMetadata({ metadata: file[5] });
|
|
180
|
+
if (decrypted.name.length === 0) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const parentPath = folderNames[file[4]];
|
|
184
|
+
const filePath = `${parentPath}/${decrypted.name}`;
|
|
185
|
+
const localPath = path_1.default.join(this.sync.syncPair.localPath, filePath);
|
|
186
|
+
if (filePath.startsWith(constants_1.LOCAL_TRASH_NAME) || decrypted.name.startsWith(constants_1.LOCAL_TRASH_NAME)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (decrypted.size <= 0) {
|
|
190
|
+
ignored.push({
|
|
191
|
+
localPath,
|
|
192
|
+
relativePath: filePath,
|
|
193
|
+
reason: "empty"
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (this.sync.remoteIgnorer.ignores(filePath)) {
|
|
198
|
+
ignored.push({
|
|
199
|
+
localPath,
|
|
200
|
+
relativePath: filePath,
|
|
201
|
+
reason: "remoteIgnore"
|
|
202
|
+
});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (this.sync.excludeDotFiles && (0, utils_1.pathIncludesDotFile)(filePath)) {
|
|
206
|
+
ignored.push({
|
|
207
|
+
localPath,
|
|
208
|
+
relativePath: filePath,
|
|
209
|
+
reason: "dotFile"
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if ((0, utils_1.isPathOverMaxLength)(localPath)) {
|
|
214
|
+
ignored.push({
|
|
215
|
+
localPath,
|
|
216
|
+
relativePath: filePath,
|
|
217
|
+
reason: "pathLength"
|
|
218
|
+
});
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if ((0, utils_1.isNameOverMaxLength)(decrypted.name)) {
|
|
222
|
+
ignored.push({
|
|
223
|
+
localPath,
|
|
224
|
+
relativePath: filePath,
|
|
225
|
+
reason: "nameLength"
|
|
226
|
+
});
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (!(0, utils_1.isValidPath)(localPath)) {
|
|
230
|
+
ignored.push({
|
|
231
|
+
localPath,
|
|
232
|
+
relativePath: filePath,
|
|
233
|
+
reason: "invalidPath"
|
|
234
|
+
});
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if ((0, utils_1.isDirectoryPathIgnoredByDefault)(filePath) || (0, utils_1.isRelativePathIgnoredByDefault)(filePath)) {
|
|
238
|
+
ignored.push({
|
|
239
|
+
localPath,
|
|
240
|
+
relativePath: filePath,
|
|
241
|
+
reason: "defaultIgnore"
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const lowercasePath = filePath.toLowerCase();
|
|
246
|
+
if (pathsAdded[lowercasePath]) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
pathsAdded[lowercasePath] = true;
|
|
250
|
+
const item = {
|
|
251
|
+
type: "file",
|
|
252
|
+
uuid: file[0],
|
|
253
|
+
name: decrypted.name,
|
|
254
|
+
size: decrypted.size,
|
|
255
|
+
mime: decrypted.mime,
|
|
256
|
+
lastModified: (0, utils_1.convertTimestampToMs)(decrypted.lastModified),
|
|
257
|
+
version: file[6],
|
|
258
|
+
chunks: file[3],
|
|
259
|
+
key: decrypted.key,
|
|
260
|
+
bucket: file[1],
|
|
261
|
+
region: file[2],
|
|
262
|
+
creation: decrypted.creation,
|
|
263
|
+
hash: decrypted.hash,
|
|
264
|
+
path: filePath
|
|
265
|
+
};
|
|
266
|
+
tree[filePath] = item;
|
|
267
|
+
uuids[item.uuid] = item;
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
this.sync.worker.logger.log("error", e, "filesystems.remote.getDirectoryTree");
|
|
271
|
+
}
|
|
272
|
+
}));
|
|
273
|
+
this.previousTreeRawResponse = rawEx.length === 2 ? (_a = rawEx[0]) !== null && _a !== void 0 ? _a : "" : "";
|
|
38
274
|
this.getDirectoryTreeCache = {
|
|
39
275
|
timestamp: Date.now(),
|
|
40
276
|
tree,
|
|
41
277
|
uuids
|
|
42
278
|
};
|
|
43
|
-
return {
|
|
279
|
+
return {
|
|
280
|
+
result: {
|
|
281
|
+
tree: this.getDirectoryTreeCache.tree,
|
|
282
|
+
uuids: this.getDirectoryTreeCache.uuids
|
|
283
|
+
},
|
|
284
|
+
ignored,
|
|
285
|
+
changed: true
|
|
286
|
+
};
|
|
44
287
|
}
|
|
45
288
|
/**
|
|
46
289
|
* Find the corresponding UUID of the relative path.
|
|
@@ -90,6 +333,7 @@ class RemoteFileSystem {
|
|
|
90
333
|
const basename = path_1.default.posix.basename(relativePath);
|
|
91
334
|
if (parentPath === "/" || parentPath === "." || parentPath.length <= 0) {
|
|
92
335
|
const uuid = await this.sync.sdk.cloud().createDirectory({ name: basename, parent: this.sync.syncPair.remoteParentUUID });
|
|
336
|
+
await this.itemsMutex.acquire();
|
|
93
337
|
this.getDirectoryTreeCache.tree[relativePath] = {
|
|
94
338
|
type: "directory",
|
|
95
339
|
uuid,
|
|
@@ -97,6 +341,14 @@ class RemoteFileSystem {
|
|
|
97
341
|
size: 0,
|
|
98
342
|
path: relativePath
|
|
99
343
|
};
|
|
344
|
+
this.getDirectoryTreeCache.uuids[uuid] = {
|
|
345
|
+
type: "directory",
|
|
346
|
+
uuid,
|
|
347
|
+
name: basename,
|
|
348
|
+
size: 0,
|
|
349
|
+
path: relativePath
|
|
350
|
+
};
|
|
351
|
+
this.itemsMutex.release();
|
|
100
352
|
return uuid;
|
|
101
353
|
}
|
|
102
354
|
const pathEx = relativePath.split("/");
|
|
@@ -116,6 +368,7 @@ class RemoteFileSystem {
|
|
|
116
368
|
const parentIsBase = partParentPath === "/" || partParentPath === "." || partParentPath === "";
|
|
117
369
|
const parentUUID = parentIsBase ? this.sync.syncPair.remoteParentUUID : parentItem.uuid;
|
|
118
370
|
const uuid = await this.sync.sdk.cloud().createDirectory({ name: partBasename, parent: parentUUID });
|
|
371
|
+
await this.itemsMutex.acquire();
|
|
119
372
|
this.getDirectoryTreeCache.tree[relativePath] = {
|
|
120
373
|
type: "directory",
|
|
121
374
|
uuid,
|
|
@@ -123,6 +376,14 @@ class RemoteFileSystem {
|
|
|
123
376
|
size: 0,
|
|
124
377
|
path: relativePath
|
|
125
378
|
};
|
|
379
|
+
this.getDirectoryTreeCache.uuids[uuid] = {
|
|
380
|
+
type: "directory",
|
|
381
|
+
uuid,
|
|
382
|
+
name: partBasename,
|
|
383
|
+
size: 0,
|
|
384
|
+
path: relativePath
|
|
385
|
+
};
|
|
386
|
+
this.itemsMutex.release();
|
|
126
387
|
}
|
|
127
388
|
}
|
|
128
389
|
if (!this.getDirectoryTreeCache.tree[relativePath]) {
|
|
@@ -147,17 +408,37 @@ class RemoteFileSystem {
|
|
|
147
408
|
* @returns {Promise<void>}
|
|
148
409
|
*/
|
|
149
410
|
async unlink({ relativePath, type, permanent = false }) {
|
|
411
|
+
let uuid = null;
|
|
412
|
+
const cleanItemEntry = async () => {
|
|
413
|
+
if (!uuid) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
await this.itemsMutex.acquire();
|
|
417
|
+
delete this.getDirectoryTreeCache.tree[relativePath];
|
|
418
|
+
delete this.getDirectoryTreeCache.uuids[uuid];
|
|
419
|
+
for (const entry in this.getDirectoryTreeCache.tree) {
|
|
420
|
+
if (entry.startsWith(relativePath + "/") || entry === relativePath) {
|
|
421
|
+
const entryItem = this.getDirectoryTreeCache.tree[entry];
|
|
422
|
+
if (entryItem) {
|
|
423
|
+
delete this.getDirectoryTreeCache.uuids[entryItem.uuid];
|
|
424
|
+
}
|
|
425
|
+
delete this.getDirectoryTreeCache.tree[entry];
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
this.itemsMutex.release();
|
|
429
|
+
};
|
|
150
430
|
await this.mutex.acquire();
|
|
151
431
|
try {
|
|
152
|
-
|
|
153
|
-
|
|
432
|
+
uuid = await this.pathToItemUUID({ relativePath });
|
|
433
|
+
const item = this.getDirectoryTreeCache.tree[relativePath];
|
|
434
|
+
if (!uuid || !item) {
|
|
154
435
|
return;
|
|
155
436
|
}
|
|
156
437
|
const acceptedTypes = !type ? ["directory", "file"] : type === "directory" ? ["directory"] : ["file"];
|
|
157
|
-
if (!acceptedTypes.includes(
|
|
438
|
+
if (!acceptedTypes.includes(item.type)) {
|
|
158
439
|
return;
|
|
159
440
|
}
|
|
160
|
-
if (
|
|
441
|
+
if (item.type === "directory") {
|
|
161
442
|
if (permanent) {
|
|
162
443
|
await this.sync.sdk.cloud().deleteDirectory({ uuid });
|
|
163
444
|
}
|
|
@@ -173,11 +454,14 @@ class RemoteFileSystem {
|
|
|
173
454
|
await this.sync.sdk.cloud().trashFile({ uuid });
|
|
174
455
|
}
|
|
175
456
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
457
|
+
await cleanItemEntry();
|
|
458
|
+
}
|
|
459
|
+
catch (e) {
|
|
460
|
+
if (e instanceof sdk_1.APIError && (e.code === "file_not_found" || e.code === "folder_not_found")) {
|
|
461
|
+
await cleanItemEntry();
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
throw e;
|
|
181
465
|
}
|
|
182
466
|
}
|
|
183
467
|
finally {
|
|
@@ -185,7 +469,7 @@ class RemoteFileSystem {
|
|
|
185
469
|
}
|
|
186
470
|
}
|
|
187
471
|
/**
|
|
188
|
-
* Rename a file/directory inside the remote sync path. Recursively creates intermediate directories if needed.
|
|
472
|
+
* Rename/Move a file/directory inside the remote sync path. Recursively creates intermediate directories if needed.
|
|
189
473
|
* @date 3/2/2024 - 9:35:12 PM
|
|
190
474
|
*
|
|
191
475
|
* @public
|
|
@@ -199,7 +483,7 @@ class RemoteFileSystem {
|
|
|
199
483
|
await this.mutex.acquire();
|
|
200
484
|
try {
|
|
201
485
|
if (fromRelativePath === "/" || fromRelativePath === toRelativePath) {
|
|
202
|
-
|
|
486
|
+
throw new Error("Invalid paths.");
|
|
203
487
|
}
|
|
204
488
|
const uuid = await this.pathToItemUUID({ relativePath: fromRelativePath });
|
|
205
489
|
const item = this.getDirectoryTreeCache.tree[fromRelativePath];
|
|
@@ -225,10 +509,13 @@ class RemoteFileSystem {
|
|
|
225
509
|
});
|
|
226
510
|
if (newParentPath === currentParentPath) {
|
|
227
511
|
if (toRelativePath === "/" || newBasename.length <= 0) {
|
|
228
|
-
|
|
512
|
+
throw new Error("Invalid paths.");
|
|
229
513
|
}
|
|
230
514
|
if (item.type === "directory") {
|
|
231
|
-
await this.sync.sdk.cloud().renameDirectory({
|
|
515
|
+
await this.sync.sdk.cloud().renameDirectory({
|
|
516
|
+
uuid,
|
|
517
|
+
name: newBasename
|
|
518
|
+
});
|
|
232
519
|
}
|
|
233
520
|
else {
|
|
234
521
|
await this.sync.sdk.cloud().renameFile({
|
|
@@ -237,16 +524,17 @@ class RemoteFileSystem {
|
|
|
237
524
|
name: newBasename
|
|
238
525
|
});
|
|
239
526
|
}
|
|
240
|
-
const oldItem = this.getDirectoryTreeCache.tree[fromRelativePath];
|
|
241
|
-
if (oldItem) {
|
|
242
|
-
this.getDirectoryTreeCache.tree[toRelativePath] = Object.assign(Object.assign({}, oldItem), { name: newBasename });
|
|
243
|
-
}
|
|
244
|
-
delete this.getDirectoryTreeCache.tree[fromRelativePath];
|
|
245
527
|
}
|
|
246
528
|
else {
|
|
529
|
+
if (toRelativePath.startsWith(fromRelativePath)) {
|
|
530
|
+
throw new Error("Invalid paths.");
|
|
531
|
+
}
|
|
247
532
|
if (oldBasename !== newBasename) {
|
|
248
533
|
if (item.type === "directory") {
|
|
249
|
-
await this.sync.sdk.cloud().renameDirectory({
|
|
534
|
+
await this.sync.sdk.cloud().renameDirectory({
|
|
535
|
+
uuid,
|
|
536
|
+
name: newBasename
|
|
537
|
+
});
|
|
250
538
|
}
|
|
251
539
|
else {
|
|
252
540
|
await this.sync.sdk.cloud().renameFile({
|
|
@@ -258,14 +546,18 @@ class RemoteFileSystem {
|
|
|
258
546
|
}
|
|
259
547
|
if (newParentPath === "/" || newParentPath === "." || newParentPath === "") {
|
|
260
548
|
if (item.type === "directory") {
|
|
261
|
-
await this.sync.sdk
|
|
262
|
-
|
|
263
|
-
|
|
549
|
+
await this.sync.sdk.cloud().moveDirectory({
|
|
550
|
+
uuid,
|
|
551
|
+
to: this.sync.syncPair.remoteParentUUID,
|
|
552
|
+
metadata: itemMetadata
|
|
553
|
+
});
|
|
264
554
|
}
|
|
265
555
|
else {
|
|
266
|
-
await this.sync.sdk
|
|
267
|
-
|
|
268
|
-
|
|
556
|
+
await this.sync.sdk.cloud().moveFile({
|
|
557
|
+
uuid,
|
|
558
|
+
to: this.sync.syncPair.remoteParentUUID,
|
|
559
|
+
metadata: itemMetadata
|
|
560
|
+
});
|
|
269
561
|
}
|
|
270
562
|
}
|
|
271
563
|
else {
|
|
@@ -275,30 +567,40 @@ class RemoteFileSystem {
|
|
|
275
567
|
throw new Error(`Could not find path ${newParentPath}.`);
|
|
276
568
|
}
|
|
277
569
|
if (item.type === "directory") {
|
|
278
|
-
await this.sync.sdk
|
|
279
|
-
|
|
280
|
-
|
|
570
|
+
await this.sync.sdk.cloud().moveDirectory({
|
|
571
|
+
uuid,
|
|
572
|
+
to: newParentItem.uuid,
|
|
573
|
+
metadata: itemMetadata
|
|
574
|
+
});
|
|
281
575
|
}
|
|
282
576
|
else {
|
|
283
|
-
await this.sync.sdk.cloud().moveFile({
|
|
577
|
+
await this.sync.sdk.cloud().moveFile({
|
|
578
|
+
uuid,
|
|
579
|
+
to: newParentItem.uuid,
|
|
580
|
+
metadata: itemMetadata
|
|
581
|
+
});
|
|
284
582
|
}
|
|
285
583
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
584
|
+
}
|
|
585
|
+
await this.itemsMutex.acquire();
|
|
586
|
+
this.getDirectoryTreeCache.tree[toRelativePath] = Object.assign(Object.assign({}, item), { name: path_1.default.basename(toRelativePath), path: toRelativePath });
|
|
587
|
+
this.getDirectoryTreeCache.uuids[item.uuid] = Object.assign(Object.assign({}, item), { name: path_1.default.basename(toRelativePath), path: toRelativePath });
|
|
588
|
+
delete this.getDirectoryTreeCache.tree[fromRelativePath];
|
|
589
|
+
for (const oldPath in this.getDirectoryTreeCache.tree) {
|
|
590
|
+
if (oldPath.startsWith(fromRelativePath + "/") && oldPath !== fromRelativePath) {
|
|
591
|
+
const newPath = (0, utils_1.replacePathStartWithFromAndTo)(oldPath, fromRelativePath, toRelativePath);
|
|
592
|
+
const oldItem = this.getDirectoryTreeCache.tree[oldPath];
|
|
593
|
+
if (oldItem) {
|
|
594
|
+
this.getDirectoryTreeCache.tree[newPath] = Object.assign(Object.assign({}, oldItem), { name: path_1.default.basename(newPath), path: newPath });
|
|
298
595
|
delete this.getDirectoryTreeCache.tree[oldPath];
|
|
596
|
+
const oldItemUUID = this.getDirectoryTreeCache.uuids[oldItem.uuid];
|
|
597
|
+
if (oldItemUUID) {
|
|
598
|
+
this.getDirectoryTreeCache.uuids[oldItem.uuid] = Object.assign(Object.assign({}, oldItemUUID), { name: path_1.default.basename(newPath), path: newPath });
|
|
599
|
+
}
|
|
299
600
|
}
|
|
300
601
|
}
|
|
301
602
|
}
|
|
603
|
+
this.itemsMutex.release();
|
|
302
604
|
}
|
|
303
605
|
finally {
|
|
304
606
|
this.mutex.release();
|
|
@@ -315,7 +617,10 @@ class RemoteFileSystem {
|
|
|
315
617
|
* @returns {Promise<fs.Stats>}
|
|
316
618
|
*/
|
|
317
619
|
async download({ relativePath }) {
|
|
620
|
+
var _a;
|
|
318
621
|
const localPath = path_1.default.posix.join(this.sync.syncPair.localPath, relativePath);
|
|
622
|
+
const tmpLocalPath = path_1.default.join(this.sync.syncPair.localPath, constants_1.LOCAL_TRASH_NAME, (0, uuid_1.v4)());
|
|
623
|
+
const signalKey = `upload:${relativePath}`;
|
|
319
624
|
const uuid = await this.pathToItemUUID({ relativePath });
|
|
320
625
|
const item = this.getDirectoryTreeCache.tree[relativePath];
|
|
321
626
|
if (!uuid || !item) {
|
|
@@ -324,19 +629,178 @@ class RemoteFileSystem {
|
|
|
324
629
|
if (item.type === "directory") {
|
|
325
630
|
throw new Error(`Could not download ${relativePath}: Not a file.`);
|
|
326
631
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
632
|
+
if (!this.sync.pauseSignals[signalKey]) {
|
|
633
|
+
this.sync.pauseSignals[signalKey] = new sdk_1.PauseSignal();
|
|
634
|
+
}
|
|
635
|
+
if (!this.sync.abortControllers[signalKey]) {
|
|
636
|
+
this.sync.abortControllers[signalKey] = new AbortController();
|
|
637
|
+
}
|
|
638
|
+
(0, ipc_1.postMessageToMain)({
|
|
639
|
+
type: "transfer",
|
|
640
|
+
syncPair: this.sync.syncPair,
|
|
641
|
+
data: {
|
|
642
|
+
of: "download",
|
|
643
|
+
type: "queued",
|
|
644
|
+
relativePath,
|
|
645
|
+
localPath,
|
|
646
|
+
size: item.size
|
|
647
|
+
}
|
|
337
648
|
});
|
|
338
|
-
|
|
339
|
-
|
|
649
|
+
try {
|
|
650
|
+
await this.sync.sdk.cloud().downloadFileToLocal({
|
|
651
|
+
uuid,
|
|
652
|
+
bucket: item.bucket,
|
|
653
|
+
region: item.region,
|
|
654
|
+
chunks: item.chunks,
|
|
655
|
+
version: item.version,
|
|
656
|
+
to: tmpLocalPath,
|
|
657
|
+
key: item.key,
|
|
658
|
+
size: item.size,
|
|
659
|
+
pauseSignal: this.sync.pauseSignals[signalKey],
|
|
660
|
+
abortSignal: (_a = this.sync.abortControllers[signalKey]) === null || _a === void 0 ? void 0 : _a.signal,
|
|
661
|
+
onError: err => {
|
|
662
|
+
this.sync.worker.logger.log("error", err, "filesystems.remote.download");
|
|
663
|
+
(0, ipc_1.postMessageToMain)({
|
|
664
|
+
type: "transfer",
|
|
665
|
+
syncPair: this.sync.syncPair,
|
|
666
|
+
data: {
|
|
667
|
+
of: "download",
|
|
668
|
+
type: "error",
|
|
669
|
+
relativePath,
|
|
670
|
+
localPath,
|
|
671
|
+
error: (0, utils_1.serializeError)(err),
|
|
672
|
+
size: item.size
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
},
|
|
676
|
+
onProgress: bytes => {
|
|
677
|
+
(0, ipc_1.postMessageToMain)({
|
|
678
|
+
type: "transfer",
|
|
679
|
+
syncPair: this.sync.syncPair,
|
|
680
|
+
data: {
|
|
681
|
+
of: "download",
|
|
682
|
+
type: "progress",
|
|
683
|
+
relativePath,
|
|
684
|
+
localPath,
|
|
685
|
+
bytes,
|
|
686
|
+
size: item.size
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
},
|
|
690
|
+
onStarted: () => {
|
|
691
|
+
(0, ipc_1.postMessageToMain)({
|
|
692
|
+
type: "transfer",
|
|
693
|
+
syncPair: this.sync.syncPair,
|
|
694
|
+
data: {
|
|
695
|
+
of: "download",
|
|
696
|
+
type: "started",
|
|
697
|
+
relativePath,
|
|
698
|
+
localPath,
|
|
699
|
+
size: item.size
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
await fs_extra_1.default.utimes(tmpLocalPath, new Date(), new Date((0, utils_1.convertTimestampToMs)(item.lastModified)));
|
|
705
|
+
await fs_extra_1.default.move(tmpLocalPath, localPath, {
|
|
706
|
+
overwrite: true
|
|
707
|
+
});
|
|
708
|
+
const stats = await fs_extra_1.default.stat(localPath);
|
|
709
|
+
const localItem = {
|
|
710
|
+
type: "file",
|
|
711
|
+
inode: parseInt(stats.ino), // Sometimes comes as a float, but we need an int
|
|
712
|
+
lastModified: parseInt(stats.mtimeMs), // Sometimes comes as a float, but we need an int
|
|
713
|
+
creation: parseInt(stats.birthtimeMs), // Sometimes comes as a float, but we need an int
|
|
714
|
+
size: parseInt(stats.size), // Sometimes comes as a float, but we need an int
|
|
715
|
+
path: relativePath
|
|
716
|
+
};
|
|
717
|
+
await this.sync.localFileSystem.itemsMutex.acquire();
|
|
718
|
+
this.sync.localFileSystem.getDirectoryTreeCache.tree[relativePath] = localItem;
|
|
719
|
+
this.sync.localFileSystem.getDirectoryTreeCache.inodes[localItem.inode] = localItem;
|
|
720
|
+
this.sync.localFileSystem.itemsMutex.release();
|
|
721
|
+
(0, ipc_1.postMessageToMain)({
|
|
722
|
+
type: "transfer",
|
|
723
|
+
syncPair: this.sync.syncPair,
|
|
724
|
+
data: {
|
|
725
|
+
of: "download",
|
|
726
|
+
type: "finished",
|
|
727
|
+
relativePath,
|
|
728
|
+
localPath,
|
|
729
|
+
size: item.size
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
return stats;
|
|
733
|
+
}
|
|
734
|
+
catch (e) {
|
|
735
|
+
this.sync.worker.logger.log("error", e, "filesystems.remote.download");
|
|
736
|
+
if (e instanceof Error) {
|
|
737
|
+
(0, ipc_1.postMessageToMain)({
|
|
738
|
+
type: "transfer",
|
|
739
|
+
syncPair: this.sync.syncPair,
|
|
740
|
+
data: {
|
|
741
|
+
of: "download",
|
|
742
|
+
type: "error",
|
|
743
|
+
relativePath,
|
|
744
|
+
localPath,
|
|
745
|
+
error: (0, utils_1.serializeError)(e),
|
|
746
|
+
size: item.size
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
throw e;
|
|
751
|
+
}
|
|
752
|
+
finally {
|
|
753
|
+
delete this.sync.pauseSignals[signalKey];
|
|
754
|
+
delete this.sync.abortControllers[signalKey];
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
async remoteDirPathExisting() {
|
|
758
|
+
try {
|
|
759
|
+
const present = await this.sync.sdk.api(3).dir().present({ uuid: this.sync.syncPair.remoteParentUUID });
|
|
760
|
+
if (!present.present || present.trash) {
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
return true;
|
|
764
|
+
}
|
|
765
|
+
catch (_a) {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
async directoryExists(relativePath) {
|
|
770
|
+
try {
|
|
771
|
+
const item = this.getDirectoryTreeCache.tree[relativePath];
|
|
772
|
+
if (!item) {
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
const present = await this.sync.sdk.api(3).dir().present({ uuid: item.uuid });
|
|
776
|
+
if (!present.present || present.trash) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
return true;
|
|
780
|
+
}
|
|
781
|
+
catch (_a) {
|
|
782
|
+
return false;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
async fileExists(relativePath) {
|
|
786
|
+
try {
|
|
787
|
+
const item = this.getDirectoryTreeCache.tree[relativePath];
|
|
788
|
+
const parent = this.getDirectoryTreeCache.tree[path_1.default.posix.dirname(relativePath)];
|
|
789
|
+
if (!item || !parent) {
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
const { exists, existsUUID } = await this.sync.sdk.api(3).file().exists({
|
|
793
|
+
name: item.name,
|
|
794
|
+
parent: parent.uuid
|
|
795
|
+
});
|
|
796
|
+
if (!exists || existsUUID !== item.uuid) {
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
catch (_a) {
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
340
804
|
}
|
|
341
805
|
}
|
|
342
806
|
exports.RemoteFileSystem = RemoteFileSystem;
|