Skip to content

Commit

Permalink
refactor read config
Browse files Browse the repository at this point in the history
  • Loading branch information
robatipoor committed Jan 19, 2024
1 parent 131ffd0 commit b99392f
Show file tree
Hide file tree
Showing 18 changed files with 205 additions and 187 deletions.
43 changes: 27 additions & 16 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description = "transfer file"
documentation = ""
edition = "2021"
homepage = "https://github.com/robatipoor/pf"
keywords = ["paste","file","transfer"]
keywords = ["paste", "file", "transfer"]
license = "MIT"
readme = "./README.md"
repository = "https://github.com/robatipoor/pf"
Expand All @@ -27,43 +27,54 @@ strip = true
anyhow = "1.0.79"
argon2 = "0.5.2"
assert_cmd = "2.0.13"
async-stream = {version = "0.3.5"}
async-stream = { version = "0.3.5" }
async-trait = "0.1.77"
axum = {version = "0.7.4", features = ["multipart"]}
axum-extra = {version = "0.9.2", features = ["async-read-body"]}
axum = { version = "0.7.4", features = ["multipart"] }
axum-extra = { version = "0.9.2", features = ["async-read-body"] }
base64 = "0.21.7"
bincode = "1.3.3"
build_html = "2.4.0"
chrono = {version = "0.4.31", features = ["serde"]}
clap = {version = "4.4.18", features = ["derive"]}
config = {version = "0.13.4", default-features = false, features = ["toml"]}
chrono = { version = "0.4.31", features = ["serde"] }
clap = { version = "4.4.18", features = ["derive"] }
config = { version = "0.13.4", default-features = false, features = ["toml"] }
cuid2 = "0.1.2"
fake = {version = "2.9.2", features = ['derive', 'uuid', 'chrono']}
fake = { version = "2.9.2", features = ['derive', 'uuid', 'chrono'] }
futures-util = "0.3.30"
wiremock = {version = "0.5.22"}
wiremock = { version = "0.5.22" }
hyper = "1.1.0"
indicatif = "0.17.7"
log = "0.4.20"
log-derive = "0.4.1"
mime_guess = "2.0.4"
multer = "3.0.0"
once_cell = {version = "1.19.0"}
once_cell = { version = "1.19.0" }
project-root = "0.2.2"
qrcodegen = "1.8.0"
rand = "0.8.5"
regex = "1.10.2"
reqwest = {version = "0.11.23", default-features = false, features = ["json", "multipart", "stream", "rustls-tls"]}
serde = {version = "1.0.195", features = ["derive"]}
reqwest = { version = "0.11.23", default-features = false, features = [
"json",
"multipart",
"stream",
"rustls-tls",
] }
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
sled = "0.34.7"
strum = { version = "0.25.0", features = ["derive"] }
test-context = "0.1.4"
thiserror = "1.0.56"
tokio = {version = "1.35.1", features = ["macros", "time", "process", "net", "rt-multi-thread"]}
tokio = { version = "1.35.1", features = [
"macros",
"time",
"process",
"net",
"rt-multi-thread",
] }
tokio-util = "0.7.10"
tower = {version = "0.4.13", features = ["util"]}
tower-http = {version = "0.5.1", features = ["fs"]}
tower = { version = "0.4.13", features = ["util"] }
tower-http = { version = "0.5.1", features = ["fs"] }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
url = "2.5.0"
validator = {version = "0.16.1", features = ["derive"]}
garde = "0.17.0"
66 changes: 33 additions & 33 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,36 @@ name = "api"
path = "src/bin/main.rs"

[dependencies]
sdk = {path = "../sdk"}
anyhow = {workspace = true}
argon2 = {workspace = true}
async-trait = {workspace = true}
clap = {workspace = true}
axum = {workspace = true}
axum-extra = {workspace = true}
bincode = {workspace = true}
build_html = {workspace = true}
chrono = {workspace = true}
config = {workspace = true}
cuid2 = {workspace = true}
fake = {workspace = true}
futures-util = {workspace = true}
hyper = {workspace = true}
once_cell = {workspace = true}
qrcodegen = {workspace = true}
rand = {workspace = true}
reqwest = {workspace = true}
serde = {workspace = true}
serde_json = {workspace = true}
sled = {workspace = true}
strum = {workspace = true}
test-context = {workspace = true}
thiserror = {workspace = true}
tokio = {workspace = true}
tokio-util = {workspace = true}
tower = {workspace = true}
tower-http = {workspace = true}
tracing = {workspace = true}
tracing-subscriber = {workspace = true}
url = {workspace = true}
validator = {workspace = true}
sdk = { path = "../sdk" }
anyhow = { workspace = true }
argon2 = { workspace = true }
async-trait = { workspace = true }
clap = { workspace = true }
axum = { workspace = true }
axum-extra = { workspace = true }
bincode = { workspace = true }
build_html = { workspace = true }
chrono = { workspace = true }
config = { workspace = true }
cuid2 = { workspace = true }
fake = { workspace = true }
futures-util = { workspace = true }
hyper = { workspace = true }
once_cell = { workspace = true }
qrcodegen = { workspace = true }
rand = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sled = { workspace = true }
strum = { workspace = true }
test-context = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true }
tower = { workspace = true }
tower-http = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
url = { workspace = true }
garde = { workspace = true }
2 changes: 1 addition & 1 deletion api/settings/base.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
domain = "http://127.0.0.1"
max_upload_size = 1024
default_code_length = 3
default_expire_time = 7200
default_expire_secs = 7200

[server]
schema = "http"
Expand Down
10 changes: 8 additions & 2 deletions api/src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
use api::{error::ApiResult, server::ApiServer, util::tracing::INIT_SUBSCRIBER};
use api::{
error::ApiResult,
server::ApiServer,
util::{arg::get_env_source, tracing::INIT_SUBSCRIBER},
};
use axum::extract::State;
use clap::Parser;
use once_cell::sync::Lazy;
use tracing::warn;

#[tokio::main]
async fn main() -> ApiResult {
let config = api::configure::ApiConfig::read()?;
let args = api::configure::Args::parse();
let config = api::configure::ApiConfig::read(args.config, get_env_source("PF"))?;
Lazy::force(&INIT_SUBSCRIBER);
if let Err(e) = tokio::fs::create_dir_all(&config.fs.base_dir).await {
warn!("failed create base dir: {e}");
Expand Down
51 changes: 28 additions & 23 deletions api/src/configure/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use crate::util::arg::get_env_source;
use clap::Parser;
use config::ConfigError;
use config::Environment;
use once_cell::sync::Lazy;
use serde::Deserialize;
use std::{
net::{AddrParseError, SocketAddr},
path::PathBuf,
};

pub static CONFIG: Lazy<ApiConfig> = Lazy::new(|| ApiConfig::read().unwrap());
pub static CONFIG: Lazy<ApiConfig> =
Lazy::new(|| ApiConfig::read(None, get_env_source("PF")).unwrap());

#[derive(Debug, Deserialize, Clone)]
pub struct ApiConfig {
Expand Down Expand Up @@ -59,29 +59,34 @@ impl ServerConfig {
}

impl ApiConfig {
pub fn read() -> Result<Self, config::ConfigError> {
let mut cfg = config::Config::builder();
let env_src = get_env_source("PF");
if let Some(path) = Args::parse().config {
cfg = cfg.add_source(config::File::from(path)).add_source(env_src);
} else {
let base_config = std::env::current_dir()
.map_err(|e| ConfigError::Message(e.to_string()))?
.join("settings")
.join("base.toml");
cfg = cfg
.add_source(config::File::from(base_config))
.add_source(env_src);
}
cfg.build()?.try_deserialize()
pub fn read(
arg_file_src: Option<PathBuf>,
env_src: Environment,
) -> Result<Self, config::ConfigError> {
config::Config::builder()
.add_source(config::File::from(
get_basic_settings_path(arg_file_src)
.map_err(|e| config::ConfigError::Message(e.to_string()))?,
))
.add_source(env_src)
.build()?
.try_deserialize()
}
}

#[derive(clap::Parser, Debug)]
fn get_basic_settings_path(arg_path: Option<PathBuf>) -> std::io::Result<PathBuf> {
if let Some(path) = arg_path {
Ok(path)
} else {
Ok(std::env::current_dir()?.join("settings").join("base.toml"))
}
}

#[derive(clap::Parser, Debug, Default)]
#[command(author, version, about, long_about = None)]
struct Args {
pub struct Args {
#[arg(short, long)]
config: Option<PathBuf>,
pub config: Option<PathBuf>,
}

#[cfg(test)]
Expand All @@ -91,7 +96,7 @@ mod tests {

#[test]
fn test_read_config() {
let config = ApiConfig::read();
assert!(config.is_ok());
let env_ns = "TEST_PF";
ApiConfig::read(None, get_env_source(env_ns)).unwrap();
}
}
2 changes: 1 addition & 1 deletion api/src/database/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl Database {
}
}
Err(err) if err.current.is_some() => {
return Err(ApiError::ResourceExists("File path exists".to_string()));
return Err(ApiError::ResourceExistsError("File path exists".to_string()));
}
Err(err) => {
tracing::error!("Compare and swap error: {err}");
Expand Down
43 changes: 18 additions & 25 deletions api/src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ pub type ApiResult<T = ()> = std::result::Result<T, ApiError>;
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
#[error(transparent)]
InvalidInput(#[from] validator::ValidationErrors),
InvalidInputError(#[from] garde::Report),
#[error("bad request: {0}")]
BadRequest(String),
BadRequestError(String),
#[error("resource not found: {0}")]
NotFound(String),
NotFoundError(String),
#[error("{0}")]
PermissionDenied(String),
PermissionDeniedError(String),
#[error("resource not available: {0}")]
NotAvailable(String),
NotAvailableError(String),
#[error("resource {0} exists already")]
ResourceExists(String),
ResourceExistsError(String),
#[error(transparent)]
ConfigError(#[from] config::ConfigError),
#[error(transparent)]
Expand Down Expand Up @@ -50,23 +50,23 @@ pub enum ApiError {
#[error("duration out of range error: {0}")]
DurationOutOfRangeError(#[from] chrono::OutOfRangeError),
#[error(transparent)]
Unknown(#[from] anyhow::Error),
UnknownError(#[from] anyhow::Error),
}

impl ApiError {
pub fn response(&self) -> (BodyResponseError, StatusCode) {
use ApiError::*;
let (error_type, error_message, status_code) = match self {
InvalidInput(err) => (
InvalidInputError(err) => (
"INVALID_INPUT",
err.to_string(),
StatusCode::UNPROCESSABLE_ENTITY,
),
BadRequest(err) => ("BAD_REQUEST", err.to_string(), StatusCode::BAD_REQUEST),
PermissionDenied(err) => ("PERMISSION_DENIED", err.to_string(), StatusCode::FORBIDDEN),
NotAvailable(err) => ("NOT_AVAILABLE", err.to_string(), StatusCode::NOT_FOUND),
NotFound(err) => ("NOT_FOUND", err.to_string(), StatusCode::NOT_FOUND),
ResourceExists(err) => ("RESOURCE_EXISTS", err.to_string(), StatusCode::CONFLICT),
BadRequestError(err) => ("BAD_REQUEST", err.to_string(), StatusCode::BAD_REQUEST),
PermissionDeniedError(err) => ("PERMISSION_DENIED", err.to_string(), StatusCode::FORBIDDEN),
NotAvailableError(err) => ("NOT_AVAILABLE", err.to_string(), StatusCode::NOT_FOUND),
NotFoundError(err) => ("NOT_FOUND", err.to_string(), StatusCode::NOT_FOUND),
ResourceExistsError(err) => ("RESOURCE_EXISTS", err.to_string(), StatusCode::CONFLICT),
ConfigError(err) => (
"CONFIG_ERROR",
err.to_string(),
Expand Down Expand Up @@ -127,7 +127,7 @@ impl ApiError {
err.to_string(),
StatusCode::INTERNAL_SERVER_ERROR,
),
Unknown(err) => (
UnknownError(err) => (
"UNKNOWN_ERROR",
err.to_string(),
StatusCode::INTERNAL_SERVER_ERROR,
Expand Down Expand Up @@ -158,16 +158,9 @@ impl IntoResponse for ApiError {
}

pub fn invalid_input_error(field: &'static str, message: &'static str) -> ApiError {
let mut err = validator::ValidationErrors::new();
err.add(
field,
validator::ValidationError {
code: std::borrow::Cow::from("1"),
message: Some(std::borrow::Cow::Borrowed(message)),
params: std::collections::HashMap::new(),
},
);
ApiError::InvalidInput(err)
let mut report = garde::Report::new();
report.append(garde::Path::new(field), garde::Error::new(message));
ApiError::InvalidInputError(report)
}

pub trait ToApiResult<T> {
Expand All @@ -176,6 +169,6 @@ pub trait ToApiResult<T> {

impl<T> ToApiResult<T> for Option<T> {
fn to_result(self) -> ApiResult<T> {
self.ok_or_else(|| ApiError::NotFound(format!("{} not found", std::any::type_name::<T>())))
self.ok_or_else(|| ApiError::NotFoundError(format!("{} not found", std::any::type_name::<T>())))
}
}
8 changes: 4 additions & 4 deletions api/src/handler/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use axum::{
};

use sdk::model::{
request::UploadParamQuery,
request::UploadQueryParam,
response::{MessageResponse, MetaDataFileResponse, UploadResponse},
};

use garde::Validate;
use tower::ServiceExt;
use tower_http::services::fs::ServeFileSystemResponseBody;
use validator::Validate;

use crate::{
error::ApiResult,
Expand All @@ -23,11 +23,11 @@ use crate::{

pub async fn upload(
State(state): State<ApiState>,
Query(query): Query<UploadParamQuery>,
Query(query): Query<UploadQueryParam>,
headers: HeaderMap,
multipart: Multipart,
) -> ApiResult<Json<UploadResponse>> {
query.validate()?;
query.validate(&())?;
let secret = crate::util::http::parse_basic_auth(&headers)?;
let (path, expire_time) = service::file::store(&state, &query, secret, multipart).await?;
let url = path.url(&state.config.domain);
Expand Down
Loading

0 comments on commit b99392f

Please sign in to comment.