From 71bdc143857b13d31440ce555a0fe56535699a30 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Mon, 8 Dec 2025 17:54:53 +0530 Subject: [PATCH 01/21] Installed `tus-js-client` version 4.3.1 --- package-lock.json | 108 +++++++++++++++++++++++++++++++++++----------- package.json | 1 + 2 files changed, 83 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index e583faf3e..7b2be179f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,6 +59,7 @@ "strip-ansi": "^7.1.2", "tar": "^7.5.2", "ts-node": "^10.9.2", + "tus-js-client": "^4.3.1", "unzipper": "^0.12.3", "winreg": "^1.2.5", "wpcom": "^7.1.1", @@ -20665,9 +20666,9 @@ } }, "node_modules/js-base64": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", - "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", + "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", "license": "BSD-3-Clause" }, "node_modules/js-tokens": { @@ -24352,25 +24353,14 @@ } }, "node_modules/proper-lockfile": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-2.0.1.tgz", - "integrity": "sha512-rjaeGbsmhNDcDInmwi4MuI6mRwJu6zq8GjYCLuSuE7GF+4UjgzkL69sVKKJ2T2xH61kK7rXvGYpvaTu909oXaQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "retry": "^0.10.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/proper-lockfile/node_modules/retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==", - "license": "MIT", - "engines": { - "node": "*" + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" } }, "node_modules/property-information": { @@ -25301,7 +25291,6 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -27527,18 +27516,21 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tus-js-client": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-2.3.2.tgz", - "integrity": "sha512-5a2rm7gp+G7Z+ZB0AO4PzD/dwczB3n1fZeWO5W8AWLJ12RRk1rY4Aeb2VAYX9oKGE+/rGPrdxoFPA/vDSVKnpg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-4.3.1.tgz", + "integrity": "sha512-ZLeYmjrkaU1fUsKbIi8JML52uAocjEZtBx4DKjRrqzrZa0O4MYwT6db+oqePlspV+FxXJAyFBc/L5gwUi2OFsg==", "license": "MIT", "dependencies": { "buffer-from": "^1.1.2", "combine-errors": "^3.0.3", "is-stream": "^2.0.0", - "js-base64": "^2.6.1", + "js-base64": "^3.7.2", "lodash.throttle": "^4.1.1", - "proper-lockfile": "^2.0.1", + "proper-lockfile": "^4.1.2", "url-parse": "^1.5.7" + }, + "engines": { + "node": ">=18" } }, "node_modules/tus-js-client/node_modules/is-stream": { @@ -29323,6 +29315,70 @@ "wp-error": "^1.3.0" } }, + "node_modules/wpcom-xhr-request/node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/wpcom/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wpcom/node_modules/js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "license": "BSD-3-Clause" + }, + "node_modules/wpcom/node_modules/proper-lockfile": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-2.0.1.tgz", + "integrity": "sha512-rjaeGbsmhNDcDInmwi4MuI6mRwJu6zq8GjYCLuSuE7GF+4UjgzkL69sVKKJ2T2xH61kK7rXvGYpvaTu909oXaQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "retry": "^0.10.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/wpcom/node_modules/retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/wpcom/node_modules/tus-js-client": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-2.3.2.tgz", + "integrity": "sha512-5a2rm7gp+G7Z+ZB0AO4PzD/dwczB3n1fZeWO5W8AWLJ12RRk1rY4Aeb2VAYX9oKGE+/rGPrdxoFPA/vDSVKnpg==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.1.2", + "combine-errors": "^3.0.3", + "is-stream": "^2.0.0", + "js-base64": "^2.6.1", + "lodash.throttle": "^4.1.1", + "proper-lockfile": "^2.0.1", + "url-parse": "^1.5.7" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 0c3478445..ba4f220b8 100644 --- a/package.json +++ b/package.json @@ -153,6 +153,7 @@ "strip-ansi": "^7.1.2", "tar": "^7.5.2", "ts-node": "^10.9.2", + "tus-js-client": "^4.3.1", "unzipper": "^0.12.3", "winreg": "^1.2.5", "wpcom": "^7.1.1", From 145f286533a974a14310fc522b0895ac631c62aa Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Mon, 8 Dec 2025 17:55:04 +0530 Subject: [PATCH 02/21] Add studio file upload constants --- src/constants.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/constants.ts b/src/constants.ts index 2414ea63d..10982cfde 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -108,4 +108,10 @@ export const IPC_VOID_HANDLERS = < const >[ ]; // What's New -export const FORCE_WHATS_NEW_WHEN_PATCH_CHANGED = false; +export const FORCE_WHATS_NEW_WHEN_PATCH_CHANGED = true; + +// Studio File Upload constants +export const STUDIO_FILE_UPLOADS_ENDPOINT_BASE = + 'https://public-api.wordpress.com/rest/v1.1/studio-file-uploads'; +export const STUDIO_FILE_UPLOADS_CHUNK_SIZE = 500000; +export const STUDIO_FILE_UPLOADS_RETRY_DELAYS = [ 0, 1000, 3000, 5000, 10000, 25000 ]; From b1998ed99206e44f740fae0dd41856aeb516b9af Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Mon, 8 Dec 2025 17:56:45 +0530 Subject: [PATCH 03/21] Upload file using tus protocol --- src/hooks/sync-sites/use-sync-push.ts | 1 + src/modules/sync/lib/ipc-handlers.ts | 96 ++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/src/hooks/sync-sites/use-sync-push.ts b/src/hooks/sync-sites/use-sync-push.ts index 1bb778c87..8fd5daf16 100644 --- a/src/hooks/sync-sites/use-sync-push.ts +++ b/src/hooks/sync-sites/use-sync-push.ts @@ -308,6 +308,7 @@ export function useSyncPush( { try { const response = await getIpcApi().pushArchive( + selectedSite.id, remoteSiteId, archivePath, options?.optionsToSync, diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index 956d9764f..0f5c61102 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -3,12 +3,19 @@ import fs from 'fs'; import fsPromises from 'fs/promises'; import path from 'node:path'; import * as Sentry from '@sentry/electron/main'; +import { HttpRequest, HttpResponse, Upload } from 'tus-js-client'; import { z } from 'zod'; import { isErrnoException } from 'common/lib/is-errno-exception'; +import { + STUDIO_FILE_UPLOADS_ENDPOINT_BASE, + STUDIO_FILE_UPLOADS_CHUNK_SIZE, + STUDIO_FILE_UPLOADS_RETRY_DELAYS, +} from 'src/constants'; import { PullStateProgressInfo, PushStateProgressInfo, } from 'src/hooks/use-sync-states-progress-info'; +import { sendIpcEventToRenderer } from 'src/ipc-utils'; import { ACTIVE_SYNC_OPERATIONS } from 'src/lib/active-sync-operations'; import { download } from 'src/lib/download'; import { getSyncBackupTempPath } from 'src/lib/get-sync-backup-temp-path'; @@ -147,6 +154,7 @@ export function removeExportedSiteTmpFile( event: IpcMainInvokeEvent, path: stri export async function pushArchive( event: IpcMainInvokeEvent, + selectedSiteId: string, remoteSiteId: number, archivePath: string, optionsToSync?: string[], @@ -158,16 +166,88 @@ export async function pushArchive( throw new Error( 'No token found' ); } + let isUploadingPaused = false; + const file = fs.createReadStream( archivePath ); + const fileSize = fs.statSync( archivePath ).size; + const filename = path.basename( archivePath ); + + const attachmentId = await new Promise< string >( ( resolve, reject ) => { + const upload = new Upload( file, { + endpoint: `${ STUDIO_FILE_UPLOADS_ENDPOINT_BASE }/${ remoteSiteId }`, + chunkSize: STUDIO_FILE_UPLOADS_CHUNK_SIZE, + retryDelays: STUDIO_FILE_UPLOADS_RETRY_DELAYS, + overridePatchMethod: true, + removeFingerprintOnSuccess: true, + storeFingerprintForResuming: true, + headers: { + Authorization: `Bearer ${ token.accessToken }`, + }, + metadata: { + filename, + filetype: 'application/gzip', + }, + uploadSize: fileSize, + onBeforeRequest: ( req: HttpRequest ) => { + if ( req.getMethod() === 'HEAD' ) { + // @ts-expect-error We need to override the method to get the response headers. + req._method = 'GET'; + req.setHeader( 'X-HTTP-Method-Override', 'HEAD' ); + } + }, + onError: ( error ) => { + console.error( '[TUS] Upload error', error ); + reject( error ); + }, + onProgress: ( bytesSent, bytesTotal ) => { + if ( isUploadingPaused ) { + isUploadingPaused = false; + void sendIpcEventToRenderer( 'studio-file-upload-resumed', { + selectedSiteId: selectedSiteId, + remoteSiteId: remoteSiteId, + } ); + console.log( '[TUS] Upload resumed' ); + } + + if ( bytesTotal > 0 ) { + const progress = ( bytesSent / bytesTotal ) * 100; + console.log( '[TUS] Upload progress: %s%%', progress.toFixed( 2 ) ); + } + }, + onSuccess: ( payload: { lastResponse: HttpResponse } ) => { + if ( ! payload.lastResponse ) { + console.error( 'Upload completed but no response received' ); + reject( new Error( 'Upload completed but no response received' ) ); + return; + } + + file.destroy(); + file.close(); + const attachmentId = payload.lastResponse.getHeader( 'x-videopress-upload-media-id' ); + if ( attachmentId ) { + resolve( attachmentId ); + } else { + console.error( 'Upload completed but required header not found' ); + reject( new Error( 'Upload completed but required header not found' ) ); + } + }, + onShouldRetry: ( error: Error ) => { + isUploadingPaused = true; + void sendIpcEventToRenderer( 'studio-file-upload-paused', { + selectedSiteId: selectedSiteId, + remoteSiteId: remoteSiteId, + error: error.message, + } ); + console.error( 'Upload request error', error.message ); + return true; + }, + } ); + + upload.start(); + } ); + const wpcom = wpcomFactory( token.accessToken, wpcomXhrRequest ); const formData: [ string, unknown, Record< string, string >? ][] = [ - [ - 'import', - fs.createReadStream( archivePath ), - { - filename: 'loca-env-site-1.tar.gz', - contentType: 'application/gzip', - }, - ], + [ 'import_attachment_id', attachmentId ], ]; if ( specificSelectionPaths && specificSelectionPaths.length > 0 ) { From 8fbdfbf8cca408d887d448cf5287e7ec7a1b316d Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Mon, 8 Dec 2025 17:58:23 +0530 Subject: [PATCH 04/21] Add support for file upload pause and resume states --- src/hooks/sync-sites/use-sync-push.ts | 19 +++++++++++++++++++ src/hooks/use-sync-states-progress-info.ts | 16 +++++++++++++++- src/ipc-utils.ts | 2 ++ src/lib/active-sync-operations.ts | 7 ++++++- 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/hooks/sync-sites/use-sync-push.ts b/src/hooks/sync-sites/use-sync-push.ts index 8fd5daf16..d183c1d31 100644 --- a/src/hooks/sync-sites/use-sync-push.ts +++ b/src/hooks/sync-sites/use-sync-push.ts @@ -11,6 +11,7 @@ import { usePullPushStates, } from 'src/hooks/sync-sites/use-pull-push-states'; import { useAuth } from 'src/hooks/use-auth'; +import { useIpcListener } from 'src/hooks/use-ipc-listener'; import { useSyncStatesProgressInfo, PushStateProgressInfo, @@ -379,6 +380,24 @@ export function useSyncPush( { isKeyCancelled, ] ); + useIpcListener( + 'studio-file-upload-paused', + ( _event, payload: { selectedSiteId: string; remoteSiteId: number; error: string } ) => { + updatePushState( payload.selectedSiteId, payload.remoteSiteId, { + status: pushStatesProgressInfo.uploadingPaused, + } ); + } + ); + + useIpcListener( + 'studio-file-upload-resumed', + ( _event, payload: { selectedSiteId: string; remoteSiteId: number } ) => { + updatePushState( payload.selectedSiteId, payload.remoteSiteId, { + status: pushStatesProgressInfo.uploading, + } ); + } + ); + const isAnySitePushing = useMemo< boolean >( () => { return Object.values( pushStates ).some( ( state ) => isKeyPushing( state.status.key ) ); }, [ pushStates, isKeyPushing ] ); diff --git a/src/hooks/use-sync-states-progress-info.ts b/src/hooks/use-sync-states-progress-info.ts index 1057ed7f2..e173c1a8c 100644 --- a/src/hooks/use-sync-states-progress-info.ts +++ b/src/hooks/use-sync-states-progress-info.ts @@ -16,7 +16,9 @@ export type PushStateProgressInfo = { | 'finishing' | 'finished' | 'failed' - | 'cancelled'; + | 'cancelled' + | 'uploadingPaused' + | 'uploadingResumed'; progress: number; message: string; }; @@ -105,6 +107,16 @@ export function useSyncStatesProgressInfo() { progress: 40, message: __( 'Uploading Studio site…' ), }, + uploadingPaused: { + key: 'uploadingPaused', + progress: 45, + message: __( 'Uploading paused, retrying…' ), + }, + uploadingResumed: { + key: 'uploadingResumed', + progress: 45, + message: __( 'Uploading resumed…' ), + }, creatingRemoteBackup: { key: 'creatingRemoteBackup', progress: 50, @@ -154,6 +166,8 @@ export function useSyncStatesProgressInfo() { const pushingStateKeys: PushStateProgressInfo[ 'key' ][] = [ 'creatingBackup', 'uploading', + 'uploadingPaused', + 'uploadingResumed', 'creatingRemoteBackup', 'applyingChanges', 'finishing', diff --git a/src/ipc-utils.ts b/src/ipc-utils.ts index 7e1a91059..6aeaca894 100644 --- a/src/ipc-utils.ts +++ b/src/ipc-utils.ts @@ -38,6 +38,8 @@ export interface IpcEvents { }, ]; 'site-context-menu-action': [ { action: string; siteId: string } ]; + 'studio-file-upload-paused': [ { error: string; selectedSiteId: string; remoteSiteId: number } ]; + 'studio-file-upload-resumed': [ { selectedSiteId: string; remoteSiteId: number } ]; 'snapshot-error': [ { operationId: crypto.UUID; data: SnapshotEventData } ]; 'snapshot-fatal-error': [ { operationId: crypto.UUID; data: { message: string } } ]; 'snapshot-output': [ { operationId: crypto.UUID; data: SnapshotEventData } ]; diff --git a/src/lib/active-sync-operations.ts b/src/lib/active-sync-operations.ts index 3e31f2f46..b5a2e38b1 100644 --- a/src/lib/active-sync-operations.ts +++ b/src/lib/active-sync-operations.ts @@ -44,7 +44,12 @@ export function canCancelPush( key: PushStateProgressInfo[ 'key' ] | undefined ) * Check if a push operation has finished uploading the backup file. */ export function pushBackupIsUploading( key: PushStateProgressInfo[ 'key' ] | undefined ): boolean { - const uploadingStateKeys: PushStateProgressInfo[ 'key' ][] = [ 'creatingBackup', 'uploading' ]; + const uploadingStateKeys: PushStateProgressInfo[ 'key' ][] = [ + 'creatingBackup', + 'uploading', + 'uploadingPaused', + 'uploadingResumed', + ]; if ( ! key ) { return false; } From b5e11c323d9f660b20c293f9d8411b2b6068023b Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Mon, 8 Dec 2025 18:02:21 +0530 Subject: [PATCH 05/21] Add cleanup function in pushArchive function clear file streams --- src/modules/sync/lib/ipc-handlers.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index 0f5c61102..2f1b9b962 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -171,6 +171,11 @@ export async function pushArchive( const fileSize = fs.statSync( archivePath ).size; const filename = path.basename( archivePath ); + const cleanup = () => { + file.destroy(); + file.close(); + }; + const attachmentId = await new Promise< string >( ( resolve, reject ) => { const upload = new Upload( file, { endpoint: `${ STUDIO_FILE_UPLOADS_ENDPOINT_BASE }/${ remoteSiteId }`, @@ -195,6 +200,7 @@ export async function pushArchive( } }, onError: ( error ) => { + cleanup(); console.error( '[TUS] Upload error', error ); reject( error ); }, @@ -214,14 +220,14 @@ export async function pushArchive( } }, onSuccess: ( payload: { lastResponse: HttpResponse } ) => { + cleanup(); + if ( ! payload.lastResponse ) { console.error( 'Upload completed but no response received' ); reject( new Error( 'Upload completed but no response received' ) ); return; } - file.destroy(); - file.close(); const attachmentId = payload.lastResponse.getHeader( 'x-videopress-upload-media-id' ); if ( attachmentId ) { resolve( attachmentId ); From a1efd63ed4574d63b5720e9d632d94dc37205662 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Mon, 8 Dec 2025 18:03:01 +0530 Subject: [PATCH 06/21] Refactor pushArchive function to include selectedSiteId as a parameter --- src/preload.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/preload.ts b/src/preload.ts index 8201e2f8b..df6b1e963 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -23,9 +23,16 @@ function ipcRendererSend< T extends keyof IpcHandlers >( const api: IpcApi = { exportSiteForPush: ( id, operationId, configuration ) => ipcRendererInvoke( 'exportSiteForPush', id, operationId, configuration ), - pushArchive: ( remoteSiteId, archivePath, optionsToSync, specificSelectionPaths ) => + pushArchive: ( + selectedSiteId, + remoteSiteId, + archivePath, + optionsToSync, + specificSelectionPaths + ) => ipcRendererInvoke( 'pushArchive', + selectedSiteId, remoteSiteId, archivePath, optionsToSync, From 33c30e4f9dd799cd47ae67f0dadcc718a8f8bd77 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Tue, 9 Dec 2025 11:50:29 +0530 Subject: [PATCH 07/21] Add 'uploadingPaused' state check and tooltip notification for connection issues. --- src/hooks/use-sync-states-progress-info.ts | 6 +++++- .../sync/components/sync-connected-sites.tsx | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/hooks/use-sync-states-progress-info.ts b/src/hooks/use-sync-states-progress-info.ts index e173c1a8c..ab2d6be85 100644 --- a/src/hooks/use-sync-states-progress-info.ts +++ b/src/hooks/use-sync-states-progress-info.ts @@ -166,7 +166,6 @@ export function useSyncStatesProgressInfo() { const pushingStateKeys: PushStateProgressInfo[ 'key' ][] = [ 'creatingBackup', 'uploading', - 'uploadingPaused', 'uploadingResumed', 'creatingRemoteBackup', 'applyingChanges', @@ -178,6 +177,10 @@ export function useSyncStatesProgressInfo() { return pushingStateKeys.includes( key ); }; + const isKeyUploadingPaused = ( key: PushStateProgressInfo[ 'key' ] | undefined ) => { + return key === 'uploadingPaused'; + }; + const isKeyImporting = ( key: PushStateProgressInfo[ 'key' ] | undefined ) => { const pushingStateKeys: PushStateProgressInfo[ 'key' ][] = [ 'creatingRemoteBackup', @@ -310,5 +313,6 @@ export function useSyncStatesProgressInfo() { getBackupStatusWithProgress, getPullStatusWithProgress, getPushStatusWithProgress, + isKeyUploadingPaused, }; } diff --git a/src/modules/sync/components/sync-connected-sites.tsx b/src/modules/sync/components/sync-connected-sites.tsx index fd2cb10de..2a6a2f48a 100644 --- a/src/modules/sync/components/sync-connected-sites.tsx +++ b/src/modules/sync/components/sync-connected-sites.tsx @@ -1,7 +1,7 @@ import { Icon } from '@wordpress/components'; import { createInterpolateElement } from '@wordpress/element'; import { sprintf } from '@wordpress/i18n'; -import { cloudUpload, cloudDownload, info, close } from '@wordpress/icons'; +import { cloudUpload, cloudDownload, info, close, error } from '@wordpress/icons'; import { useI18n } from '@wordpress/react-i18n'; import { useState } from 'react'; import { ArrowIcon } from 'src/components/arrow-icon'; @@ -199,6 +199,7 @@ const SyncConnectedSitesSectionItem = ( { isKeyFailed, isKeyCancelled, getPullStatusWithProgress, + isKeyUploadingPaused, } = useSyncStatesProgressInfo(); const sitePullState = getPullState( selectedSite.id, connectedSite.id ); @@ -211,6 +212,7 @@ const SyncConnectedSitesSectionItem = ( { const pushState = getPushState( selectedSite.id, connectedSite.id ); const isPushing = pushState && isKeyPushing( pushState.status.key ); + const isUploadingPaused = pushState && isKeyUploadingPaused( pushState.status.key ); const isPushError = pushState && isKeyFailed( pushState.status.key ); const hasPushFinished = pushState && isKeyFinished( pushState.status.key ); const hasPushCancelled = pushState && isKeyCancelled( pushState.status.key ); @@ -292,6 +294,19 @@ const SyncConnectedSitesSectionItem = ( { { __( 'Pull complete' ) } ) } + { pushState?.status && isUploadingPaused && ( + + + + ) } { pushState?.status && isPushing && (
Date: Tue, 9 Dec 2025 12:03:32 +0530 Subject: [PATCH 08/21] Remove 'uploadingPaused' state from the list of active upload states --- src/lib/active-sync-operations.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/active-sync-operations.ts b/src/lib/active-sync-operations.ts index b5a2e38b1..319739616 100644 --- a/src/lib/active-sync-operations.ts +++ b/src/lib/active-sync-operations.ts @@ -47,7 +47,6 @@ export function pushBackupIsUploading( key: PushStateProgressInfo[ 'key' ] | und const uploadingStateKeys: PushStateProgressInfo[ 'key' ][] = [ 'creatingBackup', 'uploading', - 'uploadingPaused', 'uploadingResumed', ]; if ( ! key ) { From 521af7f390874caa1173fd7078bf7e58f1f1c8fb Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 14:07:50 +0530 Subject: [PATCH 09/21] Update API path to initiate sync import --- src/modules/sync/lib/ipc-handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index 2f1b9b962..2eababd10 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -267,7 +267,7 @@ export async function pushArchive( try { await wpcom.req.post( { - path: `/sites/${ remoteSiteId }/studio-app/sync/import`, + path: `/sites/${ remoteSiteId }/studio-app/sync/import/initiate`, apiNamespace: 'wpcom/v2', formData, } ); From 961df228b6818a24ed6314764325aad693927d68 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 14:12:25 +0530 Subject: [PATCH 10/21] Refactor logic to handle file cleanup after upload completion --- src/modules/sync/lib/ipc-handlers.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index 2eababd10..f62d4c257 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -171,12 +171,7 @@ export async function pushArchive( const fileSize = fs.statSync( archivePath ).size; const filename = path.basename( archivePath ); - const cleanup = () => { - file.destroy(); - file.close(); - }; - - const attachmentId = await new Promise< string >( ( resolve, reject ) => { + const attachmentPromise = new Promise< string >( ( resolve, reject ) => { const upload = new Upload( file, { endpoint: `${ STUDIO_FILE_UPLOADS_ENDPOINT_BASE }/${ remoteSiteId }`, chunkSize: STUDIO_FILE_UPLOADS_CHUNK_SIZE, @@ -200,7 +195,6 @@ export async function pushArchive( } }, onError: ( error ) => { - cleanup(); console.error( '[TUS] Upload error', error ); reject( error ); }, @@ -220,8 +214,6 @@ export async function pushArchive( } }, onSuccess: ( payload: { lastResponse: HttpResponse } ) => { - cleanup(); - if ( ! payload.lastResponse ) { console.error( 'Upload completed but no response received' ); reject( new Error( 'Upload completed but no response received' ) ); @@ -249,8 +241,12 @@ export async function pushArchive( } ); upload.start(); + } ).finally( () => { + file.destroy(); + file.close(); } ); + const attachmentId = await attachmentPromise; const wpcom = wpcomFactory( token.accessToken, wpcomXhrRequest ); const formData: [ string, unknown, Record< string, string >? ][] = [ [ 'import_attachment_id', attachmentId ], From 773f555378b12edb798e76ef47b0c08cca22dde2 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 14:15:57 +0530 Subject: [PATCH 11/21] Remove constants and use values --- src/constants.ts | 6 ------ src/modules/sync/lib/ipc-handlers.ts | 11 +++-------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 10982cfde..7e0ec5e4e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -109,9 +109,3 @@ export const IPC_VOID_HANDLERS = < const >[ // What's New export const FORCE_WHATS_NEW_WHEN_PATCH_CHANGED = true; - -// Studio File Upload constants -export const STUDIO_FILE_UPLOADS_ENDPOINT_BASE = - 'https://public-api.wordpress.com/rest/v1.1/studio-file-uploads'; -export const STUDIO_FILE_UPLOADS_CHUNK_SIZE = 500000; -export const STUDIO_FILE_UPLOADS_RETRY_DELAYS = [ 0, 1000, 3000, 5000, 10000, 25000 ]; diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index f62d4c257..9a723eb90 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -6,11 +6,6 @@ import * as Sentry from '@sentry/electron/main'; import { HttpRequest, HttpResponse, Upload } from 'tus-js-client'; import { z } from 'zod'; import { isErrnoException } from 'common/lib/is-errno-exception'; -import { - STUDIO_FILE_UPLOADS_ENDPOINT_BASE, - STUDIO_FILE_UPLOADS_CHUNK_SIZE, - STUDIO_FILE_UPLOADS_RETRY_DELAYS, -} from 'src/constants'; import { PullStateProgressInfo, PushStateProgressInfo, @@ -173,9 +168,9 @@ export async function pushArchive( const attachmentPromise = new Promise< string >( ( resolve, reject ) => { const upload = new Upload( file, { - endpoint: `${ STUDIO_FILE_UPLOADS_ENDPOINT_BASE }/${ remoteSiteId }`, - chunkSize: STUDIO_FILE_UPLOADS_CHUNK_SIZE, - retryDelays: STUDIO_FILE_UPLOADS_RETRY_DELAYS, + endpoint: `https://public-api.wordpress.com/rest/v1.1/studio-file-uploads/${ remoteSiteId }`, + chunkSize: 500000, + retryDelays: [ 0, 1000, 3000, 5000, 10000, 25000 ], overridePatchMethod: true, removeFingerprintOnSuccess: true, storeFingerprintForResuming: true, From 264c01b5f115206e94221c2a3eeadf7fd8a6e39f Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 14:17:11 +0530 Subject: [PATCH 12/21] Simplify parameter types for request handlers --- src/modules/sync/lib/ipc-handlers.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index 9a723eb90..4b6c0434d 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -182,7 +182,7 @@ export async function pushArchive( filetype: 'application/gzip', }, uploadSize: fileSize, - onBeforeRequest: ( req: HttpRequest ) => { + onBeforeRequest: ( req ) => { if ( req.getMethod() === 'HEAD' ) { // @ts-expect-error We need to override the method to get the response headers. req._method = 'GET'; @@ -208,7 +208,7 @@ export async function pushArchive( console.log( '[TUS] Upload progress: %s%%', progress.toFixed( 2 ) ); } }, - onSuccess: ( payload: { lastResponse: HttpResponse } ) => { + onSuccess: ( payload ) => { if ( ! payload.lastResponse ) { console.error( 'Upload completed but no response received' ); reject( new Error( 'Upload completed but no response received' ) ); @@ -223,7 +223,7 @@ export async function pushArchive( reject( new Error( 'Upload completed but required header not found' ) ); } }, - onShouldRetry: ( error: Error ) => { + onShouldRetry: ( error ) => { isUploadingPaused = true; void sendIpcEventToRenderer( 'studio-file-upload-paused', { selectedSiteId: selectedSiteId, From df5adf69a05d95902bb622ec2f62aab334180a08 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 14:20:08 +0530 Subject: [PATCH 13/21] Remove 'uploadingResumed' state from sync progress info --- src/hooks/use-sync-states-progress-info.ts | 11 ++--------- src/lib/active-sync-operations.ts | 6 +----- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/hooks/use-sync-states-progress-info.ts b/src/hooks/use-sync-states-progress-info.ts index ab2d6be85..4864b40b0 100644 --- a/src/hooks/use-sync-states-progress-info.ts +++ b/src/hooks/use-sync-states-progress-info.ts @@ -17,8 +17,7 @@ export type PushStateProgressInfo = { | 'finished' | 'failed' | 'cancelled' - | 'uploadingPaused' - | 'uploadingResumed'; + | 'uploadingPaused'; progress: number; message: string; }; @@ -110,12 +109,7 @@ export function useSyncStatesProgressInfo() { uploadingPaused: { key: 'uploadingPaused', progress: 45, - message: __( 'Uploading paused, retrying…' ), - }, - uploadingResumed: { - key: 'uploadingResumed', - progress: 45, - message: __( 'Uploading resumed…' ), + message: __( 'Uploading paused' ), }, creatingRemoteBackup: { key: 'creatingRemoteBackup', @@ -166,7 +160,6 @@ export function useSyncStatesProgressInfo() { const pushingStateKeys: PushStateProgressInfo[ 'key' ][] = [ 'creatingBackup', 'uploading', - 'uploadingResumed', 'creatingRemoteBackup', 'applyingChanges', 'finishing', diff --git a/src/lib/active-sync-operations.ts b/src/lib/active-sync-operations.ts index 319739616..3e31f2f46 100644 --- a/src/lib/active-sync-operations.ts +++ b/src/lib/active-sync-operations.ts @@ -44,11 +44,7 @@ export function canCancelPush( key: PushStateProgressInfo[ 'key' ] | undefined ) * Check if a push operation has finished uploading the backup file. */ export function pushBackupIsUploading( key: PushStateProgressInfo[ 'key' ] | undefined ): boolean { - const uploadingStateKeys: PushStateProgressInfo[ 'key' ][] = [ - 'creatingBackup', - 'uploading', - 'uploadingResumed', - ]; + const uploadingStateKeys: PushStateProgressInfo[ 'key' ][] = [ 'creatingBackup', 'uploading' ]; if ( ! key ) { return false; } From baa2bec557e24a68f2eed2433a3cf96bf032de1d Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 14:22:28 +0530 Subject: [PATCH 14/21] Rename IPC events for file upload pause and resume --- src/hooks/sync-sites/use-sync-push.ts | 4 ++-- src/ipc-utils.ts | 4 ++-- src/modules/sync/lib/ipc-handlers.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hooks/sync-sites/use-sync-push.ts b/src/hooks/sync-sites/use-sync-push.ts index d183c1d31..6552b0afe 100644 --- a/src/hooks/sync-sites/use-sync-push.ts +++ b/src/hooks/sync-sites/use-sync-push.ts @@ -381,7 +381,7 @@ export function useSyncPush( { ] ); useIpcListener( - 'studio-file-upload-paused', + 'sync-upload-paused', ( _event, payload: { selectedSiteId: string; remoteSiteId: number; error: string } ) => { updatePushState( payload.selectedSiteId, payload.remoteSiteId, { status: pushStatesProgressInfo.uploadingPaused, @@ -390,7 +390,7 @@ export function useSyncPush( { ); useIpcListener( - 'studio-file-upload-resumed', + 'sync-upload-resumed', ( _event, payload: { selectedSiteId: string; remoteSiteId: number } ) => { updatePushState( payload.selectedSiteId, payload.remoteSiteId, { status: pushStatesProgressInfo.uploading, diff --git a/src/ipc-utils.ts b/src/ipc-utils.ts index 6aeaca894..9893ac993 100644 --- a/src/ipc-utils.ts +++ b/src/ipc-utils.ts @@ -38,8 +38,8 @@ export interface IpcEvents { }, ]; 'site-context-menu-action': [ { action: string; siteId: string } ]; - 'studio-file-upload-paused': [ { error: string; selectedSiteId: string; remoteSiteId: number } ]; - 'studio-file-upload-resumed': [ { selectedSiteId: string; remoteSiteId: number } ]; + 'sync-upload-paused': [ { error: string; selectedSiteId: string; remoteSiteId: number } ]; + 'sync-upload-resumed': [ { selectedSiteId: string; remoteSiteId: number } ]; 'snapshot-error': [ { operationId: crypto.UUID; data: SnapshotEventData } ]; 'snapshot-fatal-error': [ { operationId: crypto.UUID; data: { message: string } } ]; 'snapshot-output': [ { operationId: crypto.UUID; data: SnapshotEventData } ]; diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index 4b6c0434d..8776c6191 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -196,7 +196,7 @@ export async function pushArchive( onProgress: ( bytesSent, bytesTotal ) => { if ( isUploadingPaused ) { isUploadingPaused = false; - void sendIpcEventToRenderer( 'studio-file-upload-resumed', { + void sendIpcEventToRenderer( 'sync-upload-resumed', { selectedSiteId: selectedSiteId, remoteSiteId: remoteSiteId, } ); @@ -225,7 +225,7 @@ export async function pushArchive( }, onShouldRetry: ( error ) => { isUploadingPaused = true; - void sendIpcEventToRenderer( 'studio-file-upload-paused', { + void sendIpcEventToRenderer( 'sync-upload-paused', { selectedSiteId: selectedSiteId, remoteSiteId: remoteSiteId, error: error.message, From 963ff3d7b7b686e58f1398013caf23c1142286ea Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 14:31:07 +0530 Subject: [PATCH 15/21] Remove unused imports --- src/modules/sync/lib/ipc-handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index 8776c6191..ec093a978 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -3,7 +3,7 @@ import fs from 'fs'; import fsPromises from 'fs/promises'; import path from 'node:path'; import * as Sentry from '@sentry/electron/main'; -import { HttpRequest, HttpResponse, Upload } from 'tus-js-client'; +import { Upload } from 'tus-js-client'; import { z } from 'zod'; import { isErrnoException } from 'common/lib/is-errno-exception'; import { From 157addf02c48ba2092294487e411e448e2169155 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 14:47:53 +0530 Subject: [PATCH 16/21] Update logic to track upload start state and update UI when uploading pauses --- src/modules/sync/lib/ipc-handlers.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index ec093a978..6293a7934 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -161,6 +161,7 @@ export async function pushArchive( throw new Error( 'No token found' ); } + let hasUploadStarted = false; let isUploadingPaused = false; const file = fs.createReadStream( archivePath ); const fileSize = fs.statSync( archivePath ).size; @@ -203,6 +204,10 @@ export async function pushArchive( console.log( '[TUS] Upload resumed' ); } + if ( ! hasUploadStarted ) { + hasUploadStarted = true; + } + if ( bytesTotal > 0 ) { const progress = ( bytesSent / bytesTotal ) * 100; console.log( '[TUS] Upload progress: %s%%', progress.toFixed( 2 ) ); @@ -224,12 +229,16 @@ export async function pushArchive( } }, onShouldRetry: ( error ) => { - isUploadingPaused = true; - void sendIpcEventToRenderer( 'sync-upload-paused', { - selectedSiteId: selectedSiteId, - remoteSiteId: remoteSiteId, - error: error.message, - } ); + // Update the UI only if the upload has started and is paused for any reason. + if ( hasUploadStarted ) { + isUploadingPaused = true; + void sendIpcEventToRenderer( 'sync-upload-paused', { + selectedSiteId: selectedSiteId, + remoteSiteId: remoteSiteId, + error: error.message, + } ); + } + console.error( 'Upload request error', error.message ); return true; }, From d853ff86e05e661308381ce72983b1987e18d6e2 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 10 Dec 2025 15:26:58 +0530 Subject: [PATCH 17/21] Resolve merge conflicts --- src/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants.ts b/src/constants.ts index 7e0ec5e4e..2414ea63d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -108,4 +108,4 @@ export const IPC_VOID_HANDLERS = < const >[ ]; // What's New -export const FORCE_WHATS_NEW_WHEN_PATCH_CHANGED = true; +export const FORCE_WHATS_NEW_WHEN_PATCH_CHANGED = false; From 34577dd1a9161ea4b764dc1956662db152e3e0aa Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Thu, 11 Dec 2025 10:23:14 +0530 Subject: [PATCH 18/21] Remove unnecessary parameters and logging --- src/modules/sync/lib/ipc-handlers.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index 6293a7934..f3a243bca 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -194,7 +194,7 @@ export async function pushArchive( console.error( '[TUS] Upload error', error ); reject( error ); }, - onProgress: ( bytesSent, bytesTotal ) => { + onProgress: () => { if ( isUploadingPaused ) { isUploadingPaused = false; void sendIpcEventToRenderer( 'sync-upload-resumed', { @@ -207,15 +207,9 @@ export async function pushArchive( if ( ! hasUploadStarted ) { hasUploadStarted = true; } - - if ( bytesTotal > 0 ) { - const progress = ( bytesSent / bytesTotal ) * 100; - console.log( '[TUS] Upload progress: %s%%', progress.toFixed( 2 ) ); - } }, onSuccess: ( payload ) => { if ( ! payload.lastResponse ) { - console.error( 'Upload completed but no response received' ); reject( new Error( 'Upload completed but no response received' ) ); return; } @@ -224,7 +218,6 @@ export async function pushArchive( if ( attachmentId ) { resolve( attachmentId ); } else { - console.error( 'Upload completed but required header not found' ); reject( new Error( 'Upload completed but required header not found' ) ); } }, @@ -238,8 +231,6 @@ export async function pushArchive( error: error.message, } ); } - - console.error( 'Upload request error', error.message ); return true; }, } ); From c00837d6a11f0ed2415f84644578ca02660f2894 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Thu, 11 Dec 2025 10:25:47 +0530 Subject: [PATCH 19/21] Add error logging for paused uploads in TUS handler --- src/modules/sync/lib/ipc-handlers.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index f3a243bca..d51ed0f72 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -230,6 +230,7 @@ export async function pushArchive( remoteSiteId: remoteSiteId, error: error.message, } ); + console.error( '[TUS] Upload paused: ', error.message ); } return true; }, From 9275e52f4c9402ecb4aa11b6120a535f200a5765 Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Mon, 15 Dec 2025 15:38:08 +0530 Subject: [PATCH 20/21] Update header key for attachment id for pushing archives --- src/modules/sync/lib/ipc-handlers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index d51ed0f72..cc6fd3da0 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -214,7 +214,7 @@ export async function pushArchive( return; } - const attachmentId = payload.lastResponse.getHeader( 'x-videopress-upload-media-id' ); + const attachmentId = payload.lastResponse.getHeader( 'x-studio-file-upload-media-id' ); if ( attachmentId ) { resolve( attachmentId ); } else { From 5aa55de45ab7de0b37cdd4dbeac438fc3a76645c Mon Sep 17 00:00:00 2001 From: Rahul Gavande Date: Wed, 17 Dec 2025 14:42:09 +0530 Subject: [PATCH 21/21] Add handling for 403 error status in TUS upload retry logic --- src/modules/sync/lib/ipc-handlers.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modules/sync/lib/ipc-handlers.ts b/src/modules/sync/lib/ipc-handlers.ts index cc6fd3da0..d38a93618 100644 --- a/src/modules/sync/lib/ipc-handlers.ts +++ b/src/modules/sync/lib/ipc-handlers.ts @@ -232,6 +232,13 @@ export async function pushArchive( } ); console.error( '[TUS] Upload paused: ', error.message ); } + + const status = error.originalResponse ? error.originalResponse.getStatus() : 0; + // Stop retrying if the upload failed because of a 403 error. + if ( status === 403 ) { + return false; + } + return true; }, } );