@@ -13,12 +13,16 @@ use serde::Deserialize;
1313use serde_json:: json;
1414use tracing:: { event, instrument, Level } ;
1515
16- const MAX_BLOCK_SIZE : u64 = 1024 * 1024 ; // 1MB
17- const MAX_BLOCKS_PER_FILE : usize = 8 ;
16+ /// Maximum size of each file block in bytes (1MB)
17+ const MAX_BLOCK_SIZE : u64 = 1024 * 1024 ;
18+ /// Maximum number of blocks allowed per file (increased to support larger zip files)
19+ const MAX_BLOCKS_PER_FILE : usize = 16 ;
20+ /// Maximum number of retry attempts for database operations
1821const MAX_RETRIES : u32 = 5 ;
19- const RETRY_INTERVAL : u64 = 250 ; // milliseconds
22+ /// Interval between retry attempts in milliseconds
23+ const RETRY_INTERVAL : u64 = 250 ;
2024
21- // dto
25+ /// Data transfer object for file information
2226#[ derive( Debug , Deserialize ) ]
2327struct FileInfo {
2428 pub filename : String ,
@@ -27,6 +31,8 @@ struct FileInfo {
2731 pub total : u64 ,
2832}
2933
34+ /// Handler for serving the upload page
35+ /// Returns the upload HTML page or 404 if not found
3036#[ instrument]
3137pub async fn upload ( ) -> impl IntoResponse {
3238 match StaticFiles :: get ( "upload/index.html" ) {
@@ -41,6 +47,8 @@ pub async fn upload() -> impl IntoResponse {
4147 }
4248}
4349
50+ /// Handler for serving the download page
51+ /// Returns the download HTML page or 404 if not found
4452pub async fn download ( ) -> impl IntoResponse {
4553 match StaticFiles :: get ( "download/index.html" ) {
4654 Some ( content) => {
@@ -54,6 +62,10 @@ pub async fn download() -> impl IntoResponse {
5462 }
5563}
5664
65+ /// Handler for generating a unique file ID
66+ /// Accepts file name and size as query parameters
67+ /// Returns a unique ID that can be used for file transfer
68+ #[ instrument]
5769pub async fn get_id (
5870 Query ( query) : Query < HashMap < String , String > >
5971) -> impl IntoResponse {
@@ -96,6 +108,9 @@ pub async fn get_id(
96108 . into_response ( )
97109}
98110
111+ /// Handler for checking the status of a file transfer
112+ /// Returns file metadata and transfer status
113+ #[ instrument]
99114pub async fn get_status ( Path ( id) : Path < String > ) -> impl IntoResponse {
100115 // Changed from DEBUG to TRACE to reduce log verbosity
101116 event ! ( Level :: TRACE , "Checking status for ID: {}" , id) ;
@@ -112,6 +127,7 @@ pub async fn get_status(Path(id): Path<String>) -> impl IntoResponse {
112127 "file_name" : meta_info. value. file_name,
113128 "file_size" : meta_info. value. file_size,
114129 "is_using" : meta_info. value. is_using,
130+ "done" : meta_info. value. done,
115131 }
116132
117133 } ) )
@@ -127,38 +143,41 @@ pub async fn get_status(Path(id): Path<String>) -> impl IntoResponse {
127143 }
128144}
129145
146+ /// Handler for downloading file chunks
147+ /// Supports range requests for chunked file transfer
148+ /// Includes retry logic and atomic operations for concurrent access
130149#[ instrument( skip_all) ]
131150pub async fn get_file (
132151 Path ( id) : Path < String > ,
133152 Query ( query) : Query < HashMap < String , String > > ,
134- ) -> Result < impl IntoResponse , ( StatusCode , String ) > {
153+ ) -> impl IntoResponse {
135154 let receive_id = match query. get ( "rid" ) {
136155 Some ( receive_id) => receive_id. to_string ( ) ,
137156 None => {
138157 event ! ( Level :: WARN , "Missing Parameter: rid" ) ;
139- return Ok ( (
158+ return (
140159 StatusCode :: BAD_REQUEST ,
141160 Json ( json ! ( {
142161 "code" : 400 ,
143162 "success" : false ,
144163 "message" : "Missing Parameter: rid"
145164 } ) ) )
146- . into_response ( ) ) ;
165+ . into_response ( ) ;
147166 }
148167 } ;
149168
150169 let start = match query. get ( "start" ) {
151170 Some ( start) => start. parse :: < u64 > ( ) . unwrap ( ) ,
152171 None => {
153172 event ! ( Level :: WARN , "Missing Parameter: start" ) ;
154- return Ok ( (
173+ return (
155174 StatusCode :: BAD_REQUEST ,
156175 Json ( json ! ( {
157176 "code" : 400 ,
158177 "success" : false ,
159178 "message" : "Missing Parameter: start"
160179 } ) ) )
161- . into_response ( ) ) ;
180+ . into_response ( ) ;
162181 }
163182 } ;
164183
@@ -176,14 +195,14 @@ pub async fn get_file(
176195 // Check if already in use
177196 if current_meta. value . is_using {
178197 event ! ( Level :: WARN , "File already in use for ID: {}" , id) ;
179- return Ok ( (
198+ return (
180199 StatusCode :: BAD_REQUEST ,
181200 Json ( json ! ( {
182201 "code" : 400 ,
183202 "success" : false ,
184203 "message" : "Bad Request"
185204 } ) ) )
186- . into_response ( ) ) ;
205+ . into_response ( ) ;
187206 }
188207
189208 // Atomically update the metadata
@@ -202,22 +221,22 @@ pub async fn get_file(
202221 retries += 1 ;
203222 if retries >= MAX_RETRIES {
204223 event ! ( Level :: ERROR , "Failed to update metadata after {} retries for ID: {}" , MAX_RETRIES , id) ;
205- return Ok ( (
224+ return (
206225 StatusCode :: INTERNAL_SERVER_ERROR ,
207226 Json ( json ! ( {
208227 "code" : 500 ,
209228 "success" : false ,
210229 "message" : "Internal Server Error"
211230 } ) ) )
212- . into_response ( ) ) ;
231+ . into_response ( ) ;
213232 }
214233 tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 100 ) ) . await ;
215234 }
216235 }
217236 }
218237 None => {
219238 event ! ( Level :: WARN , "Access ID Not Found: {}" , id) ;
220- return Ok ( ( StatusCode :: NOT_FOUND , "Access ID Not Found" ) . into_response ( ) ) ;
239+ return ( StatusCode :: NOT_FOUND , "Access ID Not Found" ) . into_response ( ) ;
221240 }
222241 }
223242 }
@@ -230,18 +249,18 @@ pub async fn get_file(
230249 Some ( meta_info) => {
231250 if meta_info. value . used_by != receive_id {
232251 event ! ( Level :: WARN , "Wrong Receive ID for ID: {}" , id) ;
233- return Ok ( (
252+ return (
234253 StatusCode :: BAD_REQUEST ,
235254 Json ( json ! ( {
236255 "code" : 400 ,
237256 "success" : false ,
238257 "message" : "Wrong Receive ID"
239- } ) ) ) . into_response ( ) ) ;
258+ } ) ) ) . into_response ( ) ;
240259 }
241260 } ,
242261 None => {
243262 event ! ( Level :: WARN , "Access ID Not Found during verification: {}" , id) ;
244- return Ok ( ( StatusCode :: NOT_FOUND , "Access ID Not Found" ) . into_response ( ) ) ;
263+ return ( StatusCode :: NOT_FOUND , "Access ID Not Found" ) . into_response ( ) ;
245264 }
246265 } ;
247266
@@ -253,14 +272,14 @@ pub async fn get_file(
253272 Some ( file_block) => {
254273 if file_block. value . start > start {
255274 event ! ( Level :: WARN , "Wrong start position for ID: {} and start: {}" , id. clone( ) , start) ;
256- return Ok ( (
275+ return (
257276 StatusCode :: BAD_REQUEST ,
258277 Json ( json ! ( {
259278 "code" : 400 ,
260279 "success" : false ,
261280 "message" : "Wrong start position"
262281 } ) ) )
263- . into_response ( ) ) ;
282+ . into_response ( ) ;
264283 }
265284 // Changed from DEBUG to TRACE to reduce log verbosity
266285 event ! ( Level :: TRACE , "Retrieved block for ID: {} and start: {}" , id. clone( ) , start) ;
@@ -269,7 +288,7 @@ pub async fn get_file(
269288 None => {
270289 if retries >= MAX_RETRIES {
271290 event ! ( Level :: ERROR , "Block {}:{:012} Not Found after {} retries" , & id, start, MAX_RETRIES ) ;
272- return Ok ( ( StatusCode :: NOT_FOUND , format ! ( "Block {}:{:012} Not Found" , & id, start) ) . into_response ( ) ) ;
291+ return ( StatusCode :: NOT_FOUND , format ! ( "Block {}:{:012} Not Found" , & id, start) ) . into_response ( ) ;
273292 }
274293 retries += 1 ;
275294 tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( RETRY_INTERVAL ) ) . await ;
@@ -304,14 +323,18 @@ pub async fn get_file(
304323 // Changed from DEBUG to TRACE to reduce log verbosity
305324 event ! ( Level :: TRACE , "Sending file block for ID: {} range: {}-{}" , id, block_start, block_end) ;
306325
307- Ok ( (
326+ (
308327 StatusCode :: PARTIAL_CONTENT ,
309328 AppendHeaders ( headers) ,
310329 Body :: from ( block_data)
311- ) . into_response ( ) )
330+ ) . into_response ( )
312331}
313332
314333
334+ /// Handler for uploading file chunks
335+ /// Processes multipart form data with file info and chunk data
336+ /// Includes validation for block size and file limits
337+ #[ instrument]
315338pub async fn upload_file ( Path ( id) : Path < String > , multipart : Multipart ) -> impl IntoResponse {
316339 // Changed from INFO to DEBUG to reduce log verbosity for large files
317340 event ! ( Level :: DEBUG , "Starting file upload for ID: {}" , id) ;
@@ -518,7 +541,7 @@ pub async fn upload_file(Path(id): Path<String>, multipart: Multipart) -> impl I
518541 return Json ( json ! ( {
519542 "code" : 400 ,
520543 "success" : false ,
521- "message" : "Maximum number of blocks per file reached (8)"
544+ "message" : format! ( "Maximum number of blocks per file reached ({})" , MAX_BLOCKS_PER_FILE )
522545 } ) )
523546 . into_response ( ) ;
524547 }
@@ -559,8 +582,48 @@ pub async fn upload_file(Path(id): Path<String>, multipart: Multipart) -> impl I
559582 . into_response ( )
560583}
561584
585+ /// Handler for marking file download as complete
586+ /// Updates the metadata to indicate successful download
587+ #[ instrument]
588+ pub async fn done ( Path ( id) : Path < String > , Json ( _payload) : Json < serde_json:: Value > ) -> impl IntoResponse {
589+ // Mark download as complete for the given ID
590+ match MetaInfo :: get_db ( ) . get ( & id) . await {
591+ Some ( mut meta_info) => {
592+ meta_info. value . done = true ;
593+ match MetaInfo :: get_db ( ) . update ( & id, meta_info. value , meta_info. exp ) . await {
594+ Ok ( _) => {
595+ event ! ( Level :: DEBUG , "Download marked as complete for ID: {}" , id) ;
596+ Json ( json ! ( {
597+ "code" : 200 ,
598+ "success" : true ,
599+ "message" : "Download completion marked successfully"
600+ } ) )
601+ } ,
602+ Err ( e) => {
603+ event ! ( Level :: ERROR , "Failed to update download completion status: {}" , e) ;
604+ Json ( json ! ( {
605+ "code" : 500 ,
606+ "success" : false ,
607+ "message" : "Internal Server Error"
608+ } ) )
609+ }
610+ }
611+ } ,
612+ None => {
613+ event ! ( Level :: WARN , "ID not found for download completion: {}" , id) ;
614+ Json ( json ! ( {
615+ "code" : 404 ,
616+ "success" : false ,
617+ "message" : "Not Found"
618+ } ) )
619+ }
620+ }
621+ }
622+
623+ /// Handler for serving static assets
624+ /// Returns CSS, JS, and other static files with appropriate MIME types
562625#[ instrument( skip_all) ]
563- pub async fn get_assets ( Path ( file) : Path < String > ) -> impl IntoResponse {
626+ pub async fn get_assets ( Path ( file) : Path < String > ) -> impl IntoResponse {
564627 match StaticFiles :: get ( format ! ( "assets/{}" , file) . as_str ( ) ) {
565628 Some ( f) => {
566629 let mime = mime_guess:: from_path ( & file) . first_or_octet_stream ( ) ;
0 commit comments