summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFxQnLr <[email protected]>2024-02-12 14:58:08 +0100
committerFxQnLr <[email protected]>2024-02-12 14:58:08 +0100
commit8ed77d7ab484121e9d70158e14c9fd6c243f1c70 (patch)
treedabecfb3eaec1420782eb9d3987e54ba83612b18 /src
parente4832b4cf36ba0eaed298ee458498eddd7176590 (diff)
downloadwebol-8ed77d7ab484121e9d70158e14c9fd6c243f1c70.tar
webol-8ed77d7ab484121e9d70158e14c9fd6c243f1c70.tar.gz
webol-8ed77d7ab484121e9d70158e14c9fd6c243f1c70.zip
Close #9. Config impl with struct and files
Diffstat (limited to 'src')
-rw-r--r--src/auth.rs13
-rw-r--r--src/config.rs32
-rw-r--r--src/db.rs16
-rw-r--r--src/main.rs14
-rw-r--r--src/routes/device.rs6
-rw-r--r--src/routes/start.rs8
-rw-r--r--src/services/ping.rs6
7 files changed, 45 insertions, 50 deletions
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};
2use axum::http::header::ToStrError; 2use axum::http::header::ToStrError;
3use tracing::{debug, error, trace}; 3use tracing::{debug, error, trace};
4use crate::auth::Error::{MissingSecret, WrongSecret}; 4use crate::auth::Error::{MissingSecret, WrongSecret};
5use crate::config::SETTINGS; 5use crate::config::Config;
6 6
7pub fn auth(secret: Option<&HeaderValue>) -> Result<bool, Error> { 7pub 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> {
28pub enum Error { 26pub 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 @@
1use config::Config; 1use config::File;
2use once_cell::sync::Lazy; 2use serde::Deserialize;
3 3
4pub static SETTINGS: Lazy<Config> = Lazy::new(setup); 4#[derive(Debug, Clone, Deserialize)]
5pub struct Config {
6 pub database_url: String,
7 pub apikey: String,
8 pub serveraddr: String,
9 pub pingtimeout: i64,
10}
11
12impl 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
6fn 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
diff --git a/src/db.rs b/src/db.rs
index 8a6b16e..489a000 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -1,13 +1,7 @@
1#[cfg(debug_assertions)]
2use std::env;
3
4use serde::Serialize; 1use serde::Serialize;
5use sqlx::{PgPool, postgres::PgPoolOptions}; 2use sqlx::{PgPool, postgres::PgPoolOptions};
6use tracing::{debug, info}; 3use tracing::{debug, info};
7 4
8#[cfg(not(debug_assertions))]
9use crate::config::SETTINGS;
10
11#[derive(Serialize, Debug)] 5#[derive(Serialize, Debug)]
12pub struct Device { 6pub 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
20pub async fn init_db_pool() -> PgPool { 14pub 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 @@
1use std::env; 1use std::env;
2use std::net::SocketAddr;
3use std::sync::Arc; 2use std::sync::Arc;
4use axum::{Router, routing::post}; 3use axum::{Router, routing::post};
5use axum::routing::{get, put}; 4use axum::routing::{get, put};
@@ -9,7 +8,7 @@ use time::util::local_offset;
9use tokio::sync::broadcast::{channel, Sender}; 8use tokio::sync::broadcast::{channel, Sender};
10use tracing::{info, level_filters::LevelFilter}; 9use tracing::{info, level_filters::LevelFilter};
11use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*}; 10use tracing_subscriber::{EnvFilter, fmt::{self, time::LocalTime}, prelude::*};
12use crate::config::SETTINGS; 11use crate::config::Config;
13use crate::db::init_db_pool; 12use crate::db::init_db_pool;
14use crate::routes::device; 13use crate::routes::device;
15use crate::routes::start::start; 14use 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
78pub struct AppState { 79pub 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;
12pub async fn get(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<GetDevicePayload>) -> Result<Json<Value>, Error> { 12pub 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 {
39pub async fn put(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PutDevicePayload>) -> Result<Json<Value>, Error> { 39pub 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 {
73pub async fn post(State(state): State<Arc<crate::AppState>>, headers: HeaderMap, Json(payload): Json<PostDevicePayload>) -> Result<Json<Value>, Error> { 73pub 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 @@
1use crate::auth::auth; 1use crate::auth::auth;
2use crate::config::SETTINGS;
3use crate::db::Device; 2use crate::db::Device;
4use crate::error::Error; 3use crate::error::Error;
5use crate::services::ping::Value as PingValue; 4use 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};
10use tokio::sync::broadcast::Sender; 10use tokio::sync::broadcast::Sender;
11use tracing::{debug, error, trace}; 11use tracing::{debug, error, trace};
12use crate::AppState; 12use crate::AppState;
13use crate::config::SETTINGS; 13use crate::config::Config;
14use crate::db::Device; 14use crate::db::Device;
15 15
16pub type StatusMap = DashMap<String, Value>; 16pub type StatusMap = DashMap<String, Value>;
@@ -21,7 +21,7 @@ pub struct Value {
21 pub online: bool 21 pub online: bool
22} 22}
23 23
24pub async fn spawn(tx: Sender<BroadcastCommands>, device: Device, uuid: String, ping_map: &StatusMap, db: &PgPool) { 24pub 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 {