Initial commit.
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
1580
Cargo.lock
generated
Normal file
1580
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
Normal file
21
Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "gumbucket"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
axum = { version = "0.8.6", features = ["multipart"] }
|
||||||
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
|
color-eyre = "0.6"
|
||||||
|
config = { version = "0.15", default-features = false, features = [ "yaml" ] }
|
||||||
|
futures = "0.3"
|
||||||
|
rocksdb = "0.24.0"
|
||||||
|
tokio = { version = "1.47.1", features = ["fs", "macros", "rt-multi-thread"] }
|
||||||
|
tokio-macros = "2.5"
|
||||||
|
tower = { version = "0.5", features = ["buffer", "limit", "load-shed", "timeout"] }
|
||||||
|
tower-http = { version = "0.6", features = ["compression-full", "limit", "trace", "util"] }
|
||||||
|
tracing = "0.1"
|
||||||
|
tracing-subscriber = "0.3"
|
||||||
|
|
||||||
|
[profile.dev.package.backtrace]
|
||||||
|
opt-level = 3
|
||||||
1
index.html
Normal file
1
index.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
blah!
|
||||||
96
src/main.rs
Normal file
96
src/main.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use axum::{
|
||||||
|
BoxError, Router,
|
||||||
|
error_handling::HandleErrorLayer,
|
||||||
|
extract::{DefaultBodyLimit, Multipart},
|
||||||
|
http::StatusCode,
|
||||||
|
response::{Html, IntoResponse, Response},
|
||||||
|
routing::{get, post},
|
||||||
|
};
|
||||||
|
use clap::Parser;
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::fs;
|
||||||
|
use tower::{ServiceBuilder, buffer::BufferLayer, limit::rate::RateLimitLayer};
|
||||||
|
use tower_http::{compression::CompressionLayer, limit::RequestBodyLimitLayer, trace::TraceLayer};
|
||||||
|
|
||||||
|
mod store;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(version, about)]
|
||||||
|
struct Args {
|
||||||
|
/// Configuration file locaation.
|
||||||
|
#[arg(short, long)]
|
||||||
|
config: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn default_handler() -> Response {
|
||||||
|
match fs::read_to_string("index.html").await {
|
||||||
|
Ok(content) => Html(content).into_response(),
|
||||||
|
Err(e) => (
|
||||||
|
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("Failed to read index.html: {}", e),
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_error(err: BoxError) -> (StatusCode, String) {
|
||||||
|
if err.is::<tower::timeout::error::Elapsed>() {
|
||||||
|
(StatusCode::REQUEST_TIMEOUT, "Request timed out".to_string())
|
||||||
|
} else if err.is::<tower::load_shed::error::Overloaded>() {
|
||||||
|
(
|
||||||
|
StatusCode::SERVICE_UNAVAILABLE,
|
||||||
|
"Service overloaded, try again later".to_string(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("Internal error: {}", err),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn upload_handler(mut _multipart: Multipart) -> Response {
|
||||||
|
// while let field = multipart.next_field().await {
|
||||||
|
// match field {
|
||||||
|
// Ok(field) => (StatusCode::OK, "Insert Link Here...".to_string()).into_response(),
|
||||||
|
|
||||||
|
// Err(err) => (
|
||||||
|
// StatusCode::BAD_REQUEST,
|
||||||
|
// format!("Error processing request: {}", err),
|
||||||
|
// )
|
||||||
|
// .into_response(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
color_eyre::install()?;
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
let _args = Args::parse();
|
||||||
|
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/", get(default_handler))
|
||||||
|
.route("/", post(upload_handler))
|
||||||
|
.layer(
|
||||||
|
ServiceBuilder::new()
|
||||||
|
.layer(HandleErrorLayer::new(handle_error))
|
||||||
|
.layer(TraceLayer::new_for_http())
|
||||||
|
// Set to about the expected number of concurrent connections.
|
||||||
|
.layer(BufferLayer::new(64))
|
||||||
|
// Limit to 30 per minute
|
||||||
|
.layer(RateLimitLayer::new(30, Duration::from_secs(60)))
|
||||||
|
.layer(RequestBodyLimitLayer::new(1024 * 1024))
|
||||||
|
.layer(DefaultBodyLimit::disable())
|
||||||
|
.layer(CompressionLayer::new()),
|
||||||
|
);
|
||||||
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
axum::serve(listener, app).await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
8
src/store/keyval/mod.rs
Normal file
8
src/store/keyval/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Default rocksdb storage.
|
||||||
|
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub async fn init(_path: impl AsRef<str>) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
1
src/store/mod.rs
Normal file
1
src/store/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub(crate) mod keyval;
|
||||||
Reference in New Issue
Block a user