This commit is contained in:
Manav Rathi
2025-06-18 09:16:52 +05:30
parent ce9c227780
commit 4dc3421ab9
3 changed files with 89 additions and 52 deletions

View File

@@ -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

View File

@@ -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<BackupedFile> => {
): Promise<
Pick<
PostEnteFileRequest,
"file" | "thumbnail" | "metadata" | "pubMagicMetadata"
>
> => {
const { isCFUploadProxyDisabled, abortIfCancelled, updateUploadProgress } =
uploadContext;

View File

@@ -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(),
/**