diff options
author | FxQnLr <[email protected]> | 2024-02-12 14:58:08 +0100 |
---|---|---|
committer | FxQnLr <[email protected]> | 2024-02-12 14:58:08 +0100 |
commit | 8ed77d7ab484121e9d70158e14c9fd6c243f1c70 (patch) | |
tree | dabecfb3eaec1420782eb9d3987e54ba83612b18 | |
parent | e4832b4cf36ba0eaed298ee458498eddd7176590 (diff) | |
download | webol-8ed77d7ab484121e9d70158e14c9fd6c243f1c70.tar webol-8ed77d7ab484121e9d70158e14c9fd6c243f1c70.tar.gz webol-8ed77d7ab484121e9d70158e14c9fd6c243f1c70.zip |
Close #9. Config impl with struct and files
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | src/auth.rs | 13 | ||||
-rw-r--r-- | src/config.rs | 32 | ||||
-rw-r--r-- | src/db.rs | 16 | ||||
-rw-r--r-- | src/main.rs | 14 | ||||
-rw-r--r-- | src/routes/device.rs | 6 | ||||
-rw-r--r-- | src/routes/start.rs | 8 | ||||
-rw-r--r-- | src/services/ping.rs | 6 |
9 files changed, 47 insertions, 53 deletions
@@ -11,5 +11,6 @@ target/ | |||
11 | 11 | ||
12 | *.sqlite* | 12 | *.sqlite* |
13 | .env | 13 | .env |
14 | config.* | ||
14 | 15 | ||
15 | .idea | 16 | .idea |
@@ -6,6 +6,4 @@ WEBOL_APIKEY: `String` | |||
6 | 6 | ||
7 | WEBOL_SERVERADDR: `Option<String>` (0.0.0.0:7229) | 7 | WEBOL_SERVERADDR: `Option<String>` (0.0.0.0:7229) |
8 | 8 | ||
9 | WEBOL_BINDADDR: `Option<String>` (0.0.0.0:1111) | 9 | WEBOL_PINGTIMEOUT: `Option<i64>` (10) |
10 | |||
11 | WEBOL_PINGTIMEOUT: `Option<i64>` (10) \ No newline at end of file | ||
diff --git a/src/auth.rs b/src/auth.rs index 0321ade..feca652 100644 --- a/src/auth.rs +++ b/src/auth.rs | |||
@@ -2,15 +2,13 @@ use axum::http::{StatusCode, HeaderValue}; | |||
2 | use axum::http::header::ToStrError; | 2 | use axum::http::header::ToStrError; |
3 | use tracing::{debug, error, trace}; | 3 | use tracing::{debug, error, trace}; |
4 | use crate::auth::Error::{MissingSecret, WrongSecret}; | 4 | use crate::auth::Error::{MissingSecret, WrongSecret}; |
5 | use crate::config::SETTINGS; | 5 | use crate::config::Config; |
6 | 6 | ||
7 | pub fn auth(secret: Option<&HeaderValue>) -> Result<bool, Error> { | 7 | pub fn auth(config: &Config, secret: Option<&HeaderValue>) -> Result<bool, Error> { |
8 | debug!("auth request with secret {:?}", secret); | 8 | debug!("auth request with secret {:?}", secret); |
9 | if let Some(value) = secret { | 9 | if let Some(value) = secret { |
10 | trace!("value exists"); | 10 | trace!("value exists"); |
11 | let key = SETTINGS | 11 | let key = &config.apikey; |
12 | .get_string("apikey") | ||
13 | .map_err(Error::Config)?; | ||
14 | if value.to_str().map_err(Error::HeaderToStr)? == key.as_str() { | 12 | if value.to_str().map_err(Error::HeaderToStr)? == key.as_str() { |
15 | debug!("successful auth"); | 13 | debug!("successful auth"); |
16 | Ok(true) | 14 | Ok(true) |
@@ -28,7 +26,6 @@ pub fn auth(secret: Option<&HeaderValue>) -> Result<bool, Error> { | |||
28 | pub enum Error { | 26 | pub enum Error { |
29 | WrongSecret, | 27 | WrongSecret, |
30 | MissingSecret, | 28 | MissingSecret, |
31 | Config(config::ConfigError), | ||
32 | HeaderToStr(ToStrError) | 29 | HeaderToStr(ToStrError) |
33 | } | 30 | } |
34 | 31 | ||
@@ -37,10 +34,6 @@ impl Error { | |||
37 | match self { | 34 | match self { |
38 | Self::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"), | 35 | Self::WrongSecret => (StatusCode::UNAUTHORIZED, "Wrong credentials"), |
39 | Self::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"), | 36 | Self::MissingSecret => (StatusCode::BAD_REQUEST, "Missing credentials"), |
40 | Self::Config(err) => { | ||
41 | error!("server error: {}", err.to_string()); | ||
42 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") | ||
43 | }, | ||
44 | Self::HeaderToStr(err) => { | 37 | Self::HeaderToStr(err) => { |
45 | error!("server error: {}", err.to_string()); | 38 | error!("server error: {}", err.to_string()); |
46 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") | 39 | (StatusCode::INTERNAL_SERVER_ERROR, "Server Error") |
diff --git a/src/config.rs b/src/config.rs index 4c79810..e88ddab 100644 --- a/src/config.rs +++ b/src/config.rs | |||
@@ -1,11 +1,25 @@ | |||
1 | use config::Config; | 1 | use config::File; |
2 | use once_cell::sync::Lazy; | 2 | use serde::Deserialize; |
3 | 3 | ||
4 | pub static SETTINGS: Lazy<Config> = Lazy::new(setup); | 4 | #[derive(Debug, Clone, Deserialize)] |
5 | pub struct Config { | ||
6 | pub database_url: String, | ||
7 | pub apikey: String, | ||
8 | pub serveraddr: String, | ||
9 | pub pingtimeout: i64, | ||
10 | } | ||
11 | |||
12 | impl Config { | ||
13 | pub fn load() -> Result<Self, config::ConfigError> { | ||
14 | let config = config::Config::builder() | ||
15 | .set_default("serveraddr", "0.0.0.0:7229")? | ||
16 | .set_default("pingtimeout", 10)? | ||
17 | .add_source(File::with_name("config.toml").required(false)) | ||
18 | .add_source(File::with_name("config.dev.toml").required(false)) | ||
19 | .add_source(config::Environment::with_prefix("WEBOL").separator("_")) | ||
20 | .build()?; | ||
21 | |||
22 | config.try_deserialize() | ||
23 | } | ||
24 | } | ||
5 | 25 | ||
6 | fn setup() -> Config { | ||
7 | Config::builder() | ||
8 | .add_source(config::Environment::with_prefix("WEBOL").separator("_")) | ||
9 | .build() | ||
10 | .unwrap() | ||
11 | } \ No newline at end of file | ||
@@ -1,13 +1,7 @@ | |||
1 | #[cfg(debug_assertions)] | ||
2 | use std::env; | ||
3 | |||
4 | use serde::Serialize; | 1 | use serde::Serialize; |
5 | use sqlx::{PgPool, postgres::PgPoolOptions}; | 2 | use sqlx::{PgPool, postgres::PgPoolOptions}; |
6 | use tracing::{debug, info}; | 3 | use tracing::{debug, info}; |
7 | 4 | ||
8 | #[cfg(not(debug_assertions))] | ||
9 | use crate::config::SETTINGS; | ||
10 | |||
11 | #[derive(Serialize, Debug)] | 5 | #[derive(Serialize, Debug)] |
12 | pub struct Device { | 6 | pub struct Device { |
13 | pub id: String, | 7 | pub id: String, |
@@ -17,18 +11,12 @@ pub struct Device { | |||
17 | pub times: Option<Vec<i64>> | 11 | pub times: Option<Vec<i64>> |
18 | } | 12 | } |
19 | 13 | ||
20 | pub async fn init_db_pool() -> PgPool { | 14 | pub async fn init_db_pool(db_url: &str) -> PgPool { |
21 | #[cfg(not(debug_assertions))] | ||
22 | let db_url = SETTINGS.get_string("database.url").unwrap(); | ||
23 | |||
24 | #[cfg(debug_assertions)] | ||
25 | let db_url = env::var("DATABASE_URL").unwrap(); | ||
26 | |||
27 | debug!("attempt to connect dbPool to '{}'", db_url); | 15 | debug!("attempt to connect dbPool to '{}'", db_url); |
28 | 16 | ||
29 | let pool = PgPoolOptions::new() | 17 | let pool = PgPoolOptions::new() |
30 | .max_connections(5) | 18 | .max_connections(5) |
31 | .connect(&db_url) | 19 | .connect(db_url) |
32 | .await | 20 | .await |
33 | .unwrap(); | 21 | .unwrap(); |
34 | 22 | ||
diff --git a/src/main.rs b/src/main.rs index 9d30548..4ef129b 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use std::env; | 1 | use std::env; |
2 | use std::net::SocketAddr; | ||
3 | use std::sync::Arc; | 2 | use std::sync::Arc; |
4 | use axum::{Router, routing::post}; | 3 | use axum::{Router, routing::post}; |
5 | use axum::routing::{get, put}; | 4 | use axum::routing::{get, put}; |
@@ -9,7 +8,7 @@ use time::util::local_offset; | |||
9 | use tokio::sync::broadcast::{channel, Sender}; | 8 | use tokio::sync::broadcast::{channel, Sender}; |
10 | use tracing::{info, level_filters::LevelFilter}; | 9 | use tracing::{info, level_filters::LevelFilter}; |
11 | use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*}; | 10 | use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*}; |
12 | use crate::config::SETTINGS; | 11 | use crate::config::Config; |
13 | use crate::db::init_db_pool; | 12 | use crate::db::init_db_pool; |
14 | use crate::routes::device; | 13 | use crate::routes::device; |
15 | use crate::routes::start::start; | 14 | use crate::routes::start::start; |
@@ -47,16 +46,18 @@ async fn main() -> color_eyre::eyre::Result<()> { | |||
47 | 46 | ||
48 | let version = env!("CARGO_PKG_VERSION"); | 47 | let version = env!("CARGO_PKG_VERSION"); |
49 | 48 | ||
49 | let config = Config::load()?; | ||
50 | |||
50 | info!("start webol v{}", version); | 51 | info!("start webol v{}", version); |
51 | 52 | ||
52 | let db = init_db_pool().await; | 53 | let db = init_db_pool(&config.database_url).await; |
53 | sqlx::migrate!().run(&db).await.unwrap(); | 54 | sqlx::migrate!().run(&db).await.unwrap(); |
54 | 55 | ||
55 | let (tx, _) = channel(32); | 56 | let (tx, _) = channel(32); |
56 | 57 | ||
57 | let ping_map: StatusMap = DashMap::new(); | 58 | let ping_map: StatusMap = DashMap::new(); |
58 | 59 | ||
59 | let shared_state = Arc::new(AppState { db, ping_send: tx, ping_map }); | 60 | let shared_state = Arc::new(AppState { db, config: config.clone(), ping_send: tx, ping_map }); |
60 | 61 | ||
61 | let app = Router::new() | 62 | let app = Router::new() |
62 | .route("/start", post(start)) | 63 | .route("/start", post(start)) |
@@ -66,9 +67,9 @@ async fn main() -> color_eyre::eyre::Result<()> { | |||
66 | .route("/status", get(status)) | 67 | .route("/status", get(status)) |
67 | .with_state(shared_state); | 68 | .with_state(shared_state); |
68 | 69 | ||
69 | let addr = SETTINGS.get_string("serveraddr").unwrap_or("0.0.0.0:7229".to_string()); | 70 | let addr = config.serveraddr; |
70 | info!("start server on {}", addr); | 71 | info!("start server on {}", addr); |
71 | let listener = tokio::net::TcpListener::bind(addr.parse::<SocketAddr>()?) | 72 | let listener = tokio::net::TcpListener::bind(addr) |
72 | .await?; | 73 | .await?; |
73 | axum::serve(listener, app).await?; | 74 | axum::serve(listener, app).await?; |
74 | 75 | ||
@@ -77,6 +78,7 @@ async fn main() -> color_eyre::eyre::Result<()> { | |||
77 | 78 | ||
78 | pub struct AppState { | 79 | pub struct AppState { |
79 | db: PgPool, | 80 | db: PgPool, |
81 | config: Config, | ||
80 | ping_send: Sender<BroadcastCommands>, | 82 | ping_send: Sender<BroadcastCommands>, |
81 | ping_map: StatusMap, | 83 | ping_map: StatusMap, |
82 | } | 84 | } |
diff --git a/src/routes/device.rs b/src/routes/device.rs index b80cb85..c85df1b 100644 --- a/src/routes/device.rs +++ b/src/routes/device.rs | |||
@@ -12,7 +12,7 @@ use crate::error::Error; | |||
12 | pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<GetDevicePayload>) -> Result<Json<Value>, Error> { | 12 | pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<GetDevicePayload>) -> Result<Json<Value>, Error> { |
13 | info!("add device {}", payload.id); | 13 | info!("add device {}", payload.id); |
14 | let secret = headers.get("authorization"); | 14 | let secret = headers.get("authorization"); |
15 | if auth(secret).map_err(Error::Auth)? { | 15 | if auth(&state.config, secret).map_err(Error::Auth)? { |
16 | let device = sqlx::query_as!( | 16 | let device = sqlx::query_as!( |
17 | Device, | 17 | Device, |
18 | r#" | 18 | r#" |
@@ -39,7 +39,7 @@ pub struct GetDevicePayload { | |||
39 | pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PutDevicePayload>) -> Result<Json<Value>, Error> { | 39 | pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PutDevicePayload>) -> Result<Json<Value>, Error> { |
40 | info!("add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); | 40 | info!("add device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); |
41 | let secret = headers.get("authorization"); | 41 | let secret = headers.get("authorization"); |
42 | if auth(secret).map_err(Error::Auth)? { | 42 | if auth(&state.config, secret).map_err(Error::Auth)? { |
43 | sqlx::query!( | 43 | sqlx::query!( |
44 | r#" | 44 | r#" |
45 | INSERT INTO devices (id, mac, broadcast_addr, ip) | 45 | INSERT INTO devices (id, mac, broadcast_addr, ip) |
@@ -73,7 +73,7 @@ pub struct PutDeviceResponse { | |||
73 | pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PostDevicePayload>) -> Result<Json<Value>, Error> { | 73 | pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PostDevicePayload>) -> Result<Json<Value>, Error> { |
74 | info!("edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); | 74 | info!("edit device {} ({}, {}, {})", payload.id, payload.mac, payload.broadcast_addr, payload.ip); |
75 | let secret = headers.get("authorization"); | 75 | let secret = headers.get("authorization"); |
76 | if auth(secret).map_err(Error::Auth)? { | 76 | if auth(&state.config, secret).map_err(Error::Auth)? { |
77 | let device = sqlx::query_as!( | 77 | let device = sqlx::query_as!( |
78 | Device, | 78 | Device, |
79 | r#" | 79 | r#" |
diff --git a/src/routes/start.rs b/src/routes/start.rs index 4264588..ce95bf3 100644 --- a/src/routes/start.rs +++ b/src/routes/start.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use crate::auth::auth; | 1 | use crate::auth::auth; |
2 | use crate::config::SETTINGS; | ||
3 | use crate::db::Device; | 2 | use crate::db::Device; |
4 | use crate::error::Error; | 3 | use crate::error::Error; |
5 | use crate::services::ping::Value as PingValue; | 4 | use crate::services::ping::Value as PingValue; |
@@ -21,7 +20,7 @@ pub async fn start( | |||
21 | ) -> Result<Json<Value>, Error> { | 20 | ) -> Result<Json<Value>, Error> { |
22 | info!("POST request"); | 21 | info!("POST request"); |
23 | let secret = headers.get("authorization"); | 22 | let secret = headers.get("authorization"); |
24 | let authorized = auth(secret).map_err(Error::Auth)?; | 23 | let authorized = auth(&state.config, secret).map_err(Error::Auth)?; |
25 | if authorized { | 24 | if authorized { |
26 | let device = sqlx::query_as!( | 25 | let device = sqlx::query_as!( |
27 | Device, | 26 | Device, |
@@ -38,9 +37,7 @@ pub async fn start( | |||
38 | 37 | ||
39 | info!("starting {}", device.id); | 38 | info!("starting {}", device.id); |
40 | 39 | ||
41 | let bind_addr = SETTINGS | 40 | let bind_addr = "0.0.0.0:0"; |
42 | .get_string("bindaddr") | ||
43 | .unwrap_or("0.0.0.0:1111".to_string()); | ||
44 | 41 | ||
45 | let _ = send_packet( | 42 | let _ = send_packet( |
46 | &bind_addr.parse().map_err(Error::IpParse)?, | 43 | &bind_addr.parse().map_err(Error::IpParse)?, |
@@ -75,6 +72,7 @@ pub async fn start( | |||
75 | 72 | ||
76 | crate::services::ping::spawn( | 73 | crate::services::ping::spawn( |
77 | state.ping_send.clone(), | 74 | state.ping_send.clone(), |
75 | &state.config, | ||
78 | device, | 76 | device, |
79 | uuid_gen.clone(), | 77 | uuid_gen.clone(), |
80 | &state.ping_map, | 78 | &state.ping_map, |
diff --git a/src/services/ping.rs b/src/services/ping.rs index 7d71218..9b164c8 100644 --- a/src/services/ping.rs +++ b/src/services/ping.rs | |||
@@ -10,7 +10,7 @@ use time::{Duration, Instant}; | |||
10 | use tokio::sync::broadcast::Sender; | 10 | use tokio::sync::broadcast::Sender; |
11 | use tracing::{debug, error, trace}; | 11 | use tracing::{debug, error, trace}; |
12 | use crate::AppState; | 12 | use crate::AppState; |
13 | use crate::config::SETTINGS; | 13 | use crate::config::Config; |
14 | use crate::db::Device; | 14 | use crate::db::Device; |
15 | 15 | ||
16 | pub type StatusMap = DashMap<String, Value>; | 16 | pub type StatusMap = DashMap<String, Value>; |
@@ -21,7 +21,7 @@ pub struct Value { | |||
21 | pub online: bool | 21 | pub online: bool |
22 | } | 22 | } |
23 | 23 | ||
24 | pub async fn spawn(tx: Sender<BroadcastCommands>, device: Device, uuid: String, ping_map: &StatusMap, db: &PgPool) { | 24 | pub async fn spawn(tx: Sender<BroadcastCommands>, config: &Config, device: Device, uuid: String, ping_map: &StatusMap, db: &PgPool) { |
25 | let timer = Instant::now(); | 25 | let timer = Instant::now(); |
26 | let payload = [0; 8]; | 26 | let payload = [0; 8]; |
27 | 27 | ||
@@ -40,7 +40,7 @@ pub async fn spawn(tx: Sender<BroadcastCommands>, device: Device, uuid: String, | |||
40 | error!("{}", ping.to_string()); | 40 | error!("{}", ping.to_string()); |
41 | msg = Some(BroadcastCommands::Error(uuid.clone())); | 41 | msg = Some(BroadcastCommands::Error(uuid.clone())); |
42 | } | 42 | } |
43 | if timer.elapsed() >= Duration::minutes(SETTINGS.get_int("pingtimeout").unwrap_or(10)) { | 43 | if timer.elapsed() >= Duration::minutes(config.pingtimeout) { |
44 | msg = Some(BroadcastCommands::Timeout(uuid.clone())); | 44 | msg = Some(BroadcastCommands::Timeout(uuid.clone())); |
45 | } | 45 | } |
46 | } else { | 46 | } else { |