@lacrous/omnicloud 0.2.16 → 0.2.18

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.d.cts CHANGED
@@ -930,6 +930,8 @@ interface UploadOperationRecord {
930
930
  telegramMessageId: number | null;
931
931
  sha256: string | null;
932
932
  size: number | null;
933
+ /** Digest of the original request (name, folder, target, content). Replays must match it. */
934
+ requestFingerprint: string | null;
933
935
  fileId: string | null;
934
936
  error: string | null;
935
937
  createdAt: Date;
@@ -943,6 +945,7 @@ interface UploadOperationRepository {
943
945
  create(input: {
944
946
  userId: string;
945
947
  operationId: string;
948
+ requestFingerprint?: string | null;
946
949
  }): Promise<UploadOperationRecord>;
947
950
  /**
948
951
  * Atomically moves an operation from `from` to `to` if, and only if, it is
package/dist/index.d.ts CHANGED
@@ -930,6 +930,8 @@ interface UploadOperationRecord {
930
930
  telegramMessageId: number | null;
931
931
  sha256: string | null;
932
932
  size: number | null;
933
+ /** Digest of the original request (name, folder, target, content). Replays must match it. */
934
+ requestFingerprint: string | null;
933
935
  fileId: string | null;
934
936
  error: string | null;
935
937
  createdAt: Date;
@@ -943,6 +945,7 @@ interface UploadOperationRepository {
943
945
  create(input: {
944
946
  userId: string;
945
947
  operationId: string;
948
+ requestFingerprint?: string | null;
946
949
  }): Promise<UploadOperationRecord>;
947
950
  /**
948
951
  * Atomically moves an operation from `from` to `to` if, and only if, it is
package/dist/index.js CHANGED
@@ -1414,6 +1414,20 @@ function sanitizeFileName(name) {
1414
1414
  return cleaned.slice(0, 255);
1415
1415
  }
1416
1416
 
1417
+ // ../core/src/utils/request-fingerprint.ts
1418
+ import { createHash as createHash4 } from "crypto";
1419
+ function uploadRequestFingerprint(input) {
1420
+ const contentHash = input.spooled?.sha256 ?? sha256Hex(input.data ?? Buffer.alloc(0));
1421
+ const canonical = JSON.stringify({
1422
+ name: sanitizeFileName(input.name),
1423
+ folderId: input.folderId ?? null,
1424
+ replaceFileId: input.replaceFileId ?? null,
1425
+ contentHash,
1426
+ size: input.spooled?.size ?? input.data?.byteLength ?? 0
1427
+ });
1428
+ return createHash4("sha256").update(canonical).digest("hex");
1429
+ }
1430
+
1417
1431
  // ../core/src/services/file-service.ts
1418
1432
  var FileService = class {
1419
1433
  constructor(files, folders, engineFor, activity = noopActivityRecorder, operations = null, log = null) {
@@ -1452,7 +1466,13 @@ var FileService = class {
1452
1466
  if (!isValidOperationId(operationId)) {
1453
1467
  throw new ValidationError("Invalid upload operation id");
1454
1468
  }
1469
+ const fingerprint = uploadRequestFingerprint(input);
1455
1470
  let op = await operations.findByOperationId(userId, operationId);
1471
+ if (op?.requestFingerprint && op.requestFingerprint !== fingerprint) {
1472
+ throw new ConflictError(
1473
+ "This upload key was already used for a different file. Use a new key for a new upload."
1474
+ );
1475
+ }
1456
1476
  if (op?.status === "COMPLETED" && op.fileId) {
1457
1477
  return this.get(userId, op.fileId);
1458
1478
  }
@@ -1461,7 +1481,7 @@ var FileService = class {
1461
1481
  }
1462
1482
  if (!op) {
1463
1483
  try {
1464
- op = await operations.create({ userId, operationId });
1484
+ op = await operations.create({ userId, operationId, requestFingerprint: fingerprint });
1465
1485
  } catch (error) {
1466
1486
  if (!(error instanceof OperationAlreadyExistsError)) throw error;
1467
1487
  const outcome = await this.followWinner(userId, input, operationId, operations);
@@ -2571,7 +2591,7 @@ var RecentService = class {
2571
2591
  };
2572
2592
 
2573
2593
  // ../core/src/utils/spool.ts
2574
- import { createHash as createHash4 } from "crypto";
2594
+ import { createHash as createHash5 } from "crypto";
2575
2595
  import { createWriteStream } from "fs";
2576
2596
  import { mkdtemp, rm } from "fs/promises";
2577
2597
  import { tmpdir } from "os";