1
0
Fork 0
mirror of https://codeberg.org/Mo8it/git-webhook-client synced 2024-10-18 07:22:39 +00:00

Add states

This commit is contained in:
Mo 2022-10-12 15:40:25 +02:00
parent 322fa93c65
commit 8347d0a391
9 changed files with 91 additions and 1765 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/Cargo.lock
/config.json /config.json
/db/ /db/
/target/ /target/

1700
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,7 @@ license-file = "LICENSE"
[dependencies] [dependencies]
cached = "0.39.0" cached = "0.39.0"
diesel = { version = "2.0.2", features = [ diesel = { version = "2.0.2", features = [
"r2d2",
"sqlite", "sqlite",
"returning_clauses_for_sqlite_3_35", "returning_clauses_for_sqlite_3_35",
"without-deprecated", "without-deprecated",

View file

@ -1,10 +1,9 @@
use cached::proc_macro::once;
use serde::Deserialize; use serde::Deserialize;
use std::fs::File; use std::fs::File;
use std::io::BufReader; use std::io::BufReader;
use std::path::Path; use std::path::Path;
#[derive(Deserialize, Clone)] #[derive(Deserialize)]
pub struct Hook { pub struct Hook {
pub repo_url: String, pub repo_url: String,
pub current_dir: String, pub current_dir: String,
@ -12,46 +11,20 @@ pub struct Hook {
pub args: Vec<String>, pub args: Vec<String>,
} }
#[derive(Deserialize, Clone)] #[derive(Deserialize)]
struct Config { pub struct Config {
secret: String, pub secret: String,
base_url: String, pub base_url: String,
hooks: Vec<Hook>, pub hooks: Vec<Hook>,
} }
#[once] impl Config {
fn get_config() -> Config { pub fn new() -> Self {
let config_path = Path::new("config.json"); let config_path = Path::new("config.json");
let config_file = File::open(config_path).unwrap(); let config_file = File::open(config_path).unwrap();
let config_reader = BufReader::new(config_file); let config_reader = BufReader::new(config_file);
let config: Config = serde_json::from_reader(config_reader).unwrap(); let config: Self = serde_json::from_reader(config_reader).unwrap();
config config
} }
#[once]
pub fn get_secret() -> Vec<u8> {
let config = get_config();
config.secret.as_bytes().to_owned()
}
#[once]
pub fn get_hooks() -> Vec<Hook> {
let config = get_config();
config.hooks
}
#[once]
pub fn get_base_url() -> String {
let config = get_config();
config.base_url
}
pub fn get_hook(clone_url: &str) -> Option<Hook> {
let hooks = get_hooks();
hooks.into_iter().find(|hook| hook.repo_url == clone_url)
} }

View file

@ -1,17 +1,26 @@
use diesel::prelude::*; use diesel::prelude::*;
use diesel::r2d2::{ConnectionManager, Pool};
use std::env;
use std::process::Output; use std::process::Output;
use crate::config::Hook; use crate::config::Hook;
use crate::models::{HookLog, NewHookLog}; use crate::models::{HookLog, NewHookLog};
use crate::schema::hooklog; use crate::schema::hooklog;
fn establish_connection() -> SqliteConnection { pub type DBPool = Pool<ConnectionManager<SqliteConnection>>;
let database_url = "db/db.sqlite";
SqliteConnection::establish(database_url).unwrap() pub fn establish_connection_pool() -> DBPool {
let database_url =
env::var("DATABASE_URL").expect("Environment variable DATABASE_URL missing!");
let manager = ConnectionManager::<SqliteConnection>::new(&database_url);
Pool::builder()
.build(manager)
.expect("Could not build database connection pool!")
} }
pub fn add_hook_log(hook: &Hook, output: &Output) -> i32 { pub fn add_hook_log(pool: &DBPool, hook: &Hook, output: &Output) -> i32 {
let connection = &mut establish_connection(); let conn = &mut pool.get().unwrap();
let command_with_args = hook.command.to_owned() + " " + &hook.args.join(" "); let command_with_args = hook.command.to_owned() + " " + &hook.args.join(" ");
@ -26,16 +35,15 @@ pub fn add_hook_log(hook: &Hook, output: &Output) -> i32 {
let result: HookLog = diesel::insert_into(hooklog::table) let result: HookLog = diesel::insert_into(hooklog::table)
.values(&new_hook_log) .values(&new_hook_log)
.get_result(connection) .get_result(conn)
.expect("Error saving hook log!"); .expect("Error saving hook log!");
result.id result.id
} }
pub fn get_hook_log(id: i32) -> HookLog { pub fn get_hook_log(pool: &DBPool, id: i32) -> HookLog {
let conn = &mut pool.get().unwrap();
use hooklog::dsl::hooklog; use hooklog::dsl::hooklog;
let connection = &mut establish_connection(); hooklog.find(id).first(conn).unwrap()
hooklog.find(id).first(connection).unwrap()
} }

View file

@ -5,7 +5,7 @@ use rocket::request::{self, Request};
use serde_json::Value; use serde_json::Value;
use sha2::Sha256; use sha2::Sha256;
use crate::config; use crate::states;
pub struct Repo<'r> { pub struct Repo<'r> {
pub clone_url: &'r str, pub clone_url: &'r str,
@ -43,7 +43,9 @@ impl<'r> FromData<'r> for Repo<'r> {
return Outcome::Failure((Status::BadRequest, Self::Error::MoreThatOneSignature)); return Outcome::Failure((Status::BadRequest, Self::Error::MoreThatOneSignature));
} }
if !is_valid_signature(&received_signature, &payload) { let config_state = req.rocket().state::<states::Config>().unwrap();
if !is_valid_signature(&config_state.secret, &received_signature, &payload) {
return Outcome::Failure((Status::BadRequest, Self::Error::InvalidSignature)); return Outcome::Failure((Status::BadRequest, Self::Error::InvalidSignature));
} }
@ -57,8 +59,8 @@ impl<'r> FromData<'r> for Repo<'r> {
} }
} }
fn is_valid_signature(received_signature: &[u8], payload: &[u8]) -> bool { fn is_valid_signature(secret: &[u8], received_signature: &[u8], payload: &[u8]) -> bool {
let mut mac = Hmac::<Sha256>::new_from_slice(&config::get_secret()).unwrap(); let mut mac = Hmac::<Sha256>::new_from_slice(secret).unwrap();
mac.update(payload); mac.update(payload);
let expected_signature = mac.finalize().into_bytes(); let expected_signature = mac.finalize().into_bytes();

View file

@ -4,10 +4,13 @@ mod guards;
mod models; mod models;
mod routes; mod routes;
mod schema; mod schema;
mod states;
#[rocket::launch] #[rocket::launch]
fn rocket() -> _ { fn rocket() -> _ {
rocket::build() rocket::build()
.mount("/", rocket::routes![routes::index]) .mount("/", rocket::routes![routes::index])
.mount("/api", rocket::routes![routes::trigger]) .mount("/api", rocket::routes![routes::trigger])
.manage(states::DB::new())
.manage(states::Config::new())
} }

View file

@ -1,13 +1,13 @@
use rocket::{get, post}; use rocket::{get, post, State};
use std::process::Command; use std::process::Command;
use crate::config;
use crate::db; use crate::db;
use crate::guards; use crate::guards;
use crate::states;
#[get("/<id>")] #[get("/<id>")]
pub fn index(id: i32) -> String { pub fn index(db_state: &State<states::DB>, id: i32) -> String {
let hook_log = db::get_hook_log(id); let hook_log = db::get_hook_log(&db_state.pool, id);
format!( format!(
"Hook log id: "Hook log id:
{} {}
@ -44,8 +44,12 @@ Status code:
} }
#[post("/trigger", format = "json", data = "<repo>")] #[post("/trigger", format = "json", data = "<repo>")]
pub fn trigger(repo: guards::Repo) -> String { pub fn trigger(
let hook = config::get_hook(repo.clone_url).unwrap(); db_state: &State<states::DB>,
config_state: &State<states::Config>,
repo: guards::Repo,
) -> String {
let hook = config_state.get_hook(repo.clone_url).unwrap();
let output = Command::new(&hook.command) let output = Command::new(&hook.command)
.args(&hook.args) .args(&hook.args)
@ -53,9 +57,7 @@ pub fn trigger(repo: guards::Repo) -> String {
.output() .output()
.unwrap(); .unwrap();
let new_hook_log_id = db::add_hook_log(&hook, &output); let new_hook_log_id = db::add_hook_log(&db_state.pool, &hook, &output);
let base_url = config::get_base_url(); format!("{}/{new_hook_log_id}", config_state.base_url_with_https)
format!("https://{base_url}/{new_hook_log_id}")
} }

36
src/states.rs Normal file
View file

@ -0,0 +1,36 @@
use crate::config;
use crate::db;
pub struct DB {
pub pool: db::DBPool,
}
impl DB {
pub fn new() -> Self {
let pool = db::establish_connection_pool();
Self { pool }
}
}
pub struct Config {
pub secret: Vec<u8>,
pub base_url_with_https: String,
pub hooks: Vec<config::Hook>,
}
impl Config {
pub fn new() -> Self {
let config = config::Config::new();
Self {
secret: config.secret.as_bytes().to_owned(),
base_url_with_https: format!("https://{}", config.base_url),
hooks: config.hooks,
}
}
pub fn get_hook(&self, clone_url: &str) -> Option<&config::Hook> {
self.hooks.iter().find(|&hook| hook.repo_url == clone_url)
}
}