@@ -35,6 +35,16 @@ const MAX_MONTHLY_BACKUPS = 12;
3535const BACKUP_HOUR = 2 ; // 2 AM
3636const BACKUP_MINUTE = 0 ;
3737
38+ /**
39+ * Format bytes to human readable string
40+ */
41+ function formatBytes ( bytes : number ) : string {
42+ if ( bytes === 0 ) return "0 Bytes" ;
43+ if ( bytes < 1024 ) return `${ bytes } Bytes` ;
44+ if ( bytes < 1024 * 1024 ) return `${ ( bytes / 1024 ) . toFixed ( 2 ) } KB` ;
45+ return `${ ( bytes / 1024 / 1024 ) . toFixed ( 2 ) } MB` ;
46+ }
47+
3848export interface BackupMetadata {
3949 filename : string ;
4050 filepath : string ;
@@ -226,35 +236,59 @@ export async function createBackup(
226236
227237 // Track progress by monitoring file size
228238 let bytesWritten = 0 ;
229- const progressInterval = setInterval ( async ( ) => {
230- try {
231- const stats = await fs . stat ( backupPath ) ;
232- const newBytes = stats . size ;
233- if ( newBytes > bytesWritten ) {
234- bytesWritten = newBytes ;
235- webSocketService . broadcast ( WS_EVENTS . BACKUP_PROGRESS , {
236- filename,
237- bytesWritten : newBytes ,
238- status : "in_progress" ,
239- } ) ;
239+ let progressInterval : NodeJS . Timeout | null = null ;
240+ let progressTimeout : NodeJS . Timeout | null = null ;
241+ let isProcessComplete = false ;
242+
243+ const clearProgressMonitoring = ( ) => {
244+ isProcessComplete = true ;
245+ if ( progressTimeout ) clearTimeout ( progressTimeout ) ;
246+ if ( progressInterval ) clearInterval ( progressInterval ) ;
247+ } ;
248+
249+ // Wait a bit before starting progress monitoring to avoid showing tiny initial values
250+ progressTimeout = setTimeout ( ( ) => {
251+ if ( isProcessComplete ) return ; // Don't start if already complete
252+
253+ progressInterval = setInterval ( async ( ) => {
254+ if ( isProcessComplete ) {
255+ if ( progressInterval ) clearInterval ( progressInterval ) ;
256+ return ;
240257 }
241- } catch {
242- // File might not exist yet, ignore
243- }
244- } , 1000 ) ; // Update every second
258+
259+ try {
260+ const stats = await fs . stat ( backupPath ) ;
261+ const newBytes = stats . size ;
262+ // Only broadcast if there's a meaningful change (at least 1KB difference)
263+ if (
264+ newBytes > bytesWritten &&
265+ ( newBytes - bytesWritten >= 1024 || bytesWritten === 0 )
266+ ) {
267+ bytesWritten = newBytes ;
268+ webSocketService . broadcast ( WS_EVENTS . BACKUP_PROGRESS , {
269+ filename,
270+ sizeText : formatBytes ( newBytes ) ,
271+ status : "in_progress" ,
272+ } ) ;
273+ }
274+ } catch {
275+ // File might not exist yet, ignore
276+ }
277+ } , 1000 ) ; // Update every second
278+ } , 500 ) ; // Start monitoring after 500ms
245279
246280 let errorOutput = "" ;
247281 pgDump . stderr . on ( "data" , ( data ) => {
248282 errorOutput += data . toString ( ) ;
249283 } ) ;
250284
251285 pgDump . on ( "error" , ( error ) => {
252- clearInterval ( progressInterval ) ;
286+ clearProgressMonitoring ( ) ;
253287 reject ( new Error ( `pg_dump process error: ${ error . message } ` ) ) ;
254288 } ) ;
255289
256290 pgDump . on ( "close" , ( code ) => {
257- clearInterval ( progressInterval ) ;
291+ clearProgressMonitoring ( ) ;
258292 if ( code === 0 ) {
259293 resolve ( ) ;
260294 } else {
@@ -274,12 +308,12 @@ export async function createBackup(
274308 } ) ;
275309
276310 destination . on ( "error" , ( error ) => {
277- clearInterval ( progressInterval ) ;
311+ clearProgressMonitoring ( ) ;
278312 reject ( error ) ;
279313 } ) ;
280314
281315 gzip . on ( "error" , ( error ) => {
282- clearInterval ( progressInterval ) ;
316+ clearProgressMonitoring ( ) ;
283317 reject ( error ) ;
284318 } ) ;
285319 } ) ;
@@ -301,13 +335,13 @@ export async function createBackup(
301335 } ;
302336
303337 logger . info (
304- `Backup created successfully: ${ filename } (${ ( stats . size / 1024 / 1024 ) . toFixed ( 2 ) } MB )`
338+ `Backup created successfully: ${ filename } (${ formatBytes ( stats . size ) } )`
305339 ) ;
306340
307341 // Emit backup completed event
308342 webSocketService . broadcast ( WS_EVENTS . BACKUP_COMPLETED , {
309343 filename,
310- size : stats . size ,
344+ sizeText : formatBytes ( stats . size ) ,
311345 type,
312346 verified : isValid ,
313347 status : "completed" ,
0 commit comments