@@ -2,32 +2,49 @@ use crate::file_manager::{
22 FileMetadata , Language , UpdateFileContentRequest , UpdateFilenameRequest ,
33} ;
44
5+ use anyhow:: { anyhow, Result } ;
56use axum:: {
67 extract:: { Multipart , Path } ,
8+ http:: StatusCode ,
79 response:: IntoResponse ,
810 Json ,
911} ;
1012use chrono:: Utc ;
11- use reqwest:: StatusCode ;
1213use std:: path:: PathBuf ;
1314use tokio:: fs;
1415use uuid:: Uuid ;
1516
1617const UPLOAD_DIR : & str = "uploads" ;
1718
19+ async fn file_exists ( file_path : & PathBuf ) -> bool {
20+ tokio:: fs:: metadata ( file_path) . await . is_ok ( )
21+ }
22+
1823pub async fn upload_file (
1924 Path ( ( problem_id, category) ) : Path < ( u32 , String ) > ,
20- mut multipart : Multipart ,
25+ multipart : Multipart ,
2126) -> impl IntoResponse {
22- let field = multipart. next_field ( ) . await . unwrap ( ) . unwrap ( ) ;
23- let file_name = field. file_name ( ) . unwrap ( ) . to_string ( ) ;
24- let data = field. bytes ( ) . await . unwrap ( ) ;
27+ let ( file_name, data) = match extract_multipart_data ( multipart) . await {
28+ Ok ( data) => data,
29+ Err ( e) => {
30+ return (
31+ StatusCode :: BAD_REQUEST ,
32+ Json ( serde_json:: json!( {
33+ "error" : e. to_string( )
34+ } ) ) ,
35+ )
36+ . into_response ( ) ;
37+ }
38+ } ;
39+
2540 let file_id = Uuid :: new_v4 ( ) . to_string ( ) ;
26- let upload_dir = PathBuf :: from ( format ! ( "{}/{}/{}" , UPLOAD_DIR , problem_id, category) ) ;
41+ let upload_dir = PathBuf :: from ( UPLOAD_DIR )
42+ . join ( problem_id. to_string ( ) )
43+ . join ( & category) ;
2744
2845 let file_path = upload_dir. join ( & file_name) ;
2946
30- if file_path . exists ( ) {
47+ if file_exists ( & file_path ) . await {
3148 return (
3249 StatusCode :: CONFLICT ,
3350 Json ( serde_json:: json!( {
@@ -51,8 +68,16 @@ pub async fn upload_file(
5168 }
5269 } ;
5370
54- fs:: create_dir_all ( & upload_dir) . await . unwrap ( ) ;
55- fs:: write ( & file_path, & data) . await . unwrap ( ) ;
71+ if let Err ( e) = save_file ( & upload_dir, & file_path, & data) . await {
72+ return (
73+ StatusCode :: INTERNAL_SERVER_ERROR ,
74+ Json ( serde_json:: json!( {
75+ "error" : e. to_string( )
76+ } ) ) ,
77+ )
78+ . into_response ( ) ;
79+ }
80+
5681 let metadata = FileMetadata {
5782 id : file_id,
5883 filename : file_name,
@@ -65,15 +90,47 @@ pub async fn upload_file(
6590 ( StatusCode :: CREATED , Json ( metadata) ) . into_response ( )
6691}
6792
93+ async fn save_file (
94+ upload_dir : & PathBuf ,
95+ file_path : & PathBuf ,
96+ data : & axum:: body:: Bytes ,
97+ ) -> Result < ( ) > {
98+ fs:: create_dir_all ( upload_dir)
99+ . await
100+ . map_err ( |_| anyhow ! ( "Failed to create directory" ) ) ?;
101+
102+ fs:: write ( file_path, data)
103+ . await
104+ . map_err ( |_| anyhow ! ( "Failed to write file" ) ) ?;
105+
106+ Ok ( ( ) )
107+ }
108+
109+ async fn extract_multipart_data ( mut multipart : Multipart ) -> Result < ( String , axum:: body:: Bytes ) > {
110+ let field = multipart
111+ . next_field ( )
112+ . await ?
113+ . ok_or_else ( || anyhow ! ( "Missing multipart field" ) ) ?;
114+
115+ let file_name = field
116+ . file_name ( )
117+ . ok_or_else ( || anyhow ! ( "Missing file name in multipart field" ) ) ?
118+ . to_string ( ) ;
119+
120+ let data = field. bytes ( ) . await ?;
121+
122+ Ok ( ( file_name, data) )
123+ }
124+
68125pub async fn get_file (
69126 Path ( ( problem_id, category, filename) ) : Path < ( u32 , String , String ) > ,
70127) -> impl IntoResponse {
71- let file_path = PathBuf :: from ( format ! (
72- "{}/{}/{}/{}" ,
73- UPLOAD_DIR , problem_id , category, filename
74- ) ) ;
128+ let file_path = PathBuf :: from ( UPLOAD_DIR )
129+ . join ( problem_id . to_string ( ) )
130+ . join ( & category)
131+ . join ( & filename ) ;
75132
76- if !file_path . exists ( ) {
133+ if !file_exists ( & file_path ) . await {
77134 return (
78135 StatusCode :: NOT_FOUND ,
79136 Json ( serde_json:: json!( {
@@ -107,9 +164,11 @@ pub async fn get_file(
107164pub async fn get_files_by_category (
108165 Path ( ( problem_id, category) ) : Path < ( u32 , String ) > ,
109166) -> impl IntoResponse {
110- let category_dir = PathBuf :: from ( format ! ( "{}/{}/{}" , UPLOAD_DIR , problem_id, category) ) ;
167+ let category_dir = PathBuf :: from ( UPLOAD_DIR )
168+ . join ( problem_id. to_string ( ) )
169+ . join ( & category) ;
111170
112- if !category_dir . exists ( ) {
171+ if !file_exists ( & category_dir ) . await {
113172 return (
114173 StatusCode :: NOT_FOUND ,
115174 Json ( serde_json:: json!( {
@@ -149,12 +208,12 @@ pub async fn get_files_by_category(
149208pub async fn delete_file (
150209 Path ( ( problem_id, category, filename) ) : Path < ( u32 , String , String ) > ,
151210) -> impl IntoResponse {
152- let file_path = PathBuf :: from ( format ! (
153- "{}/{}/{}/{}" ,
154- UPLOAD_DIR , problem_id , category, filename
155- ) ) ;
211+ let file_path = PathBuf :: from ( UPLOAD_DIR )
212+ . join ( problem_id . to_string ( ) )
213+ . join ( & category)
214+ . join ( & filename ) ;
156215
157- if !file_path . exists ( ) {
216+ if !file_exists ( & file_path ) . await {
158217 return (
159218 StatusCode :: NOT_FOUND ,
160219 Json ( serde_json:: json!( {
@@ -186,12 +245,12 @@ pub async fn update_file_content(
186245 Path ( ( problem_id, category, filename) ) : Path < ( u32 , String , String ) > ,
187246 Json ( update_request) : Json < UpdateFileContentRequest > ,
188247) -> impl IntoResponse {
189- let file_path = PathBuf :: from ( format ! (
190- "{}/{}/{}/{}" ,
191- UPLOAD_DIR , problem_id , category, filename
192- ) ) ;
248+ let file_path = PathBuf :: from ( UPLOAD_DIR )
249+ . join ( problem_id . to_string ( ) )
250+ . join ( & category)
251+ . join ( & filename ) ;
193252
194- if !file_path . exists ( ) {
253+ if !file_exists ( & file_path ) . await {
195254 return (
196255 StatusCode :: NOT_FOUND ,
197256 Json ( serde_json:: json!( {
@@ -223,12 +282,12 @@ pub async fn update_filename(
223282 Path ( ( problem_id, category) ) : Path < ( u32 , String ) > ,
224283 Json ( update_request) : Json < UpdateFilenameRequest > ,
225284) -> impl IntoResponse {
226- let file_path = PathBuf :: from ( format ! (
227- "{}/{}/{}/{}" ,
228- UPLOAD_DIR , problem_id , category , update_request . old_filename
229- ) ) ;
285+ let file_path = PathBuf :: from ( UPLOAD_DIR )
286+ . join ( problem_id . to_string ( ) )
287+ . join ( & category )
288+ . join ( & update_request . old_filename ) ;
230289
231- if !file_path . exists ( ) {
290+ if !file_exists ( & file_path ) . await {
232291 return (
233292 StatusCode :: NOT_FOUND ,
234293 Json ( serde_json:: json!( {
0 commit comments