299 lines
9.3 KiB
Rust
299 lines
9.3 KiB
Rust
use crate::AppService;
|
|
use crate::error::AppError;
|
|
use chrono::Utc;
|
|
use models::repos::repo_webhook;
|
|
use sea_orm::*;
|
|
use serde::{Deserialize, Serialize};
|
|
use session::Session;
|
|
|
|
#[derive(Debug, Clone, Deserialize, Serialize, utoipa::ToSchema)]
|
|
pub struct WebhookEvent {
|
|
#[serde(default)]
|
|
pub push: bool,
|
|
#[serde(default)]
|
|
pub tag_push: bool,
|
|
#[serde(default)]
|
|
pub pull_request: bool,
|
|
#[serde(default)]
|
|
pub issue_comment: bool,
|
|
#[serde(default)]
|
|
pub release: bool,
|
|
}
|
|
|
|
impl Default for WebhookEvent {
|
|
fn default() -> Self {
|
|
Self {
|
|
push: true,
|
|
tag_push: false,
|
|
pull_request: false,
|
|
issue_comment: false,
|
|
release: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct CreateWebhookParams {
|
|
pub url: String,
|
|
#[serde(default)]
|
|
pub content_type: Option<String>,
|
|
#[serde(default)]
|
|
pub secret: Option<String>,
|
|
#[serde(default)]
|
|
pub insecure_ssl: Option<bool>,
|
|
#[serde(default)]
|
|
pub events: WebhookEvent,
|
|
#[serde(default = "default_active")]
|
|
pub active: bool,
|
|
}
|
|
|
|
fn default_active() -> bool {
|
|
true
|
|
}
|
|
|
|
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
|
pub struct UpdateWebhookParams {
|
|
pub url: Option<String>,
|
|
pub content_type: Option<String>,
|
|
pub secret: Option<String>,
|
|
pub insecure_ssl: Option<bool>,
|
|
pub events: Option<WebhookEvent>,
|
|
pub active: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
|
|
pub struct WebhookResponse {
|
|
pub id: i64,
|
|
pub repo_uuid: String,
|
|
pub url: String,
|
|
pub content_type: String,
|
|
pub secret: Option<String>,
|
|
pub events: WebhookEvent,
|
|
pub active: bool,
|
|
pub created_at: chrono::DateTime<Utc>,
|
|
pub last_delivered_at: Option<chrono::DateTime<Utc>>,
|
|
pub touch_count: i64,
|
|
}
|
|
|
|
impl From<repo_webhook::Model> for WebhookResponse {
|
|
fn from(m: repo_webhook::Model) -> Self {
|
|
let events: WebhookEvent = serde_json::from_value(m.event.clone()).unwrap_or_default();
|
|
let url = m.url.unwrap_or_default();
|
|
let (content_type, secret, active) =
|
|
serde_json::from_value::<serde_json::Value>(m.event.clone())
|
|
.ok()
|
|
.map(|v| {
|
|
(
|
|
v.get("content_type")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("json")
|
|
.to_string(),
|
|
v.get("secret").and_then(|v| v.as_str()).map(String::from),
|
|
v.get("active").and_then(|v| v.as_bool()).unwrap_or(true),
|
|
)
|
|
})
|
|
.unwrap_or(("json".to_string(), None, true));
|
|
|
|
WebhookResponse {
|
|
id: m.id,
|
|
repo_uuid: m.repo.to_string(),
|
|
url,
|
|
content_type,
|
|
secret,
|
|
events,
|
|
active,
|
|
created_at: m.created_at,
|
|
last_delivered_at: m.last_delivered_at,
|
|
touch_count: m.touch_count,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
|
|
pub struct WebhookListResponse {
|
|
pub webhooks: Vec<WebhookResponse>,
|
|
pub total: usize,
|
|
}
|
|
|
|
impl AppService {
|
|
pub async fn git_webhook_list(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
ctx: &Session,
|
|
) -> Result<WebhookListResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let webhooks = repo_webhook::Entity::find()
|
|
.filter(repo_webhook::Column::Repo.eq(repo.id))
|
|
.order_by_asc(repo_webhook::Column::Id)
|
|
.all(&self.db)
|
|
.await?;
|
|
let total = webhooks.len();
|
|
let webhooks = webhooks.into_iter().map(WebhookResponse::from).collect();
|
|
Ok(WebhookListResponse { webhooks, total })
|
|
}
|
|
|
|
pub async fn git_webhook_create(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
params: CreateWebhookParams,
|
|
ctx: &Session,
|
|
) -> Result<WebhookResponse, AppError> {
|
|
let repo = self
|
|
.utils_find_repo(namespace.clone(), repo_name.clone(), ctx)
|
|
.await?;
|
|
let _ = self
|
|
.utils_check_repo_admin(namespace.clone(), repo_name.clone(), ctx)
|
|
.await?;
|
|
|
|
let event_json = serde_json::json!({
|
|
"push": params.events.push,
|
|
"tag_push": params.events.tag_push,
|
|
"pull_request": params.events.pull_request,
|
|
"issue_comment": params.events.issue_comment,
|
|
"release": params.events.release,
|
|
"content_type": params.content_type.unwrap_or_else(|| "json".to_string()),
|
|
"secret": params.secret,
|
|
"active": params.active,
|
|
});
|
|
|
|
let model = repo_webhook::ActiveModel {
|
|
repo: Set(repo.id),
|
|
event: Set(event_json),
|
|
url: Set(Some(params.url)),
|
|
access_key: Set(None),
|
|
secret_key: Set(params.secret),
|
|
created_at: Set(Utc::now()),
|
|
last_delivered_at: Set(None),
|
|
touch_count: Set(0),
|
|
..Default::default()
|
|
}
|
|
.insert(&self.db)
|
|
.await?;
|
|
|
|
Ok(WebhookResponse::from(model))
|
|
}
|
|
|
|
pub async fn git_webhook_get(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
webhook_id: i64,
|
|
ctx: &Session,
|
|
) -> Result<WebhookResponse, AppError> {
|
|
let repo = self.utils_find_repo(namespace, repo_name, ctx).await?;
|
|
let webhook = repo_webhook::Entity::find_by_id(webhook_id)
|
|
.one(&self.db)
|
|
.await?
|
|
.ok_or(AppError::NotFound("Webhook not found".to_string()))?;
|
|
|
|
if webhook.repo != repo.id {
|
|
return Err(AppError::NotFound("Webhook not found".to_string()));
|
|
}
|
|
|
|
Ok(WebhookResponse::from(webhook))
|
|
}
|
|
|
|
pub async fn git_webhook_update(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
webhook_id: i64,
|
|
params: UpdateWebhookParams,
|
|
ctx: &Session,
|
|
) -> Result<WebhookResponse, AppError> {
|
|
let repo = self
|
|
.utils_find_repo(namespace.clone(), repo_name.clone(), ctx)
|
|
.await?;
|
|
let _ = self
|
|
.utils_check_repo_admin(namespace.clone(), repo_name.clone(), ctx)
|
|
.await?;
|
|
|
|
let webhook = repo_webhook::Entity::find_by_id(webhook_id)
|
|
.one(&self.db)
|
|
.await?
|
|
.ok_or(AppError::NotFound("Webhook not found".to_string()))?;
|
|
|
|
if webhook.repo != repo.id {
|
|
return Err(AppError::NotFound("Webhook not found".to_string()));
|
|
}
|
|
|
|
let mut active: repo_webhook::ActiveModel = webhook.clone().into();
|
|
if let Some(url) = params.url {
|
|
active.url = Set(Some(url));
|
|
}
|
|
let secret_val = params.secret.clone();
|
|
if let Some(secret) = params.secret {
|
|
active.secret_key = Set(Some(secret));
|
|
}
|
|
if params.events.is_some() || params.content_type.is_some() || params.active.is_some() {
|
|
let existing: serde_json::Value = webhook.event.clone();
|
|
let events = params
|
|
.events
|
|
.unwrap_or_else(|| serde_json::from_value(existing.clone()).unwrap_or_default());
|
|
let content_type = params
|
|
.content_type
|
|
.or_else(|| {
|
|
existing
|
|
.get("content_type")
|
|
.and_then(|v| v.as_str())
|
|
.map(String::from)
|
|
})
|
|
.unwrap_or_else(|| "json".to_string());
|
|
let active_val = params
|
|
.active
|
|
.or_else(|| existing.get("active").and_then(|v| v.as_bool()))
|
|
.unwrap_or(true);
|
|
let secret_in_event = existing
|
|
.get("secret")
|
|
.and_then(|v| v.as_str())
|
|
.map(String::from);
|
|
let final_secret = secret_val.clone().or(secret_in_event);
|
|
|
|
active.event = Set(serde_json::json!({
|
|
"push": events.push,
|
|
"tag_push": events.tag_push,
|
|
"pull_request": events.pull_request,
|
|
"issue_comment": events.issue_comment,
|
|
"release": events.release,
|
|
"content_type": content_type,
|
|
"secret": final_secret,
|
|
"active": active_val,
|
|
}));
|
|
}
|
|
|
|
let updated = active.update(&self.db).await?;
|
|
Ok(WebhookResponse::from(updated))
|
|
}
|
|
|
|
pub async fn git_webhook_delete(
|
|
&self,
|
|
namespace: String,
|
|
repo_name: String,
|
|
webhook_id: i64,
|
|
ctx: &Session,
|
|
) -> Result<(), AppError> {
|
|
let repo = self
|
|
.utils_find_repo(namespace.clone(), repo_name.clone(), ctx)
|
|
.await?;
|
|
let _ = self
|
|
.utils_check_repo_admin(namespace.clone(), repo_name.clone(), ctx)
|
|
.await?;
|
|
|
|
let webhook = repo_webhook::Entity::find_by_id(webhook_id)
|
|
.one(&self.db)
|
|
.await?
|
|
.ok_or(AppError::NotFound("Webhook not found".to_string()))?;
|
|
|
|
if webhook.repo != repo.id {
|
|
return Err(AppError::NotFound("Webhook not found".to_string()));
|
|
}
|
|
|
|
repo_webhook::Entity::delete_by_id(webhook_id)
|
|
.exec(&self.db)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
}
|