From aaeefc2597cee67708289ae78c0d797f3de84e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luukas=20P=C3=B6rtfors?= Date: Sat, 24 Aug 2024 14:31:43 +0300 Subject: [PATCH 1/4] feat: use clap for parsing configuration --- Cargo.lock | 108 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/api/mod.rs | 13 +++--- src/mailgun/mod.rs | 27 +++++++++--- src/main.rs | 53 ++++++++++++++++------ src/state.rs | 11 +---- 6 files changed, 178 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c43c4ff..e51d380 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,55 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.86" @@ -457,6 +506,46 @@ dependencies = [ "serde", ] +[[package]] +name = "clap" +version = "4.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.73", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + [[package]] name = "cobs" version = "0.2.3" @@ -469,6 +558,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "comemo" version = "0.4.0" @@ -1503,6 +1598,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.12.1" @@ -1559,6 +1660,7 @@ dependencies = [ "axum-test", "axum-valid", "axum_typed_multipart", + "clap", "comemo", "dotenv", "fontdb 0.17.0", @@ -3649,6 +3751,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" version = "1.10.0" diff --git a/Cargo.toml b/Cargo.toml index ba35841..b18667f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ axum-valid = { version = "0.14.0", features = [ "typed_multipart", ], default-features = false } axum_typed_multipart = "0.11.0" +clap = { version = "4.5.16", features = ["env", "derive"] } comemo = { version = "0.4.0" } dotenv = "0.15.0" fontdb = { version = "0.17.0", optional = true } diff --git a/src/api/mod.rs b/src/api/mod.rs index 3619a7b..adb8f97 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,6 +1,6 @@ use axum::{ extract::DefaultBodyLimit, - http::Method, + http::{HeaderValue, Method}, routing::{get, post}, Router, }; @@ -12,10 +12,13 @@ use tower_http::{cors::CorsLayer, limit::RequestBodyLimitLayer, trace::TraceLaye pub mod invoices; pub fn app() -> Router { - let cors_layer = CorsLayer::new().allow_origin([ - "https://tietokilta.fi".parse().unwrap(), - "http://localhost:3000".parse().unwrap(), - ]); + let cors_layer = CorsLayer::new().allow_origin( + crate::CONFIG + .allowed_origins + .iter() + .map(|c| c.parse::().unwrap()) + .collect::>(), + ); let governor_config = Arc::new( GovernorConfigBuilder::default() diff --git a/src/mailgun/mod.rs b/src/mailgun/mod.rs index 18423bb..97cf4c4 100644 --- a/src/mailgun/mod.rs +++ b/src/mailgun/mod.rs @@ -7,14 +7,27 @@ use axum::{ mod invoices; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MailgunClient { - pub client: reqwest::Client, - pub url: String, - pub api_user: String, - pub api_key: String, - pub default_to: String, - pub from: String, + client: reqwest::Client, + url: String, + api_user: String, + api_key: String, + default_to: String, + from: String, +} + +impl From for MailgunClient { + fn from(config: crate::MailgunConfig) -> Self { + Self { + client: reqwest::Client::new(), + url: config.url, + api_user: config.user, + api_key: config.password, + default_to: config.to, + from: config.from, + } + } } #[async_trait] diff --git a/src/main.rs b/src/main.rs index b6e89e7..2d6cc0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; +use clap::Parser; use std::net::SocketAddr; +use std::sync::LazyLock; mod api; mod error; @@ -16,6 +18,43 @@ mod tests; #[macro_use] extern crate tracing; +#[derive(Parser, Clone, Debug)] +struct MailgunConfig { + /// Url used by mailgun + #[clap(long = "mailgun-url", env = "MAILGUN_URL")] + url: String, + /// Username used by mailgun + #[clap(long = "mailgun-user", env = "MAILGUN_USER")] + user: String, + /// Password used by mailgun + #[clap(long = "mailgun-password", env = "MAILGUN_PASSWORD")] + password: String, + /// Initial To-value used by mailgun + #[clap(long = "mailgun-to", env = "MAILGUN_TO")] + to: String, + /// From-value used by mailgun + #[clap(long = "mailgun-from", env = "MAILGUN_FROM")] + from: String, +} + +#[derive(Parser, Clone, Debug)] +#[command(version, about, long_about = None)] +struct LaskugenConfig { + #[clap(flatten)] + mailgun: MailgunConfig, + /// The listen port for the HTTP server + #[clap(long, env, required = false, default_value = "3000")] + port: u16, + /// The ip address to bound by the HTTP server + #[clap(long, env, required = false, default_value = "127.0.0.1")] + bind_addr: std::net::IpAddr, + /// A comma-separated list of allowed origins + #[clap(long, env, required = false, value_delimiter = ',')] + allowed_origins: Vec, +} + +static CONFIG: LazyLock = LazyLock::new(|| LaskugenConfig::parse()); + #[tokio::main] async fn main() { dotenv::dotenv().ok(); @@ -27,19 +66,7 @@ async fn main() { .init(); let state = state::new().await; - - let ip = if std::env::var("EXPOSE").unwrap_or("0".into()) == "1" { - [0, 0, 0, 0] - } else { - [127, 0, 0, 1] - }; - - let addr = SocketAddr::from(( - ip, - std::env::var("PORT") - .map(|p| p.parse::().unwrap()) - .unwrap_or(3000), - )); + let addr = SocketAddr::from((CONFIG.bind_addr, CONFIG.port)); debug!("Listening on {addr}"); let listener = tokio::net::TcpListener::bind(addr) .await diff --git a/src/state.rs b/src/state.rs index ed14fd2..83d2a02 100644 --- a/src/state.rs +++ b/src/state.rs @@ -10,17 +10,8 @@ pub struct State { pub async fn new() -> State { dotenv::dotenv().ok(); - let mailgun_client = MailgunClient { - client: reqwest::Client::new(), - url: std::env::var("MAILGUN_URL").expect("No MAILGUN_URL in env"), - api_user: std::env::var("MAILGUN_USER").expect("No MAILGUN_USER in env"), - api_key: std::env::var("MAILGUN_PASSWORD").expect("No MAILGUN_PASSWORD in env"), - default_to: std::env::var("MAILGUN_TO").expect("No MAILGUN_TO in env"), - from: std::env::var("MAILGUN_FROM").expect("No MAILGUN_FROM in env"), - }; - State { - mailgun_client, + mailgun_client: MailgunClient::from(crate::CONFIG.mailgun.clone()), for_garde: (), } } From b20bb4bd76c45e595a16c20cb6342e674864114e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luukas=20P=C3=B6rtfors?= Date: Sat, 24 Aug 2024 16:03:19 +0300 Subject: [PATCH 2/4] chore: clippy --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 2d6cc0c..764b082 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,7 +53,7 @@ struct LaskugenConfig { allowed_origins: Vec, } -static CONFIG: LazyLock = LazyLock::new(|| LaskugenConfig::parse()); +static CONFIG: LazyLock = LazyLock::new(LaskugenConfig::parse); #[tokio::main] async fn main() { From 832783389e20ba17de86c6ea79c808dc7d6016fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luukas=20P=C3=B6rtfors?= Date: Sat, 24 Aug 2024 17:23:28 +0300 Subject: [PATCH 3/4] feat: set BIND_ADDR env in Dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index f6d7e9b..fb19dba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,7 @@ RUN touch src/main.rs RUN cargo build --release FROM alpine as runtime +ENV BIND_ADDR 0.0.0.0 WORKDIR /app COPY --from=builder /app/target/release/laskugeneraattori app EXPOSE 5237 From 879c464b2ea2892be541cfe08a64652b71036037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luukas=20P=C3=B6rtfors?= Date: Sat, 24 Aug 2024 17:25:48 +0300 Subject: [PATCH 4/4] docs: add supported filetypes to error message --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 37960f8..61d573e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,7 +16,7 @@ pub enum Error { MultipartRejection(#[from] axum::extract::multipart::MultipartRejection), #[error("Missing filename multipart")] MissingFilename, - #[error("Unsupported file format: {0}")] + #[error("Unsupported file format: {0}. Supported file formats are (jpg|jpeg|png|gif|svg|pdf)")] UnsupportedFileFormat(String), #[error("Error in handling json value")] JsonRejection(#[from] axum::extract::rejection::JsonRejection),