Add config for client
This commit is contained in:
parent
b3368076e8
commit
bacc24e801
14 changed files with 398 additions and 25 deletions
79
Cargo.lock
generated
79
Cargo.lock
generated
|
@ -542,6 +542,28 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efdce149c370f133a071ca8ef6ea340b7b88748ab0810097a9e2976eaa34b4f3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz-build",
|
||||
"phf",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono-tz-build"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f10f8c9340e31fc120ff885fcdb54a0b48e474bbd77cab557f0c30a3e569402"
|
||||
dependencies = [
|
||||
"parse-zoneinfo",
|
||||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.37"
|
||||
|
@ -795,9 +817,12 @@ dependencies = [
|
|||
name = "dpts-config"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono-tz",
|
||||
"dpts-entity",
|
||||
"dpts-error",
|
||||
"iana-time-zone",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"toml",
|
||||
]
|
||||
|
@ -821,6 +846,7 @@ dependencies = [
|
|||
"axum",
|
||||
"chrono",
|
||||
"dotenv",
|
||||
"dpts-config",
|
||||
"dpts-entity",
|
||||
"dpts-migration",
|
||||
"log",
|
||||
|
@ -1895,6 +1921,15 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-zoneinfo"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
|
@ -1955,6 +1990,44 @@ dependencies = [
|
|||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_codegen"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||
dependencies = [
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||
dependencies = [
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
|
@ -2580,6 +2653,12 @@ dependencies = [
|
|||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
|
|
@ -25,9 +25,11 @@ dpts-entity = { path = "dpts-entity" }
|
|||
dpts-error = { path = "dpts-error" }
|
||||
dpts-migration = {path = "dpts-migration"}
|
||||
chrono = {version = "0.4", features = ["serde"]}
|
||||
chrono-tz = {version = "0.10.3", features = ["serde"]}
|
||||
clap = "4.5"
|
||||
dotenv = "0.15.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
thiserror = "2.0.12"
|
||||
tokio = "1.44.2"
|
||||
toml = "0.8.22"
|
||||
|
||||
|
|
|
@ -9,10 +9,14 @@ repository.workspace = true
|
|||
default = ["client", "server"]
|
||||
client = []
|
||||
server = []
|
||||
test = []
|
||||
|
||||
[dependencies]
|
||||
chrono-tz.workspace = true
|
||||
dpts-entity.workspace = true
|
||||
dpts-error.workspace = true
|
||||
iana-time-zone = "0.1.63"
|
||||
serde.workspace = true
|
||||
thiserror.workspace = true
|
||||
tokio.workspace = true
|
||||
toml.workspace = true
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
mod storage;
|
||||
|
||||
pub use storage::*;
|
||||
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize
|
||||
};
|
||||
|
||||
use chrono_tz::{Tz, UTC};
|
||||
use crate::{get_host_time_zone_or_utc, Error};
|
||||
use std::{
|
||||
str::FromStr,
|
||||
net::IpAddr
|
||||
};
|
||||
|
||||
|
||||
|
||||
pub struct ClientConfig {
|
||||
|
||||
}
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct PartialClientConfig {
|
||||
time_zone: Option<Tz>,
|
||||
storage: Option<PartialClientStorageConfig>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tokio::sync::OnceCell;
|
||||
use std::path::PathBuf;
|
||||
const EMPTY_CONFIG_TOML: &str = r#""#;
|
||||
static EMPTY_CONFIG_STRUCT: OnceCell<PartialClientConfig> = OnceCell::const_new();
|
||||
|
||||
async fn get_empty_config_struct() -> &'static PartialClientConfig {
|
||||
EMPTY_CONFIG_STRUCT.get_or_init(|| async {
|
||||
PartialClientConfig{
|
||||
time_zone: None,
|
||||
storage: None,
|
||||
}
|
||||
}).await
|
||||
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn deserialize_empty_client_config() {
|
||||
let config: PartialClientConfig = toml::from_str(EMPTY_CONFIG_TOML).unwrap();
|
||||
assert_eq!(&config, get_empty_config_struct().await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn serialize_empty_client_config() {
|
||||
assert_eq!(EMPTY_CONFIG_TOML, toml::to_string(get_empty_config_struct().await).unwrap());
|
||||
}
|
||||
|
||||
const LOCAL_STORAGE_CONFIG_TOML: &str = r#"time_zone = "UTC"
|
||||
|
||||
[storage.local]
|
||||
database_path = "."
|
||||
"#;
|
||||
static LOCAL_STORAGE_CONFIG_STRUCT: OnceCell<PartialClientConfig> = OnceCell::const_new();
|
||||
|
||||
async fn get_local_storage_client_config_struct() -> &'static PartialClientConfig {
|
||||
LOCAL_STORAGE_CONFIG_STRUCT.get_or_init(|| async {
|
||||
PartialClientConfig{
|
||||
time_zone: Some(UTC),
|
||||
storage: Some(PartialClientStorageConfig::Local(
|
||||
PartialClientLocalStorageConfig {
|
||||
database_path: Some(PathBuf::from_str(".").unwrap()),
|
||||
}
|
||||
)),
|
||||
}
|
||||
}).await
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn deserialize_local_storage_client_config() {
|
||||
let config: PartialClientConfig = toml::from_str(LOCAL_STORAGE_CONFIG_TOML).unwrap();
|
||||
assert_eq!(&config, get_local_storage_client_config_struct().await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn serialize_local_storage_client_config() {
|
||||
assert_eq!(LOCAL_STORAGE_CONFIG_TOML, toml::to_string(get_local_storage_client_config_struct().await).unwrap());
|
||||
}
|
||||
}
|
||||
|
29
dpts-config/src/client/storage.rs
Normal file
29
dpts-config/src/client/storage.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
mod local;
|
||||
mod remote;
|
||||
|
||||
pub use local::{
|
||||
ClientLocalStorageConfig,
|
||||
PartialClientLocalStorageConfig,
|
||||
};
|
||||
|
||||
pub use remote::{
|
||||
ClientRemoteStorageConfig,
|
||||
PartialClientRemoteStorageConfig,
|
||||
};
|
||||
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize
|
||||
};
|
||||
|
||||
pub enum ClientStorageConfig {
|
||||
Local(ClientLocalStorageConfig),
|
||||
Remote(ClientRemoteStorageConfig),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PartialClientStorageConfig {
|
||||
Local(PartialClientLocalStorageConfig),
|
||||
Remote(PartialClientRemoteStorageConfig),
|
||||
}
|
15
dpts-config/src/client/storage/local.rs
Normal file
15
dpts-config/src/client/storage/local.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use std::path::PathBuf;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize
|
||||
};
|
||||
|
||||
pub struct ClientLocalStorageConfig {
|
||||
pub database_path: PathBuf
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct PartialClientLocalStorageConfig {
|
||||
pub database_path: Option<PathBuf>,
|
||||
}
|
||||
|
22
dpts-config/src/client/storage/remote.rs
Normal file
22
dpts-config/src/client/storage/remote.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use chrono_tz::{Tz, UTC};
|
||||
use crate::{get_host_time_zone_or_utc, Error};
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize
|
||||
};
|
||||
|
||||
use std::{
|
||||
str::FromStr,
|
||||
net::IpAddr
|
||||
};
|
||||
|
||||
pub struct ClientRemoteStorageConfig {
|
||||
pub endpoint: String,
|
||||
pub access_key: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct PartialClientRemoteStorageConfig {
|
||||
pub endpoint: Option<String>,
|
||||
pub access_key: Option<String>,
|
||||
}
|
8
dpts-config/src/error.rs
Normal file
8
dpts-config/src/error.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("Parse toml error")]
|
||||
TomlDe(#[from] toml::de::Error),
|
||||
#[error("Missing config value: ({0})")]
|
||||
MissingConfig(String)
|
||||
|
||||
}
|
|
@ -1,9 +1,28 @@
|
|||
mod client;
|
||||
mod global;
|
||||
mod error;
|
||||
mod server;
|
||||
mod user;
|
||||
|
||||
pub use error::Error;
|
||||
pub use server::{
|
||||
ServerConfig,
|
||||
PartialServerConfig,
|
||||
};
|
||||
SERVER_CONFIG,
|
||||
};
|
||||
|
||||
pub use client::{
|
||||
ClientConfig,
|
||||
PartialClientConfig,
|
||||
};
|
||||
|
||||
use chrono_tz::{Tz, UTC};
|
||||
|
||||
fn get_host_time_zone_or_utc() -> Tz {
|
||||
match iana_time_zone::get_timezone() {
|
||||
Ok(x) => match x.parse(){
|
||||
Ok(x) => x,
|
||||
Err(_) => UTC
|
||||
},
|
||||
Err(_) => UTC
|
||||
}
|
||||
}
|
|
@ -1,37 +1,114 @@
|
|||
use dpts_error::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use chrono_tz::{Tz, UTC};
|
||||
use crate::{get_host_time_zone_or_utc, Error};
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
str::FromStr,
|
||||
net::IpAddr
|
||||
};
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
pub static SERVER_CONFIG: OnceServerConfig = OnceServerConfig::const_new();
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ServerConfig {
|
||||
pub listen_ips: Vec<String>,
|
||||
pub listen_ips: Vec<IpAddr>,
|
||||
pub port: u16,
|
||||
pub database_url: String
|
||||
pub database_url: String,
|
||||
pub time_zone: Tz,
|
||||
}
|
||||
|
||||
impl ServerConfig {
|
||||
|
||||
}
|
||||
|
||||
impl TryFrom<PartialServerConfig> for ServerConfig {
|
||||
type Error = Error;
|
||||
fn try_from(p: PartialServerConfig) -> Result<ServerConfig, Error>{
|
||||
Ok(ServerConfig{
|
||||
listen_ips: p.listen_ips.ok_or(Error::MissingConfig("listen_ips".to_string()))?,
|
||||
port: p.port.ok_or(Error::MissingConfig("port".to_string()))?,
|
||||
database_url: p.database_url.ok_or(Error::MissingConfig("database_url".to_string()))?,
|
||||
time_zone: p.time_zone.ok_or(Error::MissingConfig("time_zone".to_string()))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OnceServerConfig {
|
||||
inner: OnceCell<ServerConfig>,
|
||||
}
|
||||
|
||||
impl OnceServerConfig {
|
||||
const fn const_new() -> Self {
|
||||
Self {
|
||||
inner: OnceCell::const_new(),
|
||||
}
|
||||
}
|
||||
pub fn get(&self) -> Option<&ServerConfig> {
|
||||
self.inner.get()
|
||||
}
|
||||
pub async fn get_or_init<F, T>(&self, f: F) -> &ServerConfig where
|
||||
F: FnOnce() -> T,
|
||||
T: Future<Output = ServerConfig>
|
||||
{
|
||||
self.inner.get_or_init(f).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
||||
pub struct PartialServerConfig {
|
||||
pub listen_ips: Option<Vec<String>>,
|
||||
pub listen_ips: Option<Vec<IpAddr>>,
|
||||
pub port: Option<u16>,
|
||||
pub database_url: Option<String>,
|
||||
pub time_zone: Option<Tz>,
|
||||
}
|
||||
|
||||
impl PartialServerConfig {
|
||||
/// #Examples
|
||||
/// ```
|
||||
/// use dpts_config::PartialServerConfig;
|
||||
/// let config = PartialServerConfig::try_from_toml(r#"
|
||||
/// listen_ips = ["0.0.0.0"]
|
||||
/// port = 8000
|
||||
/// database_url = "sqlite::memory:"
|
||||
/// "#).unwrap();
|
||||
/// assert_eq!(config.listen_ips, Some(vec!["0.0.0.0".to_string()]));
|
||||
/// assert_eq!(config.port, Some(8000));
|
||||
/// assert_eq!(config.database_url, Some("sqlite::memory:".to_string()));
|
||||
/// ```
|
||||
///
|
||||
|
||||
pub fn try_from_toml(s: &str) -> Result<Self, Error> {
|
||||
Ok(toml::from_str(s)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PartialServerConfig {
|
||||
/// #Examples
|
||||
/// ```
|
||||
/// use dpts_config::PartialServerConfig;
|
||||
/// let config = PartialServerConfig::default();
|
||||
/// assert_eq!(config.listen_ips, Some(vec!["127.0.0.1".parse().unwrap(), "::1".parse().unwrap()]));
|
||||
/// assert_eq!(config.port, Some(3000));
|
||||
/// assert_eq!(config.database_url, None);
|
||||
/// assert_eq!(config.time_zone, Some(iana_time_zone::get_timezone().unwrap().parse().unwrap()))
|
||||
/// ```
|
||||
///
|
||||
fn default() -> Self {
|
||||
PartialServerConfig {
|
||||
listen_ips: Some(vec!["127.0.0.1".parse().unwrap(), "::1".parse().unwrap()]),
|
||||
port: Some(3000),
|
||||
database_url: None,
|
||||
time_zone: Some(get_host_time_zone_or_utc())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PartialServerConfig {
|
||||
type Err = Error;
|
||||
/// #Examples
|
||||
/// ```
|
||||
/// use dpts_config::PartialServerConfig;
|
||||
/// let config: PartialServerConfig = r#"
|
||||
/// listen_ips = ["0.0.0.0"]
|
||||
/// port = 8000
|
||||
/// database_url = "sqlite::memory:"
|
||||
/// time_zone = "Asia/Tokyo"
|
||||
/// "#.parse().unwrap();
|
||||
/// assert_eq!(config.listen_ips, Some(vec!["0.0.0.0".parse().unwrap()]));
|
||||
/// assert_eq!(config.port, Some(8000));
|
||||
/// assert_eq!(config.database_url, Some("sqlite::memory:".to_string()));
|
||||
/// assert_eq!(config.time_zone, Some("Asia/Tokyo".parse().unwrap()))
|
||||
/// ```
|
||||
///
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(toml::from_str(s)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,13 @@ license.workspace = true
|
|||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["server", "client"]
|
||||
client = []
|
||||
server = []
|
||||
test = []
|
||||
|
||||
[dependencies]
|
||||
dpts-config.workspace = true
|
||||
dpts-entity = { workspace = true }
|
||||
dpts-migration = { workspace = true }
|
||||
async-graphql = {version = "7.0", features = ["chrono"]}
|
||||
|
@ -28,4 +31,7 @@ features = [
|
|||
"sqlx-sqlite",
|
||||
"with-chrono",
|
||||
]
|
||||
default-features = false
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
dpts-config = { workspace = true, features = ["test"] }
|
|
@ -1,7 +1,7 @@
|
|||
use std::time::Duration;
|
||||
use sea_orm::{entity::*, query::*, ConnectOptions, Database, DatabaseConnection};
|
||||
use dpts_migration::{Migrator, MigratorTrait};
|
||||
|
||||
use dpts_config::ServerConfig;
|
||||
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
|
@ -25,6 +25,27 @@ impl OnceDatabaseConnection {
|
|||
self.inner.get_or_init(f).await
|
||||
}
|
||||
|
||||
pub async fn get_or_init_with_server_config(&self, c: &ServerConfig) -> &DatabaseConnection {
|
||||
self.get_or_init( || async {
|
||||
let mut opt = ConnectOptions::new(&c.database_url);
|
||||
opt.max_connections(100)
|
||||
.min_connections(5)
|
||||
.connect_timeout(Duration::from_secs(8))
|
||||
.acquire_timeout(Duration::from_secs(8))
|
||||
.idle_timeout(Duration::from_secs(8))
|
||||
.max_lifetime(Duration::from_secs(8))
|
||||
.sqlx_logging(true)
|
||||
.sqlx_logging_level(log::LevelFilter::Info);
|
||||
//.set_schema_search_path("my_schema"); // Setting default PostgreSQL schema
|
||||
let db = Database::connect(opt).await.unwrap();
|
||||
Migrator::fresh(&db).await.unwrap();
|
||||
db
|
||||
}).await
|
||||
}
|
||||
pub async fn get_or_init_with_static_server_config(&self) -> &DatabaseConnection {
|
||||
self.get_or_init_with_server_config(dpts_config::SERVER_CONFIG.get().unwrap()).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub async fn init_test(&self) {
|
||||
self.get_or_init( || async {
|
||||
|
@ -47,6 +68,7 @@ impl OnceDatabaseConnection {
|
|||
|
||||
}
|
||||
|
||||
|
||||
pub static DATABASE_CONNECTION: OnceDatabaseConnection = OnceDatabaseConnection::new();
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,11 +3,14 @@ pub enum Error {
|
|||
#[error("Parser error")]
|
||||
Clap(#[from] clap::Error),
|
||||
#[error("Parse int error")]
|
||||
ParseIntError(#[from] std::num::ParseIntError),
|
||||
ParseInt(#[from] std::num::ParseIntError),
|
||||
#[error("IO Error")]
|
||||
IoError(#[from] std::io::Error),
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("Parse toml error")]
|
||||
TomlDe(#[from] toml::de::Error),
|
||||
#[error("Missing config value: ({0})")]
|
||||
MissingConfig(String)
|
||||
|
||||
}
|
||||
|
||||
impl Error {
|
||||
|
|
Loading…
Add table
Reference in a new issue