Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d78aacac2 |
@@ -18,6 +18,13 @@ Update the README API reference section to match the current implementation.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-09-22: Carried file size, thumbnail size, and the deletion flag through
|
||||||
|
`decryptFile` (issue 37, foundation for the cache/API design). Live files now
|
||||||
|
populate `file.size`/`thumbnail.size` from the server's `info` (left
|
||||||
|
`undefined` when the server omits it), and `isDeleted` is carried from the
|
||||||
|
diff row onto `EnteFile`. No caller change: `listFiles` still filters deleted
|
||||||
|
rows before decrypting. Surfacing a tombstone through decryption belongs to
|
||||||
|
the enumeration unit (issue 38).
|
||||||
- 2026-08-10: Made `lint-once.test.ts` enforce what its header claims. It walked
|
- 2026-08-10: Made `lint-once.test.ts` enforce what its header claims. It walked
|
||||||
`make check` only, so it never read `Dockerfile` — the image CI builds through
|
`make check` only, so it never read `Dockerfile` — the image CI builds through
|
||||||
`script/cibuild` — and a second `prettier --check .` could be added there with
|
`script/cibuild` — and a second `prettier --check .` could be added there with
|
||||||
|
|||||||
@@ -120,9 +120,16 @@ export const decryptFile = (
|
|||||||
metadata,
|
metadata,
|
||||||
magicMetadata,
|
magicMetadata,
|
||||||
pubMagicMetadata,
|
pubMagicMetadata,
|
||||||
file: { decryptionHeader: raw.file.decryptionHeader },
|
file: {
|
||||||
thumbnail: { decryptionHeader: raw.thumbnail.decryptionHeader },
|
decryptionHeader: raw.file.decryptionHeader,
|
||||||
|
size: raw.info?.fileSize,
|
||||||
|
},
|
||||||
|
thumbnail: {
|
||||||
|
decryptionHeader: raw.thumbnail.decryptionHeader,
|
||||||
|
size: raw.info?.thumbSize,
|
||||||
|
},
|
||||||
updationTime: raw.updationTime,
|
updationTime: raw.updationTime,
|
||||||
|
isDeleted: raw.isDeleted,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ export interface EnteFile {
|
|||||||
file: FileBlob;
|
file: FileBlob;
|
||||||
thumbnail: FileBlob;
|
thumbnail: FileBlob;
|
||||||
updationTime: Microseconds;
|
updationTime: Microseconds;
|
||||||
|
// Set from the diff row's flag. Live files decode with it absent/false;
|
||||||
|
// deleted rows are filtered out before decryptFile, so it is not set here.
|
||||||
|
isDeleted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The key material a logged-in client holds, everything needed to decrypt
|
// The key material a logged-in client holds, everything needed to decrypt
|
||||||
|
|||||||
@@ -145,7 +145,12 @@ const buildSharedRawCollection = (
|
|||||||
|
|
||||||
const buildRawFile = (
|
const buildRawFile = (
|
||||||
collectionKey: Uint8Array,
|
collectionKey: Uint8Array,
|
||||||
opts?: { title?: string; fileType?: number; creationTime?: number },
|
opts?: {
|
||||||
|
title?: string;
|
||||||
|
fileType?: number;
|
||||||
|
creationTime?: number;
|
||||||
|
info?: { fileSize?: number; thumbSize?: number };
|
||||||
|
},
|
||||||
): RawEnteFile => {
|
): RawEnteFile => {
|
||||||
const fileKey = sodium.crypto_secretbox_keygen();
|
const fileKey = sodium.crypto_secretbox_keygen();
|
||||||
const { ciphertext: encFileKey, nonce: fileKeyNonce } = secretboxEncrypt(
|
const { ciphertext: encFileKey, nonce: fileKeyNonce } = secretboxEncrypt(
|
||||||
@@ -186,6 +191,7 @@ const buildRawFile = (
|
|||||||
},
|
},
|
||||||
file: { decryptionHeader: toBase64(sodium.randombytes_buf(24)) },
|
file: { decryptionHeader: toBase64(sodium.randombytes_buf(24)) },
|
||||||
thumbnail: { decryptionHeader: toBase64(sodium.randombytes_buf(24)) },
|
thumbnail: { decryptionHeader: toBase64(sodium.randombytes_buf(24)) },
|
||||||
|
info: opts?.info,
|
||||||
updationTime: 1700000000000000,
|
updationTime: 1700000000000000,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -357,4 +363,47 @@ describe("model.decryptFile", () => {
|
|||||||
|
|
||||||
expect(() => decryptFile(raw, wrongKey)).toThrow();
|
expect(() => decryptFile(raw, wrongKey)).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("carries the file and thumbnail byte sizes from info", () => {
|
||||||
|
// The server reports the encrypted-blob sizes in `info`; the cache
|
||||||
|
// needs them without a HEAD request, so decryptFile must copy them
|
||||||
|
// onto the file and thumbnail blobs.
|
||||||
|
const masterKey = sodium.crypto_secretbox_keygen();
|
||||||
|
const { collectionKey } = buildRawCollection(masterKey);
|
||||||
|
const raw = buildRawFile(collectionKey, {
|
||||||
|
info: { fileSize: 4096, thumbSize: 512 },
|
||||||
|
});
|
||||||
|
|
||||||
|
const file = decryptFile(raw, collectionKey);
|
||||||
|
|
||||||
|
expect(file.file.size).toBe(4096);
|
||||||
|
expect(file.thumbnail.size).toBe(512);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("leaves the sizes undefined when the server omits info", () => {
|
||||||
|
// Older files predate the info field; the sizes must stay undefined
|
||||||
|
// rather than become 0, so callers can tell "unknown" from "empty".
|
||||||
|
const masterKey = sodium.crypto_secretbox_keygen();
|
||||||
|
const { collectionKey } = buildRawCollection(masterKey);
|
||||||
|
const raw = buildRawFile(collectionKey);
|
||||||
|
expect(raw.info).toBeUndefined();
|
||||||
|
|
||||||
|
const file = decryptFile(raw, collectionKey);
|
||||||
|
|
||||||
|
expect(file.file.size).toBeUndefined();
|
||||||
|
expect(file.thumbnail.size).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("carries the deletion flag from the diff row", () => {
|
||||||
|
// The diff marks a deleted row with isDeleted; decryptFile copies it
|
||||||
|
// onto the file so a caller can tell a deleted row from a live one.
|
||||||
|
const masterKey = sodium.crypto_secretbox_keygen();
|
||||||
|
const { collectionKey } = buildRawCollection(masterKey);
|
||||||
|
const raw = buildRawFile(collectionKey);
|
||||||
|
raw.isDeleted = true;
|
||||||
|
|
||||||
|
const file = decryptFile(raw, collectionKey);
|
||||||
|
|
||||||
|
expect(file.isDeleted).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user