@lacrous/omnicloud 0.2.32 → 0.2.34
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/index.cjs +101 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +94 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/server/index.js +133 -5
- package/server/prisma/migrations/6_api_tokens/migration.sql +19 -0
- package/server/prisma/schema.prisma +16 -0
package/dist/index.cjs
CHANGED
|
@@ -77,6 +77,51 @@ var OmniCloudError = class extends Error {
|
|
|
77
77
|
details;
|
|
78
78
|
};
|
|
79
79
|
var DEFAULT_RETRY = { attempts: 3, baseDelayMs: 300 };
|
|
80
|
+
function randomId() {
|
|
81
|
+
return globalThis.crypto.randomUUID().replace(/-/g, "");
|
|
82
|
+
}
|
|
83
|
+
function multipartHead(boundary, fields, fileName) {
|
|
84
|
+
const parts = fields.map(
|
|
85
|
+
([name, value]) => `--${boundary}\r
|
|
86
|
+
Content-Disposition: form-data; name="${name}"\r
|
|
87
|
+
\r
|
|
88
|
+
${value}\r
|
|
89
|
+
`
|
|
90
|
+
);
|
|
91
|
+
const safeName = fileName.replace(/["\\\r\n]/g, "_");
|
|
92
|
+
parts.push(
|
|
93
|
+
`--${boundary}\r
|
|
94
|
+
Content-Disposition: form-data; name="file"; filename="${safeName}"\r
|
|
95
|
+
Content-Type: application/octet-stream\r
|
|
96
|
+
\r
|
|
97
|
+
`
|
|
98
|
+
);
|
|
99
|
+
return parts.join("");
|
|
100
|
+
}
|
|
101
|
+
function nodeMultipartStream(head, file, tail) {
|
|
102
|
+
const encoder = new TextEncoder();
|
|
103
|
+
const iterator = file[Symbol.asyncIterator]();
|
|
104
|
+
let sentHead = false;
|
|
105
|
+
return new ReadableStream({
|
|
106
|
+
async pull(controller) {
|
|
107
|
+
if (!sentHead) {
|
|
108
|
+
sentHead = true;
|
|
109
|
+
controller.enqueue(encoder.encode(head));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const { done, value } = await iterator.next();
|
|
113
|
+
if (done) {
|
|
114
|
+
controller.enqueue(encoder.encode(tail));
|
|
115
|
+
controller.close();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
controller.enqueue(typeof value === "string" ? encoder.encode(value) : new Uint8Array(value));
|
|
119
|
+
},
|
|
120
|
+
async cancel(reason) {
|
|
121
|
+
await iterator.return?.(reason);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
80
125
|
function sleep(ms, signal) {
|
|
81
126
|
return new Promise((resolve, reject) => {
|
|
82
127
|
const timer = setTimeout(resolve, ms);
|
|
@@ -368,10 +413,13 @@ var FilesApi = class {
|
|
|
368
413
|
async upload(input, options = {}) {
|
|
369
414
|
const attempts = Math.max(1, (options.retry ?? 2) + 1);
|
|
370
415
|
let lastError;
|
|
371
|
-
const
|
|
416
|
+
const source = input.path !== void 0 && this.canStreamFromDisk() ? { kind: "path", path: input.path } : { kind: "blob", blob: await toBlob(input) };
|
|
372
417
|
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
373
418
|
try {
|
|
374
|
-
|
|
419
|
+
if (source.kind === "path") {
|
|
420
|
+
return await this.uploadPathOnce(source.path, input.name, options);
|
|
421
|
+
}
|
|
422
|
+
return await this.uploadOnce(source.blob, input.name, options);
|
|
375
423
|
} catch (error) {
|
|
376
424
|
lastError = error;
|
|
377
425
|
if (options.signal?.aborted) throw error;
|
|
@@ -381,6 +429,46 @@ var FilesApi = class {
|
|
|
381
429
|
}
|
|
382
430
|
throw lastError;
|
|
383
431
|
}
|
|
432
|
+
canStreamFromDisk() {
|
|
433
|
+
return typeof XMLHttpRequest !== "function" && typeof ReadableStream === "function";
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Node: streams a file from disk as a multipart body. The file is re-opened on
|
|
437
|
+
* every attempt, so a retry sends the same bytes without holding them in memory.
|
|
438
|
+
*/
|
|
439
|
+
async uploadPathOnce(path, name, options) {
|
|
440
|
+
const { createReadStream, statSync } = await import("fs");
|
|
441
|
+
const size = statSync(path).size;
|
|
442
|
+
const boundary = `omnicloud-${randomId()}`;
|
|
443
|
+
const fields = [];
|
|
444
|
+
if (options.folderId) fields.push(["folderId", options.folderId]);
|
|
445
|
+
const head = multipartHead(boundary, fields, name);
|
|
446
|
+
const tail = `\r
|
|
447
|
+
--${boundary}--\r
|
|
448
|
+
`;
|
|
449
|
+
const body = nodeMultipartStream(head, createReadStream(path), tail);
|
|
450
|
+
const response = await this.client.fetchImpl(`${this.client.baseUrl}/api/files`, {
|
|
451
|
+
method: "POST",
|
|
452
|
+
headers: {
|
|
453
|
+
...this.client.defaultHeaders,
|
|
454
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
455
|
+
"Content-Length": String(Buffer.byteLength(head) + size + Buffer.byteLength(tail))
|
|
456
|
+
},
|
|
457
|
+
body,
|
|
458
|
+
credentials: "include",
|
|
459
|
+
duplex: "half"
|
|
460
|
+
});
|
|
461
|
+
const text = await response.text();
|
|
462
|
+
if (!response.ok) {
|
|
463
|
+
const error = safeJsonParse(text)?.error;
|
|
464
|
+
throw new OmniCloudError(
|
|
465
|
+
response.status,
|
|
466
|
+
error?.code ?? "UPLOAD_FAILED",
|
|
467
|
+
error?.message ?? `Upload failed (${response.status})`
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
return JSON.parse(text).file;
|
|
471
|
+
}
|
|
384
472
|
async uploadOnce(blob, name, options) {
|
|
385
473
|
const form = new FormData();
|
|
386
474
|
form.append("file", blob, name);
|
|
@@ -1381,6 +1469,9 @@ async function resolveItemQuery(folders, userId, input) {
|
|
|
1381
1469
|
// ../core/src/services/auth-session-service.ts
|
|
1382
1470
|
var import_node_crypto3 = require("crypto");
|
|
1383
1471
|
|
|
1472
|
+
// ../core/src/services/api-token-service.ts
|
|
1473
|
+
var import_node_crypto4 = require("crypto");
|
|
1474
|
+
|
|
1384
1475
|
// ../core/src/services/version-retention.ts
|
|
1385
1476
|
function versionsToPrune(versions, currentVersionId, policy, now) {
|
|
1386
1477
|
const historical = versions.filter((version) => version.id !== currentVersionId).sort((a, b) => b.versionNumber - a.versionNumber);
|
|
@@ -1502,7 +1593,7 @@ function sanitizeFileName(name) {
|
|
|
1502
1593
|
}
|
|
1503
1594
|
|
|
1504
1595
|
// ../core/src/utils/request-fingerprint.ts
|
|
1505
|
-
var
|
|
1596
|
+
var import_node_crypto5 = require("crypto");
|
|
1506
1597
|
function uploadRequestFingerprint(input) {
|
|
1507
1598
|
const contentHash = input.spooled?.sha256 ?? sha256Hex(input.data ?? Buffer.alloc(0));
|
|
1508
1599
|
const canonical = JSON.stringify({
|
|
@@ -1512,7 +1603,7 @@ function uploadRequestFingerprint(input) {
|
|
|
1512
1603
|
contentHash,
|
|
1513
1604
|
size: input.spooled?.size ?? input.data?.byteLength ?? 0
|
|
1514
1605
|
});
|
|
1515
|
-
return (0,
|
|
1606
|
+
return (0, import_node_crypto5.createHash)("sha256").update(canonical).digest("hex");
|
|
1516
1607
|
}
|
|
1517
1608
|
|
|
1518
1609
|
// ../core/src/services/file-service.ts
|
|
@@ -2747,7 +2838,7 @@ var RecentService = class {
|
|
|
2747
2838
|
};
|
|
2748
2839
|
|
|
2749
2840
|
// ../core/src/utils/spool.ts
|
|
2750
|
-
var
|
|
2841
|
+
var import_node_crypto6 = require("crypto");
|
|
2751
2842
|
var import_node_fs = require("fs");
|
|
2752
2843
|
var import_promises = require("fs/promises");
|
|
2753
2844
|
var import_node_os = require("os");
|
|
@@ -2756,16 +2847,16 @@ var import_node_stream2 = require("stream");
|
|
|
2756
2847
|
var import_promises2 = require("stream/promises");
|
|
2757
2848
|
|
|
2758
2849
|
// ../core/src/utils/secret-box.ts
|
|
2759
|
-
var
|
|
2850
|
+
var import_node_crypto7 = require("crypto");
|
|
2760
2851
|
|
|
2761
2852
|
// ../telegram/src/client.ts
|
|
2762
2853
|
var import_telegram = require("telegram");
|
|
2763
2854
|
var import_sessions = require("telegram/sessions/index.js");
|
|
2764
2855
|
|
|
2765
2856
|
// ../telegram/src/errors.ts
|
|
2766
|
-
var
|
|
2857
|
+
var import_errors13 = require("telegram/errors/index.js");
|
|
2767
2858
|
function isTelegramRpcError(error) {
|
|
2768
|
-
return error instanceof
|
|
2859
|
+
return error instanceof import_errors13.RPCError;
|
|
2769
2860
|
}
|
|
2770
2861
|
function isPasswordRequiredError(error) {
|
|
2771
2862
|
return isTelegramRpcError(error) && error.errorMessage === "SESSION_PASSWORD_NEEDED";
|
|
@@ -3390,7 +3481,7 @@ var TelegramStorageProvider = class {
|
|
|
3390
3481
|
};
|
|
3391
3482
|
|
|
3392
3483
|
// ../telegram/src/connection.ts
|
|
3393
|
-
var
|
|
3484
|
+
var import_node_crypto8 = require("crypto");
|
|
3394
3485
|
var import_telegram3 = require("telegram");
|
|
3395
3486
|
var import_uploads2 = require("telegram/client/uploads.js");
|
|
3396
3487
|
var import_Password = require("telegram/Password.js");
|
|
@@ -3478,7 +3569,7 @@ var TelegramConnectionService = class {
|
|
|
3478
3569
|
needsPassword: false,
|
|
3479
3570
|
failure: null
|
|
3480
3571
|
};
|
|
3481
|
-
const flowId = (0,
|
|
3572
|
+
const flowId = (0, import_node_crypto8.randomUUID)();
|
|
3482
3573
|
const token = await this.exportQrToken(client);
|
|
3483
3574
|
this.qrFlows.set(flowId, flow);
|
|
3484
3575
|
this.watchQrApproval(flowId, flow);
|