Initial commit.

This commit is contained in:
2025-10-17 05:37:48 -05:00
commit cb4c8b3838
7 changed files with 1708 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

1580
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

21
Cargo.toml Normal file
View 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
View File

@@ -0,0 +1 @@
blah!

96
src/main.rs Normal file
View 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
View 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
View File

@@ -0,0 +1 @@
pub(crate) mod keyval;