mirror of
https://codeberg.org/Mo8it/git-webhook-client
synced 2024-12-03 12:30:31 +00:00
Added logs in a database
This commit is contained in:
parent
b33f8e5254
commit
bcd55f33c0
11 changed files with 298 additions and 81 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,3 +1,3 @@
|
|||
/target
|
||||
|
||||
/config.json
|
||||
/db/
|
||||
/target/
|
||||
|
|
89
Cargo.lock
generated
89
Cargo.lock
generated
|
@ -308,6 +308,28 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68c186a7418a2aac330bb76cde82f16c36b03a66fb91db32d20214311f9f6545"
|
||||
dependencies = [
|
||||
"diesel_derives",
|
||||
"libsqlite3-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel_derives"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "143b758c91dbc3fe1fdcb0dba5bd13276c6a66422f2ef5795b58488248a310aa"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.5"
|
||||
|
@ -477,6 +499,7 @@ name = "git-webhook-client"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cached",
|
||||
"diesel",
|
||||
"hex",
|
||||
"hmac",
|
||||
"rocket",
|
||||
|
@ -662,9 +685,19 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.134"
|
||||
version = "0.2.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f0455f2c1bc9a7caa792907026e469c1d91761fb0ea37cbb16427c77280cf35"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
|
@ -864,6 +897,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.0"
|
||||
|
@ -882,6 +921,30 @@ version = "0.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.46"
|
||||
|
@ -954,18 +1017,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ref-cast"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed13bcd201494ab44900a96490291651d200730904221832b9547d24a87d332b"
|
||||
checksum = "b8ebf632f3e32bf35133f620cf481f29c99ae0fb01450fd3d85eee0225274ec1"
|
||||
dependencies = [
|
||||
"ref-cast-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ref-cast-impl"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5234cd6063258a5e32903b53b1b6ac043a0541c8adc1f610f67b0326c7a578fa"
|
||||
checksum = "caab98faa75ce294d40512ce514a46b15eafe78d72c9397a68ea45b3a88201b6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1132,9 +1195,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.85"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -1335,9 +1398,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af"
|
||||
checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
|
@ -1494,6 +1557,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
|
|
@ -8,6 +8,11 @@ license-file = "LICENSE"
|
|||
|
||||
[dependencies]
|
||||
cached = "0.39.0"
|
||||
diesel = { version = "2.0.2", features = [
|
||||
"sqlite",
|
||||
"returning_clauses_for_sqlite_3_35",
|
||||
"without-deprecated",
|
||||
] }
|
||||
hex = "0.4.3"
|
||||
hmac = "0.12.1"
|
||||
rocket = "0.5.0-rc.2"
|
||||
|
|
8
diesel.toml
Normal file
8
diesel.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# For documentation on how to configure this file,
|
||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
||||
|
||||
[migrations_directory]
|
||||
dir = "migrations"
|
1
migrations/2022-10-11-151321_create_history/down.sql
Normal file
1
migrations/2022-10-11-151321_create_history/down.sql
Normal file
|
@ -0,0 +1 @@
|
|||
DROP TABLE hooklog;
|
9
migrations/2022-10-11-151321_create_history/up.sql
Normal file
9
migrations/2022-10-11-151321_create_history/up.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE hooklog (
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
repo_url VARCHAR(300) NOT NULL,
|
||||
command_with_args TEXT NOT NULL,
|
||||
current_dir VARCHAR(300) NOT NULL,
|
||||
stdout TEXT NOT NULL,
|
||||
stderr TEXT NOT NULL,
|
||||
status_code INTEGER CHECK (status_code >= 0)
|
||||
);
|
57
src/config.rs
Normal file
57
src/config.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use cached::proc_macro::once;
|
||||
use serde::Deserialize;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct Hook {
|
||||
pub repo_url: String,
|
||||
pub current_dir: String,
|
||||
pub command: String,
|
||||
pub args: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct Config {
|
||||
secret: String,
|
||||
base_url: String,
|
||||
hooks: Vec<Hook>,
|
||||
}
|
||||
|
||||
#[once]
|
||||
fn get_config() -> Config {
|
||||
let config_path = Path::new("config.json");
|
||||
let config_file = File::open(config_path).unwrap();
|
||||
let config_reader = BufReader::new(config_file);
|
||||
let config: Config = serde_json::from_reader(config_reader).unwrap();
|
||||
|
||||
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)
|
||||
}
|
40
src/db.rs
Normal file
40
src/db.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use crate::config::Hook;
|
||||
use crate::models::{HookLog, NewHookLog};
|
||||
use crate::schema::hooklog;
|
||||
use diesel::prelude::*;
|
||||
use std::process::Output;
|
||||
|
||||
fn establish_connection() -> SqliteConnection {
|
||||
let database_url = "db/db.sqlite";
|
||||
SqliteConnection::establish(database_url).unwrap()
|
||||
}
|
||||
|
||||
pub fn add_hook_log(hook: &Hook, output: &Output) -> i32 {
|
||||
let connection = &mut establish_connection();
|
||||
|
||||
let command_with_args = hook.command.to_owned() + " " + &hook.args.join(" ");
|
||||
|
||||
let new_hook_log = NewHookLog {
|
||||
repo_url: &hook.repo_url,
|
||||
command_with_args: &command_with_args,
|
||||
current_dir: &hook.current_dir,
|
||||
stdout: std::str::from_utf8(&output.stdout).unwrap(),
|
||||
stderr: std::str::from_utf8(&output.stderr).unwrap(),
|
||||
status_code: output.status.code(),
|
||||
};
|
||||
|
||||
let result: HookLog = diesel::insert_into(hooklog::table)
|
||||
.values(&new_hook_log)
|
||||
.get_result(connection)
|
||||
.expect("Error saving hook log!");
|
||||
|
||||
result.id
|
||||
}
|
||||
|
||||
pub fn get_hook_log(id: i32) -> HookLog {
|
||||
use hooklog::dsl::hooklog;
|
||||
|
||||
let connection = &mut establish_connection();
|
||||
|
||||
hooklog.find(id).first(connection).unwrap()
|
||||
}
|
129
src/main.rs
129
src/main.rs
|
@ -1,72 +1,25 @@
|
|||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
mod config;
|
||||
mod db;
|
||||
mod models;
|
||||
mod schema;
|
||||
|
||||
use hmac::{Hmac, Mac};
|
||||
use rocket::data::{self, Data, FromData, Limits, Outcome};
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{self, Request};
|
||||
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha256;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::Path;
|
||||
|
||||
use cached::proc_macro::once;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct Hook {
|
||||
repo_url: String,
|
||||
commands: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct Config {
|
||||
secret: String,
|
||||
hooks: Vec<Hook>,
|
||||
}
|
||||
use sha2::Sha256;
|
||||
use std::process::Command;
|
||||
|
||||
struct Repo<'r> {
|
||||
name: &'r str,
|
||||
clone_url: &'r str,
|
||||
}
|
||||
|
||||
#[once]
|
||||
fn get_config() -> Config {
|
||||
let config_path = Path::new("config.json");
|
||||
let config_file = File::open(config_path).unwrap();
|
||||
let config_reader = BufReader::new(config_file);
|
||||
let config: Config = serde_json::from_reader(config_reader).unwrap();
|
||||
config
|
||||
}
|
||||
|
||||
#[once]
|
||||
fn get_secret() -> Vec<u8> {
|
||||
let config = get_config();
|
||||
config.secret.as_bytes().to_owned()
|
||||
}
|
||||
|
||||
#[once]
|
||||
fn get_hooks() -> Vec<Hook> {
|
||||
let config = get_config();
|
||||
config.hooks
|
||||
}
|
||||
|
||||
fn get_hook_commands(clone_url: &str) -> Option<Vec<String>> {
|
||||
let hooks = get_hooks();
|
||||
for hook in hooks {
|
||||
if hook.repo_url == clone_url {
|
||||
return Some(hook.commands);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn is_valid_signature(received_signature: &[u8], payload: &[u8]) -> bool {
|
||||
let mut mac = Hmac::<Sha256>::new_from_slice(&get_secret()).unwrap();
|
||||
let mut mac = Hmac::<Sha256>::new_from_slice(&config::get_secret()).unwrap();
|
||||
mac.update(payload);
|
||||
let expected_signature = mac.finalize().into_bytes();
|
||||
|
||||
|
@ -111,29 +64,67 @@ impl<'r> FromData<'r> for Repo<'r> {
|
|||
|
||||
let json: Value = serde_json::from_slice(&payload).unwrap();
|
||||
let repo = json.get("repository").unwrap();
|
||||
let name = repo.get("name").unwrap().as_str().unwrap().to_string();
|
||||
let clone_url = repo.get("clone_url").unwrap().as_str().unwrap().to_string();
|
||||
|
||||
let name = request::local_cache!(req, name);
|
||||
let clone_url = request::local_cache!(req, clone_url);
|
||||
|
||||
Outcome::Success(Repo { name, clone_url })
|
||||
Outcome::Success(Repo { clone_url })
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
fn index() -> &'static str {
|
||||
"Hello, world!"
|
||||
#[get("/<id>")]
|
||||
fn index(id: i32) -> String {
|
||||
let hook_log = db::get_hook_log(id);
|
||||
format!(
|
||||
"Hook log id:
|
||||
{}
|
||||
|
||||
Repository url:
|
||||
{}
|
||||
|
||||
Command with arguments:
|
||||
{}
|
||||
|
||||
Current directory of the command:
|
||||
{}
|
||||
|
||||
Standard output:
|
||||
{}
|
||||
|
||||
Standard error:
|
||||
{}
|
||||
|
||||
Status code:
|
||||
{}
|
||||
",
|
||||
hook_log.id,
|
||||
hook_log.repo_url,
|
||||
hook_log.command_with_args,
|
||||
hook_log.current_dir,
|
||||
hook_log.stdout,
|
||||
hook_log.stderr,
|
||||
match hook_log.status_code {
|
||||
Some(code) => code.to_string(),
|
||||
None => String::from("None"),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[post("/trigger", format = "json", data = "<repo>")]
|
||||
fn trigger(repo: Repo) -> &'static str {
|
||||
println!("{:?}", repo.name);
|
||||
let commands = get_hook_commands(repo.clone_url).unwrap();
|
||||
for command in commands {
|
||||
println!("{:?}", command)
|
||||
}
|
||||
"Successful trigger!"
|
||||
fn trigger(repo: Repo) -> String {
|
||||
let hook = config::get_hook(repo.clone_url).unwrap();
|
||||
|
||||
let output = Command::new(&hook.command)
|
||||
.args(&hook.args)
|
||||
.current_dir(&hook.current_dir)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
let new_hook_log_id = db::add_hook_log(&hook, &output);
|
||||
|
||||
let base_url = config::get_base_url();
|
||||
|
||||
format!("https://{base_url}/{new_hook_log_id}")
|
||||
}
|
||||
|
||||
#[launch]
|
||||
|
|
24
src/models.rs
Normal file
24
src/models.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use crate::schema::hooklog;
|
||||
use diesel::prelude::{Insertable, Queryable};
|
||||
|
||||
#[derive(Queryable)]
|
||||
pub struct HookLog {
|
||||
pub id: i32,
|
||||
pub repo_url: String,
|
||||
pub command_with_args: String,
|
||||
pub current_dir: String,
|
||||
pub stdout: String,
|
||||
pub stderr: String,
|
||||
pub status_code: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = hooklog)]
|
||||
pub struct NewHookLog<'a> {
|
||||
pub repo_url: &'a str,
|
||||
pub command_with_args: &'a str,
|
||||
pub current_dir: &'a str,
|
||||
pub stdout: &'a str,
|
||||
pub stderr: &'a str,
|
||||
pub status_code: Option<i32>,
|
||||
}
|
13
src/schema.rs
Normal file
13
src/schema.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
// @generated automatically by Diesel CLI.
|
||||
|
||||
diesel::table! {
|
||||
hooklog (id) {
|
||||
id -> Integer,
|
||||
repo_url -> Text,
|
||||
command_with_args -> Text,
|
||||
current_dir -> Text,
|
||||
stdout -> Text,
|
||||
stderr -> Text,
|
||||
status_code -> Nullable<Integer>,
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue