From 71867dd63ecced8e1fc1f928c4e28685feb79ba7 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Thu, 12 Jun 2025 09:54:59 +0530 Subject: [PATCH] Move and doc --- web/packages/media/collection.ts | 13 ++--- web/packages/media/file-metadata.ts | 44 +---------------- web/packages/media/magic-metadata.ts | 74 ++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 49 deletions(-) create mode 100644 web/packages/media/magic-metadata.ts diff --git a/web/packages/media/collection.ts b/web/packages/media/collection.ts index 243f7629b3..22e8be0063 100644 --- a/web/packages/media/collection.ts +++ b/web/packages/media/collection.ts @@ -5,6 +5,7 @@ import { import { ItemVisibility } from "ente-media/file-metadata"; import { nullToUndefined } from "ente-utils/transform"; import { z } from "zod/v4"; +import { RemoteMagicMetadataSchema } from "./magic-metadata"; // TODO: Audit this file @@ -225,12 +226,12 @@ export const RemoteCollection = z.object({ * have been deleted and should thus be pruned by the client locally. */ isDeleted: z.boolean().nullish().transform(nullToUndefined), - // TODO(RE): - magicMetadata: z.looseObject({}).nullish().transform(nullToUndefined), // ? - // TODO(RE): - pubMagicMetadata: z.looseObject({}).nullish().transform(nullToUndefined), // ? - // TODO(RE): - sharedMagicMetadata: z.looseObject({}).nullish().transform(nullToUndefined), // ? + magicMetadata: + RemoteMagicMetadataSchema.nullish().transform(nullToUndefined), + pubMagicMetadata: + RemoteMagicMetadataSchema.nullish().transform(nullToUndefined), + sharedMagicMetadata: + RemoteMagicMetadataSchema.nullish().transform(nullToUndefined), }); export interface EncryptedCollection { diff --git a/web/packages/media/file-metadata.ts b/web/packages/media/file-metadata.ts index 25c795ff88..4c2e25bf12 100644 --- a/web/packages/media/file-metadata.ts +++ b/web/packages/media/file-metadata.ts @@ -13,6 +13,7 @@ import { nullToUndefined } from "ente-utils/transform"; import { z } from "zod/v4"; import { mergeMetadata1 } from "./file"; import { FileType } from "./file-type"; +import type { RemoteMagicMetadata } from "./magic-metadata"; /** * Information about the file that never changes post upload. @@ -621,49 +622,6 @@ export const updateRemotePublicMagicMetadata = async ( mergeMetadata1(file); }; -/** - * Magic metadata, either public and private, as persisted and used by remote. - * - * This is the encrypted magic metadata as persisted on remote, and this is what - * clients get back when they sync with remote. Alongwith the encrypted blob and - * decryption header, it also contains a few properties useful for clients to - * track changes and ensure that they have the latest metadata synced locally. - * - * Both public and private magic metadata fields use the same structure. - */ -interface RemoteMagicMetadata { - /** - * Monotonically increasing iteration of this metadata object. - * - * The version starts at 1. Remote increments this version number each time - * a client updates the corresponding magic metadata field for the file. - */ - version: number; - /** - * The number of keys with non-null (and non-undefined) values in the - * encrypted JSON object that the encrypted metadata blob contains. - * - * During edits and updates, this number should be greater than or equal to - * the previous version. - * - * > Clients are expected to retain the magic metadata verbatim so that they - * > don't accidentally overwrite fields that they might not understand. - */ - count: number; - /** - * The encrypted data. - * - * This is a base64 string representing the bytes obtained by encrypting the - * string representation of the underlying magic metadata JSON object. - */ - data: string; - /** - * The base64 encoded decryption header that will be needed for the client - * for decrypting {@link data}. - */ - header: string; -} - /** * The shape of the JSON body payload expected by the APIs that update the * public and private magic metadata fields associated with a file. diff --git a/web/packages/media/magic-metadata.ts b/web/packages/media/magic-metadata.ts new file mode 100644 index 0000000000..12d4948d46 --- /dev/null +++ b/web/packages/media/magic-metadata.ts @@ -0,0 +1,74 @@ +import { z } from "zod/v4"; + +/** + * Zod schema of the mutable metadatum objects that we send to or receive from + * remote. It is effectively an envelope of the encrypted metadata contents with + * some bookkeeping information attached. + * + * See {@link RemoteMagicMetadata} for the corresponding type. Since this module + * exports both the Zod schema and the TypeScript type, to avoid confusion the + * schema is suffixed by "Schema". + */ +export const RemoteMagicMetadataSchema = z.object({ + version: z.number(), + count: z.number(), + data: z.string(), + header: z.string(), +}); + +/** + * Any of the mutable metadata fields, as represented by remote. + * + * See: [Note: Metadatum] + * + * This is the encrypted magic metadata that we send to and receive from remote. + * It contains the encrypted contents, and a version + count of fields that help + * ensure that clients do not overwrite updates with stale objects. + * + * When decrypt these into specific {@link MagicMetadata} instantiations before + * using and storing them on the client. + * + * See {@link RemoteMagicMetadataSchema} for the corresponding Zod schema. The + * same structure is used to store the metadata for various kinds of objects + * (files, collections), so this module exports both the TypeScript type and + * also the Zod schema so that other modules can compose schema definitions. + */ +export interface RemoteMagicMetadata { + /** + * Monotonically increasing iteration of this metadata object. + * + * The version starts at 1. Remote increments this version number each time + * a client updates the corresponding magic metadata field. + */ + version: number; + /** + * The number of keys with non-null (and non-undefined) values in the + * encrypted JSON object that the encrypted metadata blob contains. + * + * During edits and updates, this number should be greater than or equal to + * the previous version. + * + * > Clients are expected to retain the magic metadata verbatim so that they + * > don't accidentally overwrite fields that they might not understand. + * > + * > See: [Note: Use looseObject for metadata Zod schemas] + */ + count: number; + /** + * The encrypted data. + * + * This are the base64 encoded bytes of the encrypted magic metadata JSON + * object. + * + * The encryption will happen using the "key" of the object whose magic + * metadata field this is. For example, if this is the + * {@link pubMagicMetadata} of an {@link EnteFile}, then the file's key will + * be used for encryption. + */ + data: string; + /** + * The base64 encoded decryption header that will be needed for the client + * for decrypting {@link data}. + */ + header: string; +}