Files
quak/src/model/types.ts
T
sneak df645e6073
check / check (push) Successful in 28s
Check downloaded originals against their recorded content hash (closes #68)
downloadFile, shared by quak get, the content cache and backup, hashes
the decrypted bytes (unkeyed BLAKE2b-512, standard base64) and stores
nothing on a mismatch, failing with an error naming the file ID. A live
photo ZIP is unpacked as it streams with fflate's Unzip, in small
slices so memory stays bounded however far an entry expands, and its
image and video hashed separately as <imageHash>:<videoHash>.
decryptFile reads the older imageHash/videoHash fields for live
photos. A file with no recorded hash is stored unchecked.

Model: opus-5-5
2026-09-23 03:50:19 +00:00

110 lines
3.0 KiB
TypeScript

export type Microseconds = number;
export type CollectionType =
| "album"
| "folder"
| "favorites"
| "uncategorized"
| "unknown";
export interface Collection {
id: number;
ownerID: number;
key: Uint8Array;
name: string;
type: CollectionType;
updationTime: Microseconds;
isShared: boolean;
magicMetadata?: Record<string, unknown>;
pubMagicMetadata?: Record<string, unknown>;
sharedMagicMetadata?: Record<string, unknown>;
}
export type FileType = "image" | "video" | "livePhoto" | "unknown";
export interface FileMetadata {
title: string;
fileType: FileType;
creationTime: Microseconds;
modificationTime: Microseconds;
latitude?: number;
longitude?: number;
// The content hash the uploader recorded (see `expectedHash` in
// decrypt.ts); `downloadFile` refuses an original that does not match it.
// Absent for files from very old clients.
hash?: string;
}
export interface FileBlob {
decryptionHeader: string;
size?: number;
}
export interface EnteFile {
id: number;
collectionID: number;
ownerID: number;
key: Uint8Array;
metadata: FileMetadata;
magicMetadata?: Record<string, unknown>;
pubMagicMetadata?: Record<string, unknown>;
file: FileBlob;
thumbnail: FileBlob;
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
// any collection: the master key (secretbox for owned collection keys) and
// the X25519 keypair (sealed box for collection keys shared with us).
export interface KeyMaterial {
masterKey: Uint8Array;
publicKey: Uint8Array;
secretKey: Uint8Array;
}
// Raw shapes as they arrive from the Ente API, before decryption.
export interface RawCollection {
id: number;
owner: { id: number };
encryptedKey: string;
// Absent for collections shared with us: their encryptedKey is a
// sealed box to our public key, which embeds an ephemeral public key
// instead of using a nonce.
keyDecryptionNonce?: string;
encryptedName?: string;
nameDecryptionNonce?: string;
type: string;
updationTime: number;
isDeleted?: boolean;
magicMetadata?: RawMagicMetadata;
pubMagicMetadata?: RawMagicMetadata;
sharedMagicMetadata?: RawMagicMetadata;
}
export interface RawMagicMetadata {
version: number;
count: number;
data: string;
header: string;
}
export interface RawEnteFile {
id: number;
collectionID: number;
ownerID: number;
encryptedKey: string;
keyDecryptionNonce: string;
metadata: { encryptedData: string; decryptionHeader: string };
magicMetadata?: RawMagicMetadata;
pubMagicMetadata?: RawMagicMetadata;
info?: { fileSize?: number; thumbSize?: number };
file: { decryptionHeader: string };
thumbnail: { decryptionHeader: string };
updationTime: number;
isDeleted?: boolean;
}