diff --git a/src/hook_push.rs b/src/hook_push.rs new file mode 100644 index 0000000..fb8661e --- /dev/null +++ b/src/hook_push.rs @@ -0,0 +1,142 @@ +use serde::Deserialize; +use serde_json::Value; + +#[allow(dead_code)] +#[derive(Debug, Deserialize)] +pub(crate) struct HookPush { + #[serde(rename = "ref")] + ref_field: String, + before: String, + compare_url: String, + commits: Vec, + total_commits: u64, + head_commit: HookCommit, + repository: HookRepository, + pusher: HookUser, + sender: HookUser, +} + +#[allow(dead_code)] +#[derive(Debug, Deserialize)] +pub(crate) struct HookUser { + id: u64, + login: String, + login_name: String, + full_name: String, + email: String, + avatar_url: String, + language: String, + is_admin: bool, + last_login: String, // TODO: parse to datetime + created: String, // TODO: parse to datetime + restricted: bool, + active: bool, + prohibit_login: bool, + location: String, + website: String, + description: String, + visibility: String, + followers_count: u64, + following_count: u64, + starred_repos_count: u64, + username: String, +} + +#[allow(dead_code)] +#[derive(Debug, Deserialize)] +pub(crate) struct HookRepository { + id: u64, + owner: HookUser, + name: String, + full_name: String, + description: String, + empty: bool, + private: bool, + fork: bool, + template: bool, + parent: Value, // Was null in test hook + mirror: bool, + size: u64, + language: String, + languages_url: String, + html_url: String, + url: String, + link: String, + ssh_url: String, + clone_url: String, + original_url: String, + website: String, + stars_count: u64, + forks_count: u64, + watchers_count: u64, + open_issues_count: u64, + open_pr_counter: u64, + release_counter: u64, + default_branch: String, + archived: bool, + created_at: String, // TODO: parse to datetime + updated_at: String, // TODO: parse to datetime + archived_at: String, // TODO: parse to datetime + permissions: HookRepositoryPermissions, + has_issues: bool, + internal_tracker: HookRepositoryInternalTracker, + has_wiki: bool, + has_pull_requests: bool, + has_projects: bool, + has_releases: bool, + has_packages: bool, + has_actions: bool, + ignore_whitespace_conflicts: bool, + allow_merge_commits: bool, + allow_rebase: bool, + allow_rebase_explicit: bool, + allow_squash_merge: bool, + allow_rebase_update: bool, + default_delete_branch_after_merge: bool, + default_merge_style: String, + default_allow_maintainer_edit: bool, + avatar_url: String, + internal: bool, + mirror_interval: String, + mirror_updated: String, // TODO: parse to datetime + repo_transfer: Value, // Was null in test hook +} + +#[allow(dead_code)] +#[derive(Debug, Deserialize)] +pub(crate) struct HookRepositoryPermissions { + admin: bool, + push: bool, + pull: bool, +} + +#[allow(dead_code)] +#[derive(Debug, Deserialize)] +pub(crate) struct HookRepositoryInternalTracker { + enable_time_tracker: bool, + allow_only_contributors_to_track_time: bool, + enable_issue_dependencies: bool, +} + +#[allow(dead_code)] +#[derive(Debug, Deserialize)] +pub(crate) struct HookCommit { + id: String, + message: String, + url: String, + author: HookGitUser, + committer: HookGitUser, + verification: Value, // Was null in test hook + timestamp: String, // TODO: parse to datetime + added: Vec, + removed: Vec, + modified: Vec, +} + +#[allow(dead_code)] +#[derive(Debug, Deserialize)] +pub(crate) struct HookGitUser { + name: String, + email: String, + username: String, +} diff --git a/src/main.rs b/src/main.rs index a805ace..96575a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use tracing_subscriber::util::SubscriberInitExt; use self::webhook::hook; +mod hook_push; mod webhook; #[tokio::main] diff --git a/src/webhook.rs b/src/webhook.rs index 074e5da..f044ca9 100644 --- a/src/webhook.rs +++ b/src/webhook.rs @@ -1,158 +1,74 @@ +use axum::async_trait; +use axum::extract::FromRequest; +use axum::extract::Request; use axum::http::HeaderMap; use axum::http::StatusCode; +use axum::response::IntoResponse; +use axum::response::Response; use axum::Json; -use serde::Deserialize; +use axum::RequestExt; use serde::Serialize; -use serde_json::Value; +use tracing::debug; + +use crate::hook_push::HookPush; pub(crate) async fn hook( - headers: HeaderMap, - Json(payload): Json, + _headers: HeaderMap, + payload: HookRequest, ) -> (StatusCode, Json) { - (StatusCode::OK, Json(HookResponse { ok: true })) + debug!("REQ: {:?}", payload); + match payload { + HookRequest::Push(_payload) => ( + StatusCode::OK, + Json(HookResponse { + ok: true, + message: None, + }), + ), + HookRequest::Unrecognized(payload) => ( + StatusCode::BAD_REQUEST, + Json(HookResponse { + ok: false, + message: Some(format!("unrecognized event type: {payload}")), + }), + ), + } } -#[allow(dead_code)] -#[derive(Deserialize)] -pub(crate) struct HookRequest { - #[serde(rename = "ref")] - ref_field: String, - before: String, - compare_url: String, - commits: Vec, - total_commits: u64, - head_commit: HookCommit, - repository: HookRepository, - pusher: HookUser, - sender: HookUser, +#[derive(Debug)] +pub(crate) enum HookRequest { + Push(HookPush), + Unrecognized(String), } -#[allow(dead_code)] -#[derive(Deserialize)] -pub(crate) struct HookUser { - id: u64, - login: String, - login_name: String, - full_name: String, - email: String, - avatar_url: String, - language: String, - is_admin: bool, - last_login: String, // TODO: parse to datetime - created: String, // TODO: parse to datetime - restricted: bool, - active: bool, - prohibit_login: bool, - location: String, - website: String, - description: String, - visibility: String, - followers_count: u64, - following_count: u64, - starred_repos_count: u64, - username: String, +#[async_trait] +impl FromRequest for HookRequest +where + S: Send + Sync, +{ + type Rejection = Response; + + async fn from_request(req: Request, _state: &S) -> Result { + let event_type = req + .headers() + .get("X-Gitea-Event-Type") + .ok_or(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response())?; + let event_type = event_type + .to_str() + .map_err(|_| StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response())?; + match event_type { + "push" => { + let Json(payload): Json = + req.extract().await.map_err(IntoResponse::into_response)?; + Ok(HookRequest::Push(payload)) + } + _ => Ok(HookRequest::Unrecognized(event_type.to_owned())), + } + } } -#[allow(dead_code)] -#[derive(Deserialize)] -pub(crate) struct HookRepository { - id: u64, - owner: HookUser, - name: String, - full_name: String, - description: String, - empty: bool, - private: bool, - fork: bool, - template: bool, - parent: Value, // Was null in test hook - mirror: bool, - size: u64, - language: String, - languages_url: String, - html_url: String, - url: String, - link: String, - ssh_url: String, - clone_url: String, - original_url: String, - website: String, - stars_count: u64, - forks_count: u64, - watchers_count: u64, - open_issues_count: u64, - open_pr_counter: u64, - release_counter: u64, - default_branch: String, - archived: bool, - created_at: String, // TODO: parse to datetime - updated_at: String, // TODO: parse to datetime - archived_at: String, // TODO: parse to datetime - permissions: HookRepositoryPermissions, - has_issues: bool, - internal_tracker: HookRepositoryInternalTracker, - has_wiki: bool, - has_pull_requests: bool, - has_projects: bool, - has_releases: bool, - has_packages: bool, - has_actions: bool, - ignore_whitespace_conflicts: bool, - allow_merge_commits: bool, - allow_rebase: bool, - allow_rebase_explicit: bool, - allow_squash_merge: bool, - allow_rebase_update: bool, - default_delete_branch_after_merge: bool, - default_merge_style: String, - default_allow_maintainer_edit: bool, - avatar_url: String, - internal: bool, - mirror_interval: String, - mirror_updated: String, // TODO: parse to datetime - repo_transfer: Value, // Was null in test hook -} - -#[allow(dead_code)] -#[derive(Deserialize)] -pub(crate) struct HookRepositoryPermissions { - admin: bool, - push: bool, - pull: bool, -} - -#[allow(dead_code)] -#[derive(Deserialize)] -pub(crate) struct HookRepositoryInternalTracker { - enable_time_tracker: bool, - allow_only_contributors_to_track_time: bool, - enable_issue_dependencies: bool, -} - -#[allow(dead_code)] -#[derive(Deserialize)] -pub(crate) struct HookCommit { - id: String, - message: String, - url: String, - author: HookGitUser, - committer: HookGitUser, - verification: Value, // Was null in test hook - timestamp: String, // TODO: parse to datetime - added: Vec, - removed: Vec, - modified: Vec, -} - -#[allow(dead_code)] -#[derive(Deserialize)] -pub(crate) struct HookGitUser { - name: String, - email: String, - username: String, -} - -#[derive(Serialize)] +#[derive(Debug, Serialize)] pub(crate) struct HookResponse { ok: bool, + message: Option, }