diff --git a/web/packages/new/photos/services/collection.ts b/web/packages/new/photos/services/collection.ts index 28441365b4..02f73e0ce7 100644 --- a/web/packages/new/photos/services/collection.ts +++ b/web/packages/new/photos/services/collection.ts @@ -121,10 +121,9 @@ export const createCollection2 = async ( type: CollectionType, magicMetadataData?: CollectionPrivateMagicMetadataData, ): Promise => { - const masterKey = await ensureMasterKeyFromSession(); const collectionKey = await generateKey(); const { encryptedData: encryptedKey, nonce: keyDecryptionNonce } = - await encryptBox(collectionKey, masterKey); + await encryptBox(collectionKey, await ensureMasterKeyFromSession()); const { encryptedData: encryptedName, nonce: nameDecryptionNonce } = await encryptBox(new TextEncoder().encode(name), collectionKey); const magicMetadata = magicMetadataData @@ -134,7 +133,7 @@ export const createCollection2 = async ( ) : undefined; - const collection = await postCollections({ + const remoteCollection = await postCollections({ encryptedKey, keyDecryptionNonce, encryptedName, @@ -143,12 +142,16 @@ export const createCollection2 = async ( ...(magicMetadata && { magicMetadata }), }); - return decryptRemoteCollection( - collection, - await decryptCollectionKey(collection), - ); + return decryptRemoteKeyAndCollection(remoteCollection); }; +/** + * Given a {@link RemoteCollection}, first obtain its decryption key, and then + * use that to decrypt and return the collection itself. + */ +const decryptRemoteKeyAndCollection = async (collection: RemoteCollection) => + decryptRemoteCollection(collection, await decryptCollectionKey(collection)); + // TODO(C2): Temporary method to convert to the newer type. export const collection1To2 = async (c1: Collection): Promise => { const collection = RemoteCollection.parse({ @@ -191,6 +194,11 @@ export const decryptCollectionKey = async ( } }; +/** + * Zod schema for a remote response containing a single collection. + */ +const CollectionResponse = z.object({ collection: RemoteCollection }); + /** * Create a collection on remote with the provided data, and return the new * remote collection object returned by remote on success. @@ -204,8 +212,31 @@ const postCollections = async ( body: JSON.stringify(collectionData), }); ensureOk(res); - return z.object({ collection: RemoteCollection }).parse(await res.json()) - .collection; + return CollectionResponse.parse(await res.json()).collection; +}; + +/** + * Fetch a collection from remote by its ID. + * + * Remote only, does not use or modify local state. + * + * This is not expected to be needed in the normal flow of things, since we + * fetch collections enmass, and efficiently, using the collection diff + * requests. + * + * @param collectionID The ID of the collection to fetch. + * + * @returns The collection obtained from remote after decrypting its contents. + */ +export const getCollectionByID = async ( + collectionID: number, +): Promise => { + const res = await fetch(await apiURL(`/collections/${collectionID}`), { + headers: await authenticatedRequestHeaders(), + }); + ensureOk(res); + const { collection } = CollectionResponse.parse(await res.json()); + return decryptRemoteKeyAndCollection(collection); }; /** diff --git a/web/packages/new/photos/services/collections.ts b/web/packages/new/photos/services/collections.ts index 34fac14765..f2b37e6d69 100644 --- a/web/packages/new/photos/services/collections.ts +++ b/web/packages/new/photos/services/collections.ts @@ -31,7 +31,7 @@ import HTTPService from "ente-shared/network/HTTPService"; import localForage from "ente-shared/storage/localForage"; import { getData } from "ente-shared/storage/localStorage"; import { getToken } from "ente-shared/storage/localStorage/helpers"; -import { isHiddenCollection } from "./collection"; +import { getCollectionByID, isHiddenCollection } from "./collection"; import { ensureUserKeyPair } from "./user"; const COLLECTION_TABLE = "collections"; @@ -292,34 +292,6 @@ export const getCollectionWithSecrets = async ( }; }; -export const getCollection = async ( - collectionID: number, -): Promise => { - try { - const token = getToken(); - if (!token) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return; - } - const resp = await HTTPService.get( - await apiURL(`/collections/${collectionID}`), - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - null, - { "X-Auth-Token": token }, - ); - const collectionWithSecrets = await getCollectionWithSecrets( - resp.data?.collection, - await ensureMasterKeyFromSession(), - ); - return collectionWithSecrets; - } catch (e) { - log.error("failed to get collection", e); - throw e; - } -}; - const TRASH_TIME = "trash-time"; const DELETED_COLLECTION = "deleted-collection"; @@ -402,7 +374,7 @@ export async function syncTrash( const collectionID = trashItem.file.collectionID; let collection = collectionByID.get(collectionID); if (!collection) { - collection = await getCollection(collectionID); + collection = await getCollectionByID(collectionID); collectionByID.set(collectionID, collection); await localForage.setItem(DELETED_COLLECTION, [ ...collectionByID.values(),