whole bunch of stuff ig
This commit is contained in:
parent
e1c499ee72
commit
88b875b809
4 changed files with 236 additions and 19 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/target
|
||||
.DS_Store
|
||||
|
|
107
Cargo.lock
generated
107
Cargo.lock
generated
|
@ -111,6 +111,21 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.28"
|
||||
|
@ -118,6 +133,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -126,6 +142,34 @@ version = "0.3.28"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
|
@ -144,10 +188,27 @@ version = "0.3.28"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -387,11 +448,15 @@ name = "nyazoom"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"futures",
|
||||
"rand",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -467,6 +532,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
|
@ -485,6 +556,36 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
|
@ -842,6 +943,12 @@ version = "1.0.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -7,8 +7,12 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
axum = { version = "0.6.12", features = ["multipart", "http2"] }
|
||||
futures = "0.3.28"
|
||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||
tokio = { version = "1.27.0", features = ["full"] }
|
||||
tokio-util = { version = "0.7.7", features = ["io"] }
|
||||
tower = { version = "0.4.13", features = ["util"] }
|
||||
tower-http = { version = "0.4.0", features = ["fs", "trace", "limit"] }
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
urlencoding = "2.1.2"
|
||||
|
|
143
src/main.rs
143
src/main.rs
|
@ -1,56 +1,161 @@
|
|||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use axum::body::Bytes;
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::post;
|
||||
use axum::BoxError;
|
||||
use axum::{
|
||||
extract::{DefaultBodyLimit, Multipart},
|
||||
response::{IntoResponse, Redirect},
|
||||
response::Redirect,
|
||||
Router,
|
||||
};
|
||||
use axum::routing::post;
|
||||
use futures::{Stream, TryStreamExt};
|
||||
use rand::distributions::{Alphanumeric, DistString};
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::SeedableRng;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::BufWriter;
|
||||
use tokio_util::io::StreamReader;
|
||||
use tower_http::{limit::RequestBodyLimitLayer, services::ServeDir, trace::TraceLayer};
|
||||
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
async fn main() -> io::Result<()> {
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| "nyazoo=debug,tower_http=debug".into()),
|
||||
.unwrap_or_else(|_| "nyazoom=debug,tower_http=debug".into()),
|
||||
)
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
// uses create_dir_all to create both .cache and .temp inside it in one go
|
||||
make_dir(".cache/.temp").await?;
|
||||
|
||||
// Router Setup
|
||||
let with_big_body = Router::new()
|
||||
.route("/upload", post(upload))
|
||||
.layer(DefaultBodyLimit::disable())
|
||||
.layer(RequestBodyLimitLayer::new(
|
||||
250 * 1024 * 1024, // 250Mb
|
||||
10 * 1024 * 1024 * 1024, // 10GiB
|
||||
));
|
||||
|
||||
let base = Router::new().nest_service("/", ServeDir::new("dist"));
|
||||
let base = Router::new()
|
||||
.nest_service("/", ServeDir::new("dist"))
|
||||
.nest_service("/download", ServeDir::new(".cache"));
|
||||
|
||||
let app = Router::new().merge(with_big_body).merge(base);
|
||||
let app = Router::new()
|
||||
.merge(with_big_body)
|
||||
.merge(base)
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
// Server creation
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
|
||||
tracing::debug!("listening on {}", addr);
|
||||
axum::Server::bind(&addr)
|
||||
.serve(app.layer(TraceLayer::new_for_http()).into_make_service())
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn upload(mut body: Multipart) -> impl IntoResponse {
|
||||
while let Some(field) = body.next_field().await.unwrap() {
|
||||
let name = field.name().unwrap().to_string();
|
||||
let content_type = field.content_type().unwrap().to_string();
|
||||
let file_name = field.file_name().unwrap().to_string();
|
||||
let data = field.bytes().await.unwrap();
|
||||
async fn upload(mut body: Multipart) -> Result<Redirect, (StatusCode, String)> {
|
||||
let cache_folder = Path::new(".cache/.temp").join(get_random_name(10));
|
||||
|
||||
tracing::debug!(
|
||||
"\n\nLength of {name} ({file_name}: {content_type}) is {} bytes\n",
|
||||
data.len()
|
||||
)
|
||||
make_dir(&cache_folder)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;
|
||||
|
||||
while let Some(field) = body.next_field().await.unwrap() {
|
||||
let file_name = if let Some(file_name) = field.file_name() {
|
||||
file_name.to_owned()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
||||
if !path_is_valid(&file_name) {
|
||||
return Err((StatusCode::BAD_REQUEST, "Invalid Filename >:(".to_owned()))
|
||||
}
|
||||
|
||||
Redirect::to("/")
|
||||
let path = cache_folder.join(file_name);
|
||||
|
||||
tracing::debug!("\n\nstuff written to {path:?}\n");
|
||||
stream_to_file(&path, field).await?
|
||||
}
|
||||
|
||||
tracing::debug!("{cache_folder:?}");
|
||||
|
||||
Ok(Redirect::to("/"))
|
||||
}
|
||||
|
||||
async fn stream_to_file<S, E, P>(path: P, stream: S) -> Result<(), (StatusCode, String)>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
S: Stream<Item = Result<Bytes, E>>,
|
||||
E: Into<BoxError>,
|
||||
{
|
||||
async {
|
||||
// Convert the stream into an `AsyncRead`.
|
||||
let body_with_io_error = stream.map_err(|err| io::Error::new(io::ErrorKind::Other, err));
|
||||
let body_reader = StreamReader::new(body_with_io_error);
|
||||
futures::pin_mut!(body_reader);
|
||||
|
||||
// Create the file. `File` implements `AsyncWrite`.
|
||||
let mut file = BufWriter::new(File::create(&path).await?);
|
||||
|
||||
// Copy the body into the file.
|
||||
tokio::io::copy(&mut body_reader, &mut file).await?;
|
||||
|
||||
io::Result::Ok(())
|
||||
}
|
||||
.await
|
||||
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))
|
||||
}
|
||||
|
||||
async fn remove_dir<T>(folder: T) -> io::Result<()>
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
tokio::fs::remove_dir_all(&folder).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn path_is_valid(path: &str) -> bool {
|
||||
let mut components = Path::new(path).components().peekable();
|
||||
|
||||
if let Some(first) = components.peek() {
|
||||
tracing::debug!("{:?}", &first);
|
||||
if !matches!(first, std::path::Component::Normal(_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
components.count() == 1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn make_dir<T>(name: T) -> io::Result<()>
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
tokio::fs::create_dir_all(name)
|
||||
.await
|
||||
.or_else(|err| match err.kind() {
|
||||
io::ErrorKind::AlreadyExists => Ok(()),
|
||||
_ => Err(err),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_random_name(len: usize) -> String {
|
||||
let mut rng = SmallRng::from_entropy();
|
||||
|
||||
Alphanumeric.sample_string(&mut rng, len)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue