summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFxQnLr <[email protected]>2024-02-12 15:05:03 +0100
committerGitHub <[email protected]>2024-02-12 15:05:03 +0100
commitca395b81bcadb9dde9214098f2cab0c7a3561574 (patch)
tree055bae9ef4e65f53b826b240f0b8e3b15b73693e
parente4832b4cf36ba0eaed298ee458498eddd7176590 (diff)
parente50a868f69b602cc0bc84d51e9940878924f49ef (diff)
downloadwebol-ca395b81bcadb9dde9214098f2cab0c7a3561574.tar
webol-ca395b81bcadb9dde9214098f2cab0c7a3561574.tar.gz
webol-ca395b81bcadb9dde9214098f2cab0c7a3561574.zip
Merge pull request #13 from FxQnLr/config
Config
-rw-r--r--.github/workflows/pull_request.yml63
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock5
-rw-r--r--Cargo.toml1
-rw-r--r--README.md4
-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
12 files changed, 79 insertions, 90 deletions
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index 279e16d..732e9e9 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -1,35 +1,32 @@
1name: check 1name: check
2 2
3on: 3on: [ pull_request, workflow_dispatch ]
4 pull_request: 4
5 branches: [ "main" ] 5nv:
6 workflow_dispatch: 6 SCCACHE_GHA_ENABLED: "true"
7 7 RUSTC_WRAPPER: "sccache"
8env: 8 SQLX_OFFLINE: "true"
9 SCCACHE_GHA_ENABLED: "true" 9 CARGO_TERM_COLOR: always
10 RUSTC_WRAPPER: "sccache" 10
11 SQLX_OFFLINE: "true" 11obs:
12 CARGO_TERM_COLOR: always 12 check:
13 13 runs-on: ubuntu-latest
14jobs: 14 steps:
15 check: 15 - name: Run sccache-cache
16 runs-on: ubuntu-latest 16 uses: mozilla-actions/[email protected]
17 steps: 17
18 - name: Run sccache-cache 18 - uses: actions/checkout@v4
19 uses: mozilla-actions/[email protected] 19
20 20 - run: cargo check
21 - uses: actions/checkout@v4 21 - run: cargo clippy
22 22
23 - run: cargo check 23 check-release:
24 - run: cargo clippy 24 runs-on: ubuntu-latest
25 25 steps:
26 check-release: 26 - name: Run sccache-cache
27 runs-on: ubuntu-latest 27 uses: mozilla-actions/[email protected]
28 steps: 28
29 - name: Run sccache-cache 29 - uses: actions/checkout@v4
30 uses: mozilla-actions/[email protected] 30
31 31 - run: cargo check --release
32 - uses: actions/checkout@v4 32 - run: cargo clippy --release
33
34 - run: cargo check --release
35 - run: cargo clippy --release \ No newline at end of file
diff --git a/.gitignore b/.gitignore
index bf0eacb..0fd9734 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,6 @@ target/
11 11
12*.sqlite* 12*.sqlite*
13.env 13.env
14config.*
14 15
15.idea 16.idea
diff --git a/Cargo.lock b/Cargo.lock
index bf75375..f95052b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -818,9 +818,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
818 818
819[[package]] 819[[package]]
820name = "indexmap" 820name = "indexmap"
821version = "2.2.2" 821version = "2.2.3"
822source = "registry+https://github.com/rust-lang/crates.io-index" 822source = "registry+https://github.com/rust-lang/crates.io-index"
823checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" 823checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
824dependencies = [ 824dependencies = [
825 "equivalent", 825 "equivalent",
826 "hashbrown 0.14.3", 826 "hashbrown 0.14.3",
@@ -2360,7 +2360,6 @@ dependencies = [
2360 "color-eyre", 2360 "color-eyre",
2361 "config", 2361 "config",
2362 "dashmap", 2362 "dashmap",
2363 "once_cell",
2364 "serde", 2363 "serde",
2365 "serde_json", 2364 "serde_json",
2366 "sqlx", 2365 "sqlx",
diff --git a/Cargo.toml b/Cargo.toml
index 14a955c..0fb250a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,6 @@ time = { version = "0.3", features = ["macros"] }
14serde = { version = "1.0", features = ["derive"] } 14serde = { version = "1.0", features = ["derive"] }
15serde_json = "1.0" 15serde_json = "1.0"
16config = "0.14" 16config = "0.14"
17once_cell = "1.18"
18sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"]} 17sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"]}
19surge-ping = "0.8" 18surge-ping = "0.8"
20axum-macros = "0.4" 19axum-macros = "0.4"
diff --git a/README.md b/README.md
index e4a6c44..eabc051 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,4 @@ WEBOL_APIKEY: `String`
6 6
7WEBOL_SERVERADDR: `Option<String>` (0.0.0.0:7229) 7WEBOL_SERVERADDR: `Option<String>` (0.0.0.0:7229)
8 8
9WEBOL_BINDADDR: `Option<String>` (0.0.0.0:1111) 9WEBOL_PINGTIMEOUT: `Option<i64>` (10)
10
11WEBOL_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};
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 {