2022-12-28 22:05:28 -06:00
|
|
|
use std::{
|
|
|
|
ffi::OsStr,
|
|
|
|
path::{Component, PathBuf},
|
|
|
|
sync::Arc,
|
|
|
|
};
|
|
|
|
|
|
|
|
use axum::{
|
|
|
|
body::StreamBody,
|
|
|
|
extract::{Path, State},
|
2023-01-08 18:08:24 -06:00
|
|
|
http::HeaderValue,
|
|
|
|
response::{IntoResponse, Response},
|
2022-12-28 22:05:28 -06:00
|
|
|
};
|
2023-01-08 18:08:24 -06:00
|
|
|
|
2022-12-28 22:05:28 -06:00
|
|
|
use hyper::StatusCode;
|
2023-01-08 18:08:24 -06:00
|
|
|
use mime_guess::mime;
|
2023-01-08 19:10:30 -06:00
|
|
|
use tokio::fs::File;
|
2022-12-28 22:05:28 -06:00
|
|
|
use tokio_util::io::ReaderStream;
|
|
|
|
|
2022-12-28 23:03:14 -06:00
|
|
|
#[axum::debug_handler]
|
2022-12-28 22:05:28 -06:00
|
|
|
pub async fn view(
|
|
|
|
State(state): State<Arc<crate::state::AppState>>,
|
|
|
|
Path(original_path): Path<PathBuf>,
|
|
|
|
) -> Response {
|
|
|
|
// (hopefully) prevent path traversal, just check for any non-file components
|
|
|
|
if original_path
|
|
|
|
.components()
|
|
|
|
.into_iter()
|
|
|
|
.any(|x| !matches!(x, Component::Normal(_)))
|
|
|
|
{
|
2023-01-08 18:08:24 -06:00
|
|
|
error!(target: "view", "a request attempted path traversal");
|
2022-12-28 22:05:28 -06:00
|
|
|
return StatusCode::NOT_FOUND.into_response();
|
|
|
|
}
|
|
|
|
|
|
|
|
let name = original_path
|
|
|
|
.file_name()
|
|
|
|
.and_then(OsStr::to_str)
|
|
|
|
.unwrap_or_default()
|
|
|
|
.to_string();
|
|
|
|
|
2023-01-08 18:08:24 -06:00
|
|
|
let cache = state.cache.lock().await;
|
2022-12-28 22:05:28 -06:00
|
|
|
|
|
|
|
let cache_item = cache.get(&name);
|
|
|
|
|
2023-01-08 18:08:24 -06:00
|
|
|
if cache_item.is_none() {
|
|
|
|
let mut path = PathBuf::new();
|
|
|
|
path.push("uploads/");
|
|
|
|
path.push(name);
|
2022-12-28 22:05:28 -06:00
|
|
|
|
2023-01-08 18:08:24 -06:00
|
|
|
if !path.exists() || !path.is_file() {
|
|
|
|
return StatusCode::NOT_FOUND.into_response();
|
|
|
|
}
|
2022-12-28 22:05:28 -06:00
|
|
|
|
2023-01-08 18:08:24 -06:00
|
|
|
let file = File::open(path).await.unwrap();
|
2022-12-28 22:05:28 -06:00
|
|
|
|
2023-01-08 18:08:24 -06:00
|
|
|
let reader = ReaderStream::new(file);
|
|
|
|
let stream = StreamBody::new(reader);
|
|
|
|
|
|
|
|
info!(target: "view", "reading upload from disk");
|
|
|
|
|
|
|
|
return stream.into_response();
|
2022-12-28 22:05:28 -06:00
|
|
|
}
|
|
|
|
|
2023-01-08 18:08:24 -06:00
|
|
|
info!(target: "view", "reading upload from cache");
|
2022-12-28 22:05:28 -06:00
|
|
|
|
2023-01-08 18:08:24 -06:00
|
|
|
let data = cache_item.unwrap().clone();
|
2022-12-28 22:05:28 -06:00
|
|
|
|
2023-01-08 18:08:24 -06:00
|
|
|
let content_type = mime_guess::from_path(original_path)
|
|
|
|
.first()
|
|
|
|
.unwrap_or(mime::APPLICATION_OCTET_STREAM)
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
let mut res = data.into_response();
|
|
|
|
let headers = res.headers_mut();
|
|
|
|
|
|
|
|
headers.clear();
|
|
|
|
headers.insert(
|
|
|
|
"content-type",
|
|
|
|
HeaderValue::from_str(content_type.as_str()).unwrap(),
|
|
|
|
);
|
|
|
|
|
|
|
|
return res;
|
2023-01-08 19:10:30 -06:00
|
|
|
}
|