Skip to content

Commit abe02d4

Browse files
committed
fix: adjust event data structure and prevent duplicate webhooks
1 parent 0a44dc9 commit abe02d4

File tree

8 files changed

+56
-26
lines changed

8 files changed

+56
-26
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ resolver = "2"
77
[package]
88
name = "backend"
99
description = "Backend API and services for StackClass"
10-
version = "0.32.8"
10+
version = "0.32.9"
1111
edition = "2024"
1212

1313
default-run = "stackclass-server"

crates/gitea-client/src/client/hook.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use reqwest::StatusCode;
1717
use crate::{
1818
client::GiteaClient,
1919
error::ClientError,
20-
types::{CreateHookRequest, Hook},
20+
types::{CreateHookRequest, Hook, HookType},
2121
};
2222

2323
impl GiteaClient {
@@ -34,13 +34,16 @@ impl GiteaClient {
3434

3535
/// Lists all system hooks (admin endpoint).
3636
///
37+
/// # Arguments
38+
/// * `hook_type` - The type of hooks to list (defaults to `HookType::System`).
39+
///
3740
/// # Possible Responses
3841
/// - 200: List of hooks returned successfully (returns `Vec<Hook>`).
3942
///
4043
/// https://docs.gitea.com/api/1.24/#tag/admin/operation/adminListHooks
4144
#[inline]
42-
pub async fn list_system_hooks(&self) -> Result<Vec<Hook>, ClientError> {
43-
self.list_hooks("admin/hooks").await
45+
pub async fn list_system_hooks(&self, hook_type: HookType) -> Result<Vec<Hook>, ClientError> {
46+
self.list_hooks(&format!("admin/hooks?type={hook_type}")).await
4447
}
4548

4649
/// Creates a new hook at the specified path.

crates/gitea-client/src/types/event.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414

1515
use serde::{Deserialize, Serialize};
1616

17-
use super::{PartialCommit, PartialRepository, PartialUser};
17+
use crate::types::{Repository, User};
18+
19+
use super::PartialCommit;
1820

1921
/// GitHub webhook event payload.
2022
#[derive(Debug, Serialize, Deserialize)]
2123
pub struct Event {
2224
/// Secret token for verifying the webhook origin.
23-
pub secret: String,
25+
pub secret: Option<String>,
2426

2527
/// The full Git ref that was pushed.
2628
#[serde(rename = "ref")]
@@ -38,12 +40,18 @@ pub struct Event {
3840
/// List of commits included in the push.
3941
pub commits: Vec<PartialCommit>,
4042

43+
/// Total number of commits in the push event.
44+
pub total_commits: u64,
45+
46+
/// The most recent commit in the push event.
47+
pub head_commit: PartialCommit,
48+
4149
/// Repository where the event occurred.
42-
pub repository: PartialRepository,
50+
pub repository: Repository,
4351

4452
/// User who performed the push.
45-
pub pusher: PartialUser,
53+
pub pusher: User,
4654

4755
/// User who triggered the event.
48-
pub sender: PartialUser,
56+
pub sender: User,
4957
}

crates/gitea-client/src/types/hook.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ pub struct Hook {
5555
/// This is used to avoid duplicate hooks with the same settings.
5656
pub fn matching(hook: &Hook, req: &CreateHookRequest) -> bool {
5757
hook.kind == req.kind &&
58-
hook.config == req.config &&
5958
hook.branch_filter == req.branch_filter &&
60-
hook.events == req.events
59+
hook.events == req.events &&
60+
hook.config.get("url") == req.config.get("url") &&
61+
hook.config.get("content_type") == req.config.get("content_type")
6162
}
6263

6364
/// Request body for creating a hook.
@@ -82,3 +83,26 @@ pub struct CreateHookRequest {
8283
#[serde(rename = "type")]
8384
pub kind: String,
8485
}
86+
87+
/// Represents the type of hooks to list.
88+
#[derive(Debug, Clone, Copy, Default)]
89+
pub enum HookType {
90+
/// System hooks.
91+
#[default]
92+
System,
93+
/// Default hooks.
94+
Default,
95+
/// Both system and default hooks.
96+
All,
97+
}
98+
99+
impl std::fmt::Display for HookType {
100+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101+
let s = match self {
102+
HookType::System => "system",
103+
HookType::Default => "default",
104+
HookType::All => "all",
105+
};
106+
write!(f, "{}", s)
107+
}
108+
}

crates/gitea-client/src/types/user.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ pub struct User {
7777
/// Number of repositories starred by the user.
7878
pub starred_repos_count: u64,
7979

80+
/// Username of the user.
81+
pub username: String,
82+
8083
/// Visibility setting of the user.
8184
pub visibility: String,
8285

@@ -127,21 +130,12 @@ pub struct CreateUserRequest {
127130
/// containing only the most essential fields.
128131
#[derive(Debug, Serialize, Deserialize)]
129132
pub struct PartialUser {
130-
/// Unique identifier for the user.
131-
pub id: u64,
132-
133-
/// Username of the user.
134-
pub login: String,
135-
136133
/// Full name of the user.
137-
pub full_name: String,
134+
pub name: String,
138135

139136
/// Email address of the user.
140137
pub email: String,
141138

142-
/// URL to the user's avatar.
143-
pub avatar_url: String,
144-
145-
/// Login name of the user.
139+
/// Username of the user.
146140
pub username: String,
147141
}

openapi.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"license": {
77
"name": ""
88
},
9-
"version": "0.32.8"
9+
"version": "0.32.9"
1010
},
1111
"paths": {
1212
"/v1/courses": {

src/service/repository.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl RepoService {
110110
/// - Otherwise, it triggers the pipeline for the current stage and monitors completion.
111111
/// - On success, marks the stage as complete.
112112
pub async fn process(&self, event: &Event) -> Result<()> {
113-
let PartialRepository { owner, name, .. } = &event.repository;
113+
let Repository { owner, name, .. } = &event.repository;
114114
debug!("Handling push event for repository: {}", name);
115115

116116
let ctx = self.ctx.clone();
@@ -215,6 +215,7 @@ impl RepoService {
215215
active: true,
216216
branch_filter: Some("main".to_string()),
217217
config: HashMap::from([
218+
("is_system_webhook".to_string(), "true".to_string()),
218219
("content_type".to_string(), "json".to_string()),
219220
("url".to_string(), url.clone()),
220221
]),
@@ -224,7 +225,7 @@ impl RepoService {
224225
};
225226

226227
// List all existing hooks
227-
let hooks = self.ctx.git.list_system_hooks().await?;
228+
let hooks = self.ctx.git.list_system_hooks(HookType::System).await?;
228229

229230
// Check if a hook with the same configuration already exists
230231
if !hooks.iter().any(|hook| matching(hook, &req)) {

0 commit comments

Comments
 (0)