From 4dc3421ab93e4bb2d3ccc4d6abe97b2be77bd25b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 18 Jun 2025 09:16:52 +0530 Subject: [PATCH] Split --- .../gallery/services/upload/remote.ts | 58 +++++++++++++++++-- .../gallery/services/upload/upload-service.ts | 31 ++++------ web/packages/media/file.ts | 52 ++++++++--------- 3 files changed, 89 insertions(+), 52 deletions(-) diff --git a/web/packages/gallery/services/upload/remote.ts b/web/packages/gallery/services/upload/remote.ts index 53cd3b6718..af78b9f7c9 100644 --- a/web/packages/gallery/services/upload/remote.ts +++ b/web/packages/gallery/services/upload/remote.ts @@ -16,7 +16,6 @@ import { type EncryptedMagicMetadata, type EnteFile, type RemoteFileMetadata, - type S3FileAttributes, } from "ente-media/file"; import { handleUploadError } from "ente-shared/error"; import HTTPService from "ente-shared/network/HTTPService"; @@ -415,16 +414,67 @@ export const completeMultipartUploadViaWorker = async ( }), ); -interface PostEnteFileRequest { +export interface PostEnteFileRequest { collectionID: number; encryptedKey: string; keyDecryptionNonce: string; - file: S3FileAttributes; - thumbnail: S3FileAttributes; + file: UploadedFileObjectAttributes; + thumbnail: UploadedFileObjectAttributes; metadata: RemoteFileMetadata; pubMagicMetadata: EncryptedMagicMetadata; } +/** + * Attributes about an object uploaded to S3. + * + * This is similar to the {@link FileObjectAttributes} that we get back in the + * {@link EnteFile} we get from remote, however it contains more fields. + * + * - When we're finalizing the upload of {@link EnteFile}, we have at our + * disposal the {@link objectKey}, {@link decryptionHeader} and {@link size} + * attributes that we need to set in the POST "/files" request. + * + * - Later when we get back the file from remote as an {@link EnteFile}, it will + * only have the {@link decryptionHeader}. This is all we need for obtaining + * the decrypted file: the contents of the file get fetched (on demand) using + * a presigned URL, and the file's key is the decryption key, so armed with + * this decryption header we are good to go. + */ +export interface UploadedFileObjectAttributes { + /** + * The "key" (unique ID) of the S3 object that was uploaded. + * + * This is not related to encryption, it is the "objectKey" that uniquely + * identifies the S3 object that was uploaded. We get these as part of the + * {@link ObjectUploadURL} we get from remote. We upload the contents of the + * object (e.g. file, thumbnail) to the corresponding URL, and then report + * back the "objectKey" in the upload finalization request. + */ + objectKey: string; + /** + * The decryption header that was used when encrypting the objects's + * contents (with the file's key) before uploading them to S3 remote. + * + * The {@link decryptionHeader} is both required when finalizing the upload, + * and is also returned as part of the {@link EnteFile} that clients get + * back from remote since it is needed (along with the file's key) to + * decrypt the object that the client would download from S3 remote. + */ + decryptionHeader: string; + /** + * The size of the uploaded object, in bytes. + * + * For both file and thumbnails, the client also sends the size of the + * encrypted file (as per the client) while creating a new object on remote. + * This allows the server to validate that the size of the objects is same + * as what client is reporting. + * + * This should be present during upload, but is not returned back from + * remote in the /diff response. + */ + size: number; +} + /** * Create a new {@link EnteFile} on remote by providing remote with information * about the file's contents (objects) that were uploaded, and other metadata diff --git a/web/packages/gallery/services/upload/upload-service.ts b/web/packages/gallery/services/upload/upload-service.ts index e91ed9c139..a0a0a852bb 100644 --- a/web/packages/gallery/services/upload/upload-service.ts +++ b/web/packages/gallery/services/upload/upload-service.ts @@ -33,8 +33,6 @@ import type { EnteFile, FilePublicMagicMetadata, FilePublicMagicMetadataProps, - RemoteFileMetadata, - S3FileAttributes, } from "ente-media/file"; import { metadataHash, @@ -71,6 +69,7 @@ import { putFileViaWorker, type MultipartCompletedPart, type ObjectUploadURL, + type PostEnteFileRequest, } from "./remote"; import { fallbackThumbnail, @@ -173,10 +172,10 @@ class UploadService { this.ensureUniqueUploadURLs(); } - async uploadFile(uploadFile: UploadFile) { + async postFile(file: PostEnteFileRequest) { return this.publicAlbumsCredentials - ? postPublicAlbumsEnteFile(uploadFile, this.publicAlbumsCredentials) - : postEnteFile(uploadFile); + ? postPublicAlbumsEnteFile(file, this.publicAlbumsCredentials) + : postEnteFile(file); } private async refillUploadURLs() { @@ -341,19 +340,6 @@ interface EncryptedFilePieces { localID: number; } -export interface BackupedFile { - file: S3FileAttributes; - thumbnail: S3FileAttributes; - metadata: RemoteFileMetadata; - pubMagicMetadata: EncryptedMagicMetadata; -} - -export interface UploadFile extends BackupedFile { - collectionID: number; - encryptedKey: string; - keyDecryptionNonce: string; -} - export interface PotentialLivePhotoAsset { fileName: string; fileType: FileType; @@ -716,7 +702,7 @@ export const upload = async ( abortIfCancelled(); - const uploadedFile = await uploadService.uploadFile({ + const uploadedFile = await uploadService.postFile({ collectionID: collection.id, encryptedKey: encryptedFileKey.encryptedData, keyDecryptionNonce: encryptedFileKey.nonce, @@ -1506,7 +1492,12 @@ const encryptFileStream = async ( const uploadToBucket = async ( encryptedFilePieces: EncryptedFilePieces, uploadContext: UploadContext, -): Promise => { +): Promise< + Pick< + PostEnteFileRequest, + "file" | "thumbnail" | "metadata" | "pubMagicMetadata" + > +> => { const { isCFUploadProxyDisabled, abortIfCancelled, updateUploadProgress } = uploadContext; diff --git a/web/packages/media/file.ts b/web/packages/media/file.ts index 8dca9dd307..c9b02eb6cc 100644 --- a/web/packages/media/file.ts +++ b/web/packages/media/file.ts @@ -44,9 +44,16 @@ export interface EncryptedEnteFile { * {@link FilePublicMagicMetadataData}). */ ownerID: number; - file: S3FileAttributes; - thumbnail: S3FileAttributes; - metadata: RemoteFileMetadata; + /** + * Information pertaining to the encrypted S3 object that has the file's + * contents. + */ + file: FileObjectAttributes; + /** + * Information pertaining to the encrypted S3 object that has the contents + * of the file's thumbnail. + */ + thumbnail: FileObjectAttributes; /** * Static, remote visible, information associated with a file. * @@ -57,6 +64,7 @@ export interface EncryptedEnteFile { * Files uploaded by very old versions of Ente might not have this field. */ info?: FileInfo; + metadata: RemoteFileMetadata; magicMetadata: EncryptedMagicMetadata; pubMagicMetadata: EncryptedMagicMetadata; /** @@ -172,36 +180,24 @@ export interface EnteFile } /** - * Attributes about an object uploaded to S3. + * Attributes about an object related to the file * - * TODO: Split between fields needed during upload, and the fields we get back - * from remote in the /diff response. + * - The file's contents, + * + * - The file's thumbnail's contents. */ -export interface S3FileAttributes { +export interface FileObjectAttributes { /** - * Upload only: This should be present during upload, but is not returned - * back from remote in the /diff response. - */ - objectKey: string; - /** - * Upload and diff: This is present both during upload and also returned by - * remote in the /diff response. + * The decryption header that was used when encrypting the objects's + * contents (with the file's key) before uploading them to S3 remote. */ decryptionHeader: string; - /** - * The size of the file, in bytes. - * - * For both file and thumbnails, the client also sends the size of the - * encrypted file (as per the client) while creating a new object on remote. - * This allows the server to validate that the size of the objects is same - * as what client is reporting. - * - * Upload only: This should be present during upload, but is not returned - * back from remote in the /diff response. - */ - size: number; } +const RemoteFileObjectAttributes = z.looseObject({ + decryptionHeader: z.string(), +}); + /** * Static information associated with a file. */ @@ -266,8 +262,8 @@ export const RemoteEnteFile = z.looseObject({ * Base64 encoded. */ keyDecryptionNonce: z.string(), - file: z.unknown(), - thumbnail: z.unknown(), + file: RemoteFileObjectAttributes, + thumbnail: RemoteFileObjectAttributes, info: RemoteFileInfo.nullish().transform(nullToUndefined), updationTime: z.number(), /**