diff --git a/backend/api_server/src/blog/cron/check_update.rs b/backend/api_server/src/blog/cron/check_update.rs index f2fe6a9..5f016d6 100644 --- a/backend/api_server/src/blog/cron/check_update.rs +++ b/backend/api_server/src/blog/cron/check_update.rs @@ -1,6 +1,7 @@ -use dirs::home_dir; use std::env; +use dirs::home_dir; + pub fn task() { // get dir let git_work_dir_path = &env::var("GIT_WORK_DIR").unwrap_or_else(|e| { diff --git a/backend/api_server/src/init.rs b/backend/api_server/src/init.rs deleted file mode 100644 index c240d89..0000000 --- a/backend/api_server/src/init.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::logger; -use std::{env, panic}; - -use tracing_appender::non_blocking::WorkerGuard; - -pub fn logger_init() -> (WorkerGuard, WorkerGuard) { - logger::init() -} - -pub fn load_env() { - if let Some(env_file) = env::args().nth(1) { - match dotenv::from_filename(&env_file) { - Ok(_) => { - println!("[info] env loaded from {}, starting up", env_file); - } - Err(e) => { - panic!("[fatal] failed to read {}: {}", env_file, e); - } - } - } else { - println!("[info] no .env file referred, starting up") - } -} diff --git a/backend/api_server/src/init/database.rs b/backend/api_server/src/init/database.rs new file mode 100644 index 0000000..e403269 --- /dev/null +++ b/backend/api_server/src/init/database.rs @@ -0,0 +1,38 @@ +use std::env; +use std::error::Error; +use std::str::FromStr; + +use constants::config::database::{default_value, var_name}; +use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; +use sqlx::{ConnectOptions, Pool, Postgres}; +use tracing::info; + +pub async fn init() -> Result, Box> { + let db_connect_option = PgConnectOptions::from_str(&parse_db_uri()) + .unwrap() + .disable_statement_logging(); + Ok(PgPoolOptions::new().connect_with(db_connect_option).await?) +} + +fn parse_db_uri() -> String { + env::var(var_name::URI).unwrap_or_else(|_| { + info!( + "`{}` env var is not set, \ + using `{}`, `{}`, `{}`, `{}` and `{}`", + var_name::URI, + var_name::USERNAME, + var_name::PASSWORD, + var_name::HOST, + var_name::PORT, + var_name::DBNAME + ); + let postgres_user = env::var(var_name::USERNAME).unwrap_or(default_value::USERNAME.into()); + let postgres_password = + env::var(var_name::PASSWORD).unwrap_or(default_value::PASSWORD.into()); + let db_host = env::var(var_name::HOST).unwrap_or(default_value::HOST.into()); + let db_port = env::var(var_name::PORT).unwrap_or(default_value::PORT.into()); + let postgres_db = env::var(var_name::DBNAME).unwrap_or(default_value::DBNAME.into()); + let db_type = default_value::DATABASE_TYPE; + format!("{db_type}://{postgres_user}:{postgres_password}@{db_host}:{db_port}/{postgres_db}") + }) +} diff --git a/backend/api_server/src/logger.rs b/backend/api_server/src/init/logger.rs similarity index 63% rename from backend/api_server/src/logger.rs rename to backend/api_server/src/init/logger.rs index 7dc26e2..5fd56b4 100644 --- a/backend/api_server/src/logger.rs +++ b/backend/api_server/src/init/logger.rs @@ -2,6 +2,7 @@ use std::path::Path; use std::{env, io}; use chrono::Local; +use constants::config::general::{default_value, var_name}; use dirs::home_dir; use tracing::info; use tracing_appender::{non_blocking, non_blocking::WorkerGuard, rolling}; @@ -25,33 +26,26 @@ impl FormatTime for LocalTimer { type _LogWriter = dyn for<'writer> MakeWriter<'writer, Writer = dyn io::Write + 'static> + 'static; pub fn init() -> (WorkerGuard, WorkerGuard) { - let log_path = &env::var("LOG_ROOT_DIR").unwrap_or_else(|e| { - if let Ok(default_log_path) = env::var("WORK_DIR") { - if default_log_path.starts_with('~') { - let home_path = home_dir() - .expect("[fatal] unable to locate home dir with parsing env `WORK_DIR`, try to use absolute path instead"); - home_path - .join(default_log_path.strip_prefix("~/").unwrap()) - .into_os_string() - .into_string() - .expect("[fatal] failed to parse env `WORK_DIR`, try to use absolute path instead") + let log_path = match env::var(var_name::LOG_DIR) { + Ok(path) => Path::new(&path).to_path_buf(), + Err(_) => { + let path = if let Ok(default_log_path) = env::var(var_name::WORK_DIR) { + expand_home_path(default_log_path) } else { - default_log_path - } - } else { - panic!("[fatal] failed to parse env `LOG_ROOT_DIR`: {}", e); + let fallback_path = expand_home_path(default_value::WORK_DIR.to_string()); + println!( + "[warn] unset env var `{}` and `{}`, trying to log on fallback path: {}/log", + var_name::LOG_DIR, + var_name::WORK_DIR, + fallback_path + ); + fallback_path + }; + Path::new(&path).join("log") } - }); - - let log_path = Path::new(log_path) - .join("log") - .join("backend") - .into_os_string() - .into_string() - .unwrap_or_else(|e| panic!("[fatal] failed to parse {:?}", e)); + }; let (info_log_writer, info_guard) = non_blocking(rolling::never(&log_path, "info.log")); - let (err_log_writer, error_guard) = non_blocking(rolling::never(&log_path, "error.log")); let info_format = tracing_subscriber::fmt::format() @@ -70,9 +64,6 @@ pub fn init() -> (WorkerGuard, WorkerGuard) { .with_source_location(true) .with_timer(LocalTimer); - // let default_filter = tracing_subscriber::filter::EnvFilter::try_from_default_env() - // .unwrap_or_else(|_| "tower_http=debug,axum::rejection=trace".into()); - let subscribers = Registry::default() .with( fmt::Layer::default() @@ -95,8 +86,21 @@ pub fn init() -> (WorkerGuard, WorkerGuard) { tracing::subscriber::set_global_default(subscribers) .expect("[fatal] failed to setup logging system"); - info!("module started: logger"); - info!("logging to dir: {}", log_path); + info!("logging to dir: {}", log_path.to_str().unwrap()); (info_guard, error_guard) } + +fn expand_home_path(path: String) -> String { + if path.starts_with('~') { + let home_path = home_dir() + .unwrap_or_else(|| panic!("[fatal] unable to locate home dir when parsing env `{}`, try to use absolute path instead", var_name::WORK_DIR)); + home_path + .join(path.strip_prefix("~/").unwrap()) + .into_os_string() + .into_string() + .unwrap() + } else { + path + } +} diff --git a/backend/api_server/src/init/mod.rs b/backend/api_server/src/init/mod.rs new file mode 100644 index 0000000..e65de59 --- /dev/null +++ b/backend/api_server/src/init/mod.rs @@ -0,0 +1,45 @@ +mod database; +pub mod logger; +pub mod server; + +use macros::panic_with_log; +use std::{env, panic}; +use tracing::{info, Level}; + +pub fn load_env() { + if let Some(env_file) = env::args().nth(1) { + match dotenv::from_filename(&env_file) { + Ok(_) => { + println!("[info] env loaded from {}, starting up", env_file); + } + Err(e) => { + panic!("[fatal] failed to read {}: {}", env_file, e); + } + } + } else { + println!("[info] no .env file referred, starting up") + } +} + +pub async fn start() { + let db_pool = match database::init().await { + Ok(pool) => { + info!("database connection pool initialized"); + pool + } + Err(e) => { + panic_with_log!( + Level::ERROR, + "database connection pool failed to initialize: {}", + e + ); + } + }; + + match server::start(db_pool).await { + Ok(_) => {} + Err(e) => { + panic_with_log!(Level::ERROR, "server panicked: {}", e) + } + } +} diff --git a/backend/api_server/src/init/server.rs b/backend/api_server/src/init/server.rs new file mode 100644 index 0000000..703a043 --- /dev/null +++ b/backend/api_server/src/init/server.rs @@ -0,0 +1,52 @@ +use std::env; +use std::error::Error; +use std::num::ParseIntError; +use std::process::exit; + +use crate::blog; +use axum::Router; +use constants::config::server::{default_value, var_name}; +use macros::panic_with_log; +use sqlx::{Pool, Postgres}; +use tracing::{error, info, warn, Level}; + +pub async fn start(db_pool: Pool) -> Result<(), Box> { + let app = Router::new() + .nest("/blog", blog::get_router()) + .with_state(db_pool); + + let port: u32 = match env::var(var_name::PORT) { + Ok(value) => value.parse().unwrap_or_else(|e: ParseIntError| { + warn!( + "failed to parse `{}`, using default port: {}", + var_name::PORT, + e.to_string() + ); + default_value::PORT + }), + Err(_) => default_value::PORT, + }; + + let listener = + match tokio::net::TcpListener::bind(format!("{}:{}", default_value::HOST, port)).await { + Ok(listener) => { + info!("server started on {}:{}", default_value::HOST, port); + listener + } + Err(e) => { + panic_with_log!( + Level::ERROR, + "failed to start server on {}:{}: {}", + default_value::HOST, + port, + e.to_string() + ); + } + }; + + axum::serve(listener, app).await.unwrap_or_else(|e| { + error!("failed to start axum: {}", e.to_string()); + exit(1); + }); + Ok(()) +} diff --git a/backend/api_server/src/main.rs b/backend/api_server/src/main.rs index c1431c4..5cdd2e5 100644 --- a/backend/api_server/src/main.rs +++ b/backend/api_server/src/main.rs @@ -1,21 +1,12 @@ -use macros::panic_with_log; -use tracing::Level; - mod blog; mod init; -mod logger; mod scheduler; -mod server; #[tokio::main] async fn main() { init::load_env(); - let _guards = init::logger_init(); - match server::start().await { - Ok(_) => {} - Err(e) => { - panic_with_log!(Level::ERROR, "failed to start server: {}", e); - } - }; + let _guards = init::logger::init(); + + init::start().await; } diff --git a/backend/api_server/src/server.rs b/backend/api_server/src/server.rs deleted file mode 100644 index 047eae1..0000000 --- a/backend/api_server/src/server.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::env; -use std::error::Error; -use std::num::ParseIntError; -use std::process::exit; -use std::str::FromStr; - -use crate::blog; -use axum::Router; -use macros::panic_with_log; -use sqlx::{ - postgres::{PgConnectOptions, PgPoolOptions}, - ConnectOptions, -}; -use tracing::{error, info, warn, Level}; - -pub async fn start() -> Result<(), Box> { - let port: u32 = match env::var("BACKEND_PORT") { - Ok(value) => value.parse().unwrap_or_else(|e: ParseIntError| { - warn!("failed to parse BACKEND_PORT: {}", e.to_string()); - 3001 - }), - Err(_) => 3001, - }; - - let db_connect_option = PgConnectOptions::from_str(&parse_db_uri()) - .unwrap() - .disable_statement_logging(); - - let db_pool = match PgPoolOptions::new().connect_with(db_connect_option).await { - Ok(pool) => { - info!("database connected"); - pool - } - Err(e) => { - error!("failed to load database: {}", e.to_string()); - exit(1); - } - }; - - // cron task - // let scheduler = Scheduler::new(); - // let test_str = "hello world".to_string(); - // scheduler.insert( - // BasicTask::new( - // move || blog::cron::check_update::task(test_str.clone()), - // "* * * * * *", - // )? - // .to_task(), - // )?; - - // http server - let app = Router::new() - .nest("/blog", blog::get_router()) - .with_state(db_pool); - - let listener = match tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port)).await { - Ok(listener) => { - info!("server started on 0.0.0.0:{}", port); - listener - } - Err(e) => { - panic_with_log!( - Level::ERROR, - "failed to start server on 0.0.0.0:{}: {}", - port, - e.to_string() - ); - } - }; - - axum::serve(listener, app).await.unwrap_or_else(|e| { - error!("failed to start axum: {}", e.to_string()); - exit(1); - }); - Ok(()) -} - -fn parse_db_uri() -> String { - let postgres_user = env::var("POSTGRES_USER").unwrap_or("".to_string()); - let postgres_password = env::var("POSTGRES_PASSWORD").unwrap_or("".to_string()); - let db_host = env::var("DB_HOST").unwrap_or("localhost".to_string()); - let db_port = env::var("DB_PORT") - .unwrap_or("5432".to_string()) - .to_string(); - let postgres_db = env::var("POSTGRES_DB").unwrap_or("nocturne".to_string()); - - format!("postgresql://{postgres_user}:{postgres_password}@{db_host}:{db_port}/{postgres_db}") -} diff --git a/backend/constants/src/config.rs b/backend/constants/src/config.rs new file mode 100644 index 0000000..4584402 --- /dev/null +++ b/backend/constants/src/config.rs @@ -0,0 +1,43 @@ +// Database related +pub mod database { + pub mod var_name { + pub const URI: &str = "DATABASE_URL"; + pub const USERNAME: &str = "POSTGRES_USER"; + pub const PASSWORD: &str = "POSTGRES_PASSWORD"; + pub const HOST: &str = "POSTGRES_HOST"; + pub const PORT: &str = "POSTGRES_PORT"; + pub const DBNAME: &str = "POSTGRES_DB"; + } + + pub mod default_value { + pub const USERNAME: &str = ""; + pub const PASSWORD: &str = ""; + pub const HOST: &str = "localhost"; + pub const PORT: &str = "5432"; + pub const DBNAME: &str = "nocturne"; + pub const DATABASE_TYPE: &str = "postgresql"; + } +} + +pub mod server { + pub mod var_name { + pub const PORT: &str = "BACKEND_PORT"; + pub const HOST: &str = "BACKEND_HOST"; + } + + pub mod default_value { + pub const PORT: u32 = 3001; + pub const HOST: &str = "0.0.0.0"; + } +} + +pub mod general { + pub mod var_name { + pub const LOG_DIR: &str = "LOG_DIR"; + pub const WORK_DIR: &str = "WORK_DIR"; + } + + pub mod default_value { + pub const WORK_DIR: &str = "~/.nocturne"; + } +} diff --git a/backend/constants/src/lib.rs b/backend/constants/src/lib.rs index f5cd248..1b58b94 100644 --- a/backend/constants/src/lib.rs +++ b/backend/constants/src/lib.rs @@ -1,2 +1,3 @@ pub mod blog; +pub mod config; pub mod time;