@@ -21,10 +21,8 @@ use crate::{
2121 } ,
2222 utils:: PortableModeExt ,
2323} ;
24- use axum:: http:: HeaderMap ;
2524use chrono:: { Datelike , Timelike } ;
2625use compact_str:: ToCompactString ;
27- use human_bytes:: human_bytes;
2826use serde:: Deserialize ;
2927use std:: {
3028 collections:: HashMap ,
@@ -45,8 +43,14 @@ static RESTIC_BACKUP_CACHE: LazyLock<ResticBackupCache> =
4543#[ derive( Debug , Deserialize ) ]
4644struct ResticSnapshot {
4745 short_id : String ,
48- tags : Vec < String > ,
49- paths : Vec < String > ,
46+ tags : Vec < compact_str:: CompactString > ,
47+ paths : Vec < compact_str:: CompactString > ,
48+ summary : ResticSnapshotSummary ,
49+ }
50+
51+ #[ derive( Debug , Deserialize ) ]
52+ struct ResticSnapshotSummary {
53+ total_bytes_processed : u64 ,
5054}
5155
5256#[ derive( Deserialize ) ]
@@ -69,6 +73,7 @@ pub struct ResticDirectoryEntry {
6973pub struct ResticBackup {
7074 uuid : uuid:: Uuid ,
7175 short_id : String ,
76+ total_bytes_processed : u64 ,
7277
7378 config : Arc < crate :: config:: Config > ,
7479 server_path : PathBuf ,
@@ -217,6 +222,7 @@ impl BackupFindExt for ResticBackup {
217222 return Ok ( Some ( Backup :: Restic ( ResticBackup {
218223 uuid,
219224 short_id : snapshot. short_id . clone ( ) ,
225+ total_bytes_processed : snapshot. summary . total_bytes_processed ,
220226 config : Arc :: clone ( config) ,
221227 server_path : match snapshot. paths . first ( ) {
222228 Some ( path) => PathBuf :: from ( path) ,
@@ -280,6 +286,7 @@ impl BackupFindExt for ResticBackup {
280286 backup = Some ( ResticBackup {
281287 uuid,
282288 short_id : snapshot. short_id . clone ( ) ,
289+ total_bytes_processed : snapshot. summary . total_bytes_processed ,
283290 config : Arc :: clone ( config) ,
284291 server_path : match snapshot. paths . first ( ) {
285292 Some ( path) => PathBuf :: from ( path) ,
@@ -343,6 +350,7 @@ impl BackupFindExt for ResticBackup {
343350 backup = Some ( ResticBackup {
344351 uuid,
345352 short_id : snapshot. short_id . clone ( ) ,
353+ total_bytes_processed : snapshot. summary . total_bytes_processed ,
346354 config : Arc :: clone ( config) ,
347355 server_path : match snapshot. paths . first ( ) {
348356 Some ( path) => PathBuf :: from ( path) ,
@@ -562,8 +570,11 @@ impl BackupCreateExt for ResticBackup {
562570 (
563571 ResticSnapshot {
564572 short_id : snapshot_id. clone ( ) ,
565- tags : vec ! [ uuid. to_string( ) ] ,
566- paths : vec ! [ server. filesystem. base_path. to_string_lossy( ) . to_string( ) ] ,
573+ tags : vec ! [ uuid. to_compact_string( ) ] ,
574+ paths : vec ! [ server. filesystem. base_path. to_string_lossy( ) . into( ) ] ,
575+ summary : ResticSnapshotSummary {
576+ total_bytes_processed,
577+ } ,
567578 } ,
568579 Arc :: new ( configuration) ,
569580 ) ,
@@ -711,19 +722,16 @@ impl BackupExt for ResticBackup {
711722 }
712723 }
713724
714- let mut headers = HeaderMap :: with_capacity ( 2 ) ;
715- headers. insert (
716- "Content-Disposition" ,
717- format ! (
718- "attachment; filename={}.{}" ,
719- self . uuid,
720- archive_format. extension( )
725+ Ok ( ApiResponse :: new_stream ( reader)
726+ . with_header (
727+ "Content-Disposition" ,
728+ & format ! (
729+ "attachment; filename={}.{}" ,
730+ self . uuid,
731+ archive_format. extension( )
732+ ) ,
721733 )
722- . parse ( ) ?,
723- ) ;
724- headers. insert ( "Content-Type" , archive_format. mime_type ( ) . parse ( ) ?) ;
725-
726- Ok ( ApiResponse :: new_stream ( reader) . with_headers ( headers) )
734+ . with_header ( "Content-Type" , archive_format. mime_type ( ) ) )
727735 }
728736
729737 async fn restore (
@@ -733,6 +741,8 @@ impl BackupExt for ResticBackup {
733741 total : Arc < AtomicU64 > ,
734742 _download_url : Option < compact_str:: CompactString > ,
735743 ) -> Result < ( ) , anyhow:: Error > {
744+ total. store ( self . total_bytes_processed , Ordering :: SeqCst ) ;
745+
736746 let child = Command :: new ( "restic" )
737747 . envs ( & self . configuration . environment )
738748 . arg ( "--json" )
@@ -748,38 +758,28 @@ impl BackupExt for ResticBackup {
748758 . arg ( & server. filesystem . base_path )
749759 . arg ( "--limit-download" )
750760 . arg ( ( server. app_state . config . system . backups . read_limit . as_kib ( ) ) . to_compact_string ( ) )
761+ . arg ( "-vv" )
751762 . stdout ( std:: process:: Stdio :: piped ( ) )
752763 . spawn ( ) ?;
753764
754765 let mut line_reader = tokio:: io:: BufReader :: new ( child. stdout . unwrap ( ) ) . lines ( ) ;
755766
756767 while let Ok ( Some ( line) ) = line_reader. next_line ( ) . await {
757768 if let Ok ( json) = serde_json:: from_str :: < serde_json:: Value > ( & line)
758- && json. get ( "message_type" ) . and_then ( |v| v. as_str ( ) ) == Some ( "status " )
769+ && json. get ( "message_type" ) . and_then ( |v| v. as_str ( ) ) == Some ( "verbose_status " )
759770 {
760- let total_bytes = json
761- . get ( "total_bytes" )
762- . and_then ( |v| v. as_u64 ( ) )
763- . unwrap_or ( 0 ) ;
764- let bytes_restored = json
765- . get ( "bytes_restored" )
766- . and_then ( |v| v. as_u64 ( ) )
767- . unwrap_or ( 0 ) ;
768- let percent_done = json
769- . get ( "percent_done" )
770- . and_then ( |v| v. as_f64 ( ) )
771- . unwrap_or ( 0.0 ) ;
772- let percent_done = ( percent_done * 10000.0 ) . round ( ) / 100.0 ;
773-
774- progress. store ( bytes_restored, Ordering :: SeqCst ) ;
775- total. store ( total_bytes, Ordering :: SeqCst ) ;
776-
777- server. log_daemon ( compact_str:: format_compact!(
778- "(restoring): {} of {} ({}%)" ,
779- human_bytes( bytes_restored as f64 ) ,
780- human_bytes( total_bytes as f64 ) ,
781- percent_done
782- ) ) ;
771+ let Some ( item) = json. get ( "item" ) . and_then ( |v| v. as_str ( ) ) else {
772+ continue ;
773+ } ;
774+ let size = json. get ( "size" ) . and_then ( |v| v. as_u64 ( ) ) . unwrap_or ( 0 ) ;
775+
776+ if size == 0 {
777+ continue ;
778+ }
779+
780+ progress. fetch_add ( size, Ordering :: SeqCst ) ;
781+
782+ server. log_daemon ( compact_str:: format_compact!( "(restoring): {}" , item) ) ;
783783 }
784784 }
785785
0 commit comments