@@ -8,6 +8,7 @@ use crate::config::Config;
88use crate :: error:: AppError ;
99
1010const PRESIGN_EXPIRY : Duration = Duration :: from_secs ( 24 * 60 * 60 ) ;
11+ const CLEANUP_MAX_AGE : Duration = Duration :: from_secs ( 24 * 60 * 60 ) ;
1112
1213pub struct Storage {
1314 client : Client ,
@@ -81,6 +82,76 @@ impl Storage {
8182
8283 Ok ( UploadResult { presigned_url } )
8384 }
85+
86+ pub async fn cleanup_expired ( & self ) {
87+ let cutoff = aws_sdk_s3:: primitives:: DateTime :: from_secs_f64 (
88+ std:: time:: SystemTime :: now ( )
89+ . duration_since ( std:: time:: UNIX_EPOCH )
90+ . unwrap_or_default ( )
91+ . as_secs_f64 ( )
92+ - CLEANUP_MAX_AGE . as_secs_f64 ( ) ,
93+ ) ;
94+
95+ let mut continuation_token: Option < String > = None ;
96+
97+ loop {
98+ let mut request = self
99+ . client
100+ . list_objects_v2 ( )
101+ . bucket ( & self . bucket )
102+ . prefix ( "uploads/" ) ;
103+
104+ if let Some ( token) = & continuation_token {
105+ request = request. continuation_token ( token) ;
106+ }
107+
108+ let response = match request. send ( ) . await {
109+ Ok ( response) => response,
110+ Err ( e) => {
111+ tracing:: error!( "cleanup: failed to list objects: {e}" ) ;
112+ return ;
113+ }
114+ } ;
115+
116+ let mut deleted = 0 ;
117+ for object in response. contents ( ) {
118+ let is_expired = object
119+ . last_modified ( )
120+ . map ( |modified| * modified < cutoff)
121+ . unwrap_or ( false ) ;
122+
123+ if !is_expired {
124+ continue ;
125+ }
126+
127+ let Some ( key) = object. key ( ) else {
128+ continue ;
129+ } ;
130+
131+ if let Err ( e) = self
132+ . client
133+ . delete_object ( )
134+ . bucket ( & self . bucket )
135+ . key ( key)
136+ . send ( )
137+ . await
138+ {
139+ tracing:: error!( "cleanup: failed to delete {key}: {e}" ) ;
140+ } else {
141+ deleted += 1 ;
142+ }
143+ }
144+
145+ if deleted > 0 {
146+ tracing:: info!( "cleanup: deleted {deleted} expired files" ) ;
147+ }
148+
149+ match response. next_continuation_token ( ) {
150+ Some ( token) => continuation_token = Some ( token. to_string ( ) ) ,
151+ None => break ,
152+ }
153+ }
154+ }
84155}
85156
86157fn build_key ( original_filename : Option < & str > , hash : & str ) -> String {
0 commit comments