/ src / base / retrieve.rs
retrieve.rs
 1  use crate::base::kit::{DIRECTORY, path_is_valid};
 2  use crate::base::kit::{format_size, get_mime_type, show_usage};
 3  use axum::{
 4      body::Body,
 5      extract::Path,
 6      http::{Response, StatusCode, header},
 7      response::{Html, IntoResponse},
 8  };
 9  
10  use tokio_util::io::ReaderStream;
11  
12  pub async fn list_items() -> Result<Html<String>, (StatusCode, String)> {
13      use crate::base::templates::items_list_page;
14  
15      let mut entries = tokio::fs::read_dir(DIRECTORY)
16          .await
17          .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
18  
19      let mut files_html = String::from("<ul class='file-list'>");
20      while let Some(entry) = entries
21          .next_entry()
22          .await
23          .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
24      {
25          let filename = entry.file_name().to_string_lossy().to_string();
26          files_html.push_str(&format!(
27              "<li><a href='/download/{}'>{}</a></li>",
28              filename, filename
29          ));
30      }
31      files_html.push_str("</ul>");
32  
33      let stats_html = match show_usage().await {
34          Ok((total_size, file_count)) => {
35              let size_str = format_size(total_size);
36              Some(format!(
37                  "<p class='stats'>Total: {} files, {}</p>",
38                  file_count, size_str
39              ))
40          }
41          Err(_) => None,
42      };
43  
44      Ok(items_list_page(files_html, stats_html).await)
45  }
46  
47  pub async fn get_item(
48      Path(filename): Path<String>,
49  ) -> Result<impl IntoResponse, (StatusCode, String)> {
50      if !path_is_valid(&filename) {
51          return Err((StatusCode::BAD_REQUEST, "Invalid path".to_owned()));
52      }
53  
54      let path = std::path::Path::new(DIRECTORY).join(&filename);
55      let file = tokio::fs::File::open(path)
56          .await
57          .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
58  
59      let stream = ReaderStream::new(file);
60  
61      let body = Body::from_stream(stream);
62  
63      let mime_type = get_mime_type(&filename);
64      let response = Response::builder()
65          .status(StatusCode::OK)
66          .header(header::CONTENT_TYPE, mime_type)
67          .header(
68              header::CONTENT_DISPOSITION,
69              format!("attachment; filename=\"{}\"", filename),
70          )
71          .body(body)
72          .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
73  
74      Ok(response)
75  }