mirror of
https://codeberg.org/Mo8it/git-webhook-client
synced 2024-11-21 11:06:32 +00:00
Split code into more modules
This commit is contained in:
parent
bcd55f33c0
commit
322fa93c65
6 changed files with 137 additions and 130 deletions
0
migrations/.keep
Normal file
0
migrations/.keep
Normal file
|
@ -1,8 +1,9 @@
|
||||||
|
use diesel::prelude::*;
|
||||||
|
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;
|
||||||
use diesel::prelude::*;
|
|
||||||
use std::process::Output;
|
|
||||||
|
|
||||||
fn establish_connection() -> SqliteConnection {
|
fn establish_connection() -> SqliteConnection {
|
||||||
let database_url = "db/db.sqlite";
|
let database_url = "db/db.sqlite";
|
||||||
|
|
66
src/guards.rs
Normal file
66
src/guards.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use hmac::{Hmac, Mac};
|
||||||
|
use rocket::data::{Data, FromData, Limits, Outcome};
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::request::{self, Request};
|
||||||
|
use serde_json::Value;
|
||||||
|
use sha2::Sha256;
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
|
||||||
|
pub struct Repo<'r> {
|
||||||
|
pub clone_url: &'r str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RepoDataGuardError {
|
||||||
|
PayloadTooLarge,
|
||||||
|
MissingSignature,
|
||||||
|
MoreThatOneSignature,
|
||||||
|
InvalidSignature,
|
||||||
|
Io(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromData<'r> for Repo<'r> {
|
||||||
|
type Error = RepoDataGuardError;
|
||||||
|
|
||||||
|
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> {
|
||||||
|
let payload = match data.open(Limits::JSON).into_bytes().await {
|
||||||
|
Ok(payload) if payload.is_complete() => payload.into_inner(),
|
||||||
|
Ok(_) => {
|
||||||
|
return Outcome::Failure((Status::PayloadTooLarge, Self::Error::PayloadTooLarge))
|
||||||
|
}
|
||||||
|
Err(e) => return Outcome::Failure((Status::InternalServerError, Self::Error::Io(e))),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut received_signatures = req.headers().get("X-GITEA-SIGNATURE");
|
||||||
|
let received_signature = match received_signatures.next() {
|
||||||
|
Some(signature) => hex::decode(signature).unwrap(),
|
||||||
|
None => return Outcome::Failure((Status::BadRequest, Self::Error::MissingSignature)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if received_signatures.next().is_some() {
|
||||||
|
return Outcome::Failure((Status::BadRequest, Self::Error::MoreThatOneSignature));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_valid_signature(&received_signature, &payload) {
|
||||||
|
return Outcome::Failure((Status::BadRequest, Self::Error::InvalidSignature));
|
||||||
|
}
|
||||||
|
|
||||||
|
let json: Value = serde_json::from_slice(&payload).unwrap();
|
||||||
|
let repo = json.get("repository").unwrap();
|
||||||
|
let clone_url = repo.get("clone_url").unwrap().as_str().unwrap().to_string();
|
||||||
|
|
||||||
|
let clone_url = request::local_cache!(req, clone_url);
|
||||||
|
|
||||||
|
Outcome::Success(Repo { clone_url })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_signature(received_signature: &[u8], payload: &[u8]) -> bool {
|
||||||
|
let mut mac = Hmac::<Sha256>::new_from_slice(&config::get_secret()).unwrap();
|
||||||
|
mac.update(payload);
|
||||||
|
let expected_signature = mac.finalize().into_bytes();
|
||||||
|
|
||||||
|
received_signature[..] == expected_signature[..]
|
||||||
|
}
|
132
src/main.rs
132
src/main.rs
|
@ -1,135 +1,13 @@
|
||||||
#[macro_use]
|
|
||||||
extern crate rocket;
|
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod db;
|
mod db;
|
||||||
|
mod guards;
|
||||||
mod models;
|
mod models;
|
||||||
|
mod routes;
|
||||||
mod schema;
|
mod schema;
|
||||||
|
|
||||||
use hmac::{Hmac, Mac};
|
#[rocket::launch]
|
||||||
use rocket::data::{self, Data, FromData, Limits, Outcome};
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::request::{self, Request};
|
|
||||||
use serde_json::Value;
|
|
||||||
use sha2::Sha256;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
struct Repo<'r> {
|
|
||||||
clone_url: &'r str,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_valid_signature(received_signature: &[u8], payload: &[u8]) -> bool {
|
|
||||||
let mut mac = Hmac::<Sha256>::new_from_slice(&config::get_secret()).unwrap();
|
|
||||||
mac.update(payload);
|
|
||||||
let expected_signature = mac.finalize().into_bytes();
|
|
||||||
|
|
||||||
received_signature[..] == expected_signature[..]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum RepoDataGuardError {
|
|
||||||
PayloadTooLarge,
|
|
||||||
MissingSignature,
|
|
||||||
MoreThatOneSignature,
|
|
||||||
InvalidSignature,
|
|
||||||
Io(std::io::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rocket::async_trait]
|
|
||||||
impl<'r> FromData<'r> for Repo<'r> {
|
|
||||||
type Error = RepoDataGuardError;
|
|
||||||
|
|
||||||
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
|
|
||||||
let payload = match data.open(Limits::JSON).into_bytes().await {
|
|
||||||
Ok(payload) if payload.is_complete() => payload.into_inner(),
|
|
||||||
Ok(_) => {
|
|
||||||
return Outcome::Failure((Status::PayloadTooLarge, Self::Error::PayloadTooLarge))
|
|
||||||
}
|
|
||||||
Err(e) => return Outcome::Failure((Status::InternalServerError, Self::Error::Io(e))),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut received_signatures = req.headers().get("X-GITEA-SIGNATURE");
|
|
||||||
let received_signature = match received_signatures.next() {
|
|
||||||
Some(signature) => hex::decode(signature).unwrap(),
|
|
||||||
None => return Outcome::Failure((Status::BadRequest, Self::Error::MissingSignature)),
|
|
||||||
};
|
|
||||||
|
|
||||||
if received_signatures.next().is_some() {
|
|
||||||
return Outcome::Failure((Status::BadRequest, Self::Error::MoreThatOneSignature));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !is_valid_signature(&received_signature, &payload) {
|
|
||||||
return Outcome::Failure((Status::BadRequest, Self::Error::InvalidSignature));
|
|
||||||
}
|
|
||||||
|
|
||||||
let json: Value = serde_json::from_slice(&payload).unwrap();
|
|
||||||
let repo = json.get("repository").unwrap();
|
|
||||||
let clone_url = repo.get("clone_url").unwrap().as_str().unwrap().to_string();
|
|
||||||
|
|
||||||
let clone_url = request::local_cache!(req, clone_url);
|
|
||||||
|
|
||||||
Outcome::Success(Repo { clone_url })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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) -> 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]
|
|
||||||
fn rocket() -> _ {
|
fn rocket() -> _ {
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.mount("/", routes![index])
|
.mount("/", rocket::routes![routes::index])
|
||||||
.mount("/api", routes![trigger])
|
.mount("/api", rocket::routes![routes::trigger])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::schema::hooklog;
|
|
||||||
use diesel::prelude::{Insertable, Queryable};
|
use diesel::prelude::{Insertable, Queryable};
|
||||||
|
|
||||||
|
use crate::schema::hooklog;
|
||||||
|
|
||||||
#[derive(Queryable)]
|
#[derive(Queryable)]
|
||||||
pub struct HookLog {
|
pub struct HookLog {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
|
|
61
src/routes.rs
Normal file
61
src/routes.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
use rocket::{get, post};
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use crate::config;
|
||||||
|
use crate::db;
|
||||||
|
use crate::guards;
|
||||||
|
|
||||||
|
#[get("/<id>")]
|
||||||
|
pub 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>")]
|
||||||
|
pub fn trigger(repo: guards::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}")
|
||||||
|
}
|
Loading…
Reference in a new issue