add config options via env vars and file

This commit is contained in:
Schrottkatze 2024-11-13 16:03:10 +01:00
parent 7754599e72
commit 283b702c56
Signed by: schrottkatze
SSH key fingerprint: SHA256:hXb3t1vINBFCiDCmhRABHX5ocdbLiKyCdKI4HK2Rbbc
5 changed files with 95 additions and 28 deletions

View file

@ -28,8 +28,7 @@ async fn auth_middleware(
req: Request, req: Request,
next: Next, next: Next,
) -> Response { ) -> Response {
let admin_tok = headers.get("x-admin-tok"); if state.check_admin_tok(&headers) {
if headers.get("x-admin-tok") == Some(&HeaderValue::from_static(ADMIN_TOK)) {
let res = next.run(req).await; let res = next.run(req).await;
return res; return res;
} }

View file

@ -114,25 +114,21 @@ pub async fn post(
return StatusCode::BAD_REQUEST.into_response(); return StatusCode::BAD_REQUEST.into_response();
} }
if headers.get("x-admin-tok") == Some(&HeaderValue::from_static(ADMIN_TOK)) { let is_admin = state.check_admin_tok(&headers);
sqlx::query!(
r#"insert into messages (chat_id, content, from_admin) values ($1, $2, true);"#, sqlx::query!(
chat.id, r#"insert into messages (chat_id, content, from_admin) values ($1, $2, $3);"#,
body chat.id,
) body,
.execute(state.pool()) is_admin
.await )
.unwrap(); .execute(state.pool())
.await
.unwrap();
if is_admin {
StatusCode::OK.into_response() StatusCode::OK.into_response()
} else { } else {
sqlx::query!(
r#"insert into messages (chat_id, content, from_admin) values ($1, $2, false);"#,
chat.id,
body
)
.execute(state.pool())
.await
.unwrap();
Redirect::to(&format!("/{url_path}")).into_response() Redirect::to(&format!("/{url_path}")).into_response()
} }
} }

View file

@ -1,5 +1,8 @@
use std::sync::Arc;
use axum::response::IntoResponse; use axum::response::IntoResponse;
use http::StatusCode; use config::Config;
use http::{HeaderMap, HeaderValue, StatusCode};
use sqlx::{Pool, Postgres}; use sqlx::{Pool, Postgres};
use thiserror::Error; use thiserror::Error;
@ -9,23 +12,85 @@ type Result<T> = std::result::Result<T, AppStateError>;
const DB_URL: &str = "postgres://chatdings@localhost/chatdings"; const DB_URL: &str = "postgres://chatdings@localhost/chatdings";
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AppState { pub struct AppState(Arc<AppStateInner>);
#[derive(Debug)]
pub struct AppStateInner {
pool: Pool<Postgres>, pool: Pool<Postgres>,
config: Config,
}
mod config {
use std::{env, fs};
#[derive(Debug)]
pub(super) struct Config {
db_url: String,
admin_token: String,
}
impl Config {
pub fn read_from_env() -> Self {
let db_url = env::var("CHATDINGS_DB_URL")
.expect("environment variable CHATDINGS_DB_URL has to exist");
let admin_token_path = env::var("CHATDINGS_ADMIN_TOKEN_PATH")
.expect("environment variable CHATDINGS_ADMIN_TOKEN has to exist");
let admin_token = fs::read_to_string(&admin_token_path)
.expect(&format!("failed to read '{admin_token_path:?}'"))
.trim()
.to_string();
Self {
db_url,
admin_token,
}
}
pub fn db_url(&self) -> &str {
&self.db_url
}
pub fn admin_tok(&self) -> &str {
&self.admin_token
}
}
} }
impl AppState { impl AppState {
pub async fn init() -> Result<Self> { pub async fn init() -> Result<Self> {
let pool = Pool::<Postgres>::connect(DB_URL).await?; Ok(Self(Arc::new(AppStateInner::init().await?)))
}
pub async fn fetch_chat_by_url_path(&self, url_path: &str) -> Result<Chat> {
self.0.fetch_chat_by_url_path(url_path).await
}
pub async fn fetch_messages(&self, chat: &Chat) -> Result<Vec<Message>> {
self.0.fetch_messages(chat).await
}
pub async fn send_message(&self, chat: &Chat, content: String, from_admin: bool) -> Result<()> {
self.0.send_message(chat, content, from_admin).await
}
pub fn check_admin_tok(&self, headers: &HeaderMap) -> bool {
self.0.check_admin_tok(headers)
}
pub fn pool(&self) -> &Pool<Postgres> {
self.0.pool()
}
}
impl AppStateInner {
async fn init() -> Result<Self> {
let config = Config::read_from_env();
let pool = Pool::<Postgres>::connect(config.db_url()).await?;
sqlx::migrate!() sqlx::migrate!()
.run(&pool) .run(&pool)
.await .await
.expect("migration should not fail"); .expect("migration should not fail");
Ok(Self { pool }) Ok(Self { pool, config })
} }
pub async fn fetch_chat_by_url_path(&self, url_path: &str) -> Result<Chat> { async fn fetch_chat_by_url_path(&self, url_path: &str) -> Result<Chat> {
Ok( Ok(
sqlx::query_as!(Chat, r#"select * from chats where url_path = $1"#, url_path) sqlx::query_as!(Chat, r#"select * from chats where url_path = $1"#, url_path)
.fetch_one(&self.pool) .fetch_one(&self.pool)
@ -33,7 +98,7 @@ impl AppState {
) )
} }
pub async fn fetch_messages(&self, chat: &Chat) -> Result<Vec<Message>> { async fn fetch_messages(&self, chat: &Chat) -> Result<Vec<Message>> {
Ok(sqlx::query_as!( Ok(sqlx::query_as!(
Message, Message,
r#"select * from messages where chat_id = $1"#, r#"select * from messages where chat_id = $1"#,
@ -43,11 +108,15 @@ impl AppState {
.await?) .await?)
} }
pub async fn send_message(&self, chat: &Chat, content: String, from_admin: bool) -> Result<()> { async fn send_message(&self, chat: &Chat, content: String, from_admin: bool) -> Result<()> {
todo!() todo!()
} }
pub fn pool(&self) -> &Pool<Postgres> { fn check_admin_tok(&self, headers: &HeaderMap) -> bool {
headers.get("x-admin-tok") == Some(&HeaderValue::from_str(self.config.admin_tok()).unwrap())
}
fn pool(&self) -> &Pool<Postgres> {
&self.pool &self.pool
} }
} }

View file

@ -59,8 +59,10 @@
listen_addresses = "127.0.0.1"; listen_addresses = "127.0.0.1";
}; };
env = { env = rec {
DATABASE_URL = "postgres://localhost/chatdings"; DATABASE_URL = "postgres://localhost/chatdings";
CHATDINGS_DB_URL = DATABASE_URL;
CHATDINGS_ADMIN_TOKEN_PATH = "./test_admin_tok.txt";
}; };
}) })
]; ];

1
test_admin_tok.txt Normal file
View file

@ -0,0 +1 @@
miauu