diff --git a/.gitignore b/.gitignore index 42b3623..0c72fc1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.log /Cargo.lock /config.json /db/ diff --git a/Cargo.toml b/Cargo.toml index 6d24a73..95d1226 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,27 @@ [package] name = "git-webhook-client" -version = "0.1.0" +version = "0.2.0" authors = ["Mo Bitar "] edition = "2021" readme = "README.adoc" -license-file = "LICENSE" +repository = "https://codeberg.org/Mo8it/git-webhook-client" +license-file = "LICENSE.txt" [dependencies] -chrono = "0.4.22" -diesel = { version = "2.0.2", features = [ +anyhow = "1.0" +chrono = "0.4" +diesel = { version = "2.0", features = [ "r2d2", "sqlite", "returning_clauses_for_sqlite_3_35", "without-deprecated", ] } -hex = "0.4.3" -hmac = "0.12.1" +hex = "0.4" +hmac = "0.12" +log = "0.4" rocket = "0.5.0-rc.2" rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] } serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.87" -sha2 = "0.10.6" +serde_json = "1.0" +sha2 = "0.10" +simplelog = "0.12" diff --git a/README.adoc b/README.adoc index a827473..21fe914 100644 --- a/README.adoc +++ b/README.adoc @@ -22,7 +22,7 @@ Currently, only Gitea is supported. If you want support for Gitlab or Github, th === Configuration -The program looks for the configuration file `config.json` that contains the following: +The program looks for the configuration file configured with the environment variable `GWC_CONFIG_FILE` that contains the following: . `secret`: The secret of the webhook. . `base_url`: The base_url of the webhook client. diff --git a/src/config.rs b/src/config.rs index 2de60eb..8088f71 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use serde::Deserialize; +use std::env; use std::fs::File; use std::io::BufReader; -use std::path::Path; #[derive(Deserialize)] pub struct Hook { @@ -15,19 +15,18 @@ pub struct Hook { pub struct Config { pub secret: String, pub base_url: String, + pub log_file: String, pub hooks: Vec, } impl Config { pub fn new() -> Self { - let config_path = Path::new("config.json"); - let config_file = File::open(config_path).unwrap_or_else(|_| { - panic!( - "Can not open the config file at the path {}!", - config_path - .to_str() - .expect("Can not convert the config file path into a string") - ) + let config_file_var = "GWC_CONFIG_FILE"; + let config_path = env::var(config_file_var) + .unwrap_or_else(|_| panic!("Environment variable {config_file_var} missing!")); + + let config_file = File::open(&config_path).unwrap_or_else(|e| { + panic!("Can not open the config file at the path {config_path}: {e}") }); let config_reader = BufReader::new(config_file); let config: Self = diff --git a/src/db.rs b/src/db.rs index 8ef5f1c..99c6bd5 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,6 +1,8 @@ +use anyhow::{Context, Result}; use chrono::Local; use diesel::prelude::*; use diesel::r2d2::{ConnectionManager, Pool, PooledConnection}; +use log::error; use std::env; use crate::config::Hook; @@ -10,8 +12,10 @@ use crate::schema::hooklog; pub type DBPool = Pool>; pub fn establish_connection_pool() -> DBPool { - let database_url = - env::var("DATABASE_URL").expect("Environment variable DATABASE_URL missing!"); + let database_url_var = "DATABASE_URL"; + let database_url = env::var(database_url_var) + .unwrap_or_else(|_| panic!("Environment variable {database_url_var} missing!")); + let manager = ConnectionManager::::new(&database_url); Pool::builder() @@ -19,14 +23,11 @@ pub fn establish_connection_pool() -> DBPool { .expect("Could not build database connection pool!") } -fn get_conn( - pool: &DBPool, -) -> Result>, String> { - pool.get() - .map_err(|_| "Could not get database pool!".to_string()) +fn get_conn(pool: &DBPool) -> Result>> { + pool.get().context("Could not get database pool!") } -pub fn add_hook_log(pool: &DBPool, hook: &Hook) -> Result { +pub fn add_hook_log(pool: &DBPool, hook: &Hook) -> Result { let conn = &mut get_conn(pool)?; let command_with_args = hook.command.to_owned() + " " + &hook.args.join(" "); @@ -41,7 +42,7 @@ pub fn add_hook_log(pool: &DBPool, hook: &Hook) -> Result { diesel::insert_into(hooklog::table) .values(&new_hook_log) .get_result::(conn) - .map_err(|e| e.to_string()) + .context("Could not insert hook log!") } pub fn fill_hook_log( @@ -54,7 +55,7 @@ pub fn fill_hook_log( let conn = &mut match get_conn(pool) { Ok(pool) => pool, Err(e) => { - println!("Error while trying to fill hook log: {e}"); + error!("Could not get a database pool connection to fill hook log with id {hook_log_id}: {e}"); return; } }; @@ -73,12 +74,12 @@ pub fn fill_hook_log( { Ok(_) => (), Err(e) => { - println!("Could not update hook log: {e}"); + error!("Could not update hook log with id {hook_log_id}: {e}"); } }; } -pub fn get_hook_log(pool: &DBPool, id: i32) -> Result { +pub fn get_hook_log(pool: &DBPool, id: i32) -> Result { // id=0 not allowed! let conn = &mut get_conn(pool)?; @@ -92,5 +93,5 @@ pub fn get_hook_log(pool: &DBPool, id: i32) -> Result { .first(conn) }; - hl.map_err(|e| e.to_string()) + hl.context("Could not get hook log!") } diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 0000000..a57a177 --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,29 @@ +use simplelog::{ColorChoice, LevelFilter, TermLogger, TerminalMode, WriteLogger}; +use std::fs::OpenOptions; + +use crate::config; + +pub fn init_logger(config: &config::Config) { + let logger = if cfg!(debug_assertions) { + TermLogger::init( + LevelFilter::Debug, + simplelog::Config::default(), + TerminalMode::Mixed, + ColorChoice::Auto, + ) + } else { + WriteLogger::init( + LevelFilter::Info, + simplelog::Config::default(), + OpenOptions::new() + .create(true) + .append(true) + .open(&config.log_file) + .unwrap_or_else(|e| { + panic!("Could not open the log file {}: {e}", &config.log_file) + }), + ) + }; + + logger.expect("Could not initialize the logger!"); +} diff --git a/src/main.rs b/src/main.rs index 7f34d46..6d4bf77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,27 @@ mod config; mod db; mod guards; +mod logging; mod models; mod routes; mod schema; mod states; +use log::info; use rocket_dyn_templates::Template; #[rocket::launch] fn rocket() -> _ { + let config = config::Config::new(); + + logging::init_logger(&config); + + info!("Starting client"); + rocket::build() .mount("/", rocket::routes![routes::index]) .mount("/api", rocket::routes![routes::trigger]) .manage(states::DB::new()) - .manage(states::Config::new()) + .manage(states::Config::new(config)) .attach(Template::fairing()) } diff --git a/src/states.rs b/src/states.rs index 3762135..cc827fb 100644 --- a/src/states.rs +++ b/src/states.rs @@ -20,9 +20,7 @@ pub struct Config { } impl Config { - pub fn new() -> Self { - let config = config::Config::new(); - + pub fn new(config: config::Config) -> Self { Self { secret: config.secret.as_bytes().to_owned(), base_url: config.base_url,