use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::error::AppError; use crate::models::channels::ChannelWebhook; use crate::service::ImService; use super::session::ImSession; #[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)] pub struct CreateWebhookParams { pub name: String, pub url: String, pub secret: Option, pub events: Vec, } #[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)] pub struct UpdateWebhookParams { pub name: Option, pub url: Option, pub secret: Option, pub events: Option>, pub active: Option, } impl ImService { pub async fn webhook_list( &self, _ctx: &ImSession, channel_id: Uuid, ) -> Result, AppError> { sqlx::query_as::<_, ChannelWebhook>( "SELECT id, channel_id, name, url, secret_ciphertext, events, active, \ last_delivery_status, last_delivery_at, created_by, created_at, updated_at \ FROM channel_webhook WHERE channel_id = $1 ORDER BY created_at", ) .bind(channel_id) .fetch_all(self.ctx.db.reader()) .await .map_err(AppError::Database) } pub async fn webhook_create( &self, ctx: &ImSession, channel_id: Uuid, params: CreateWebhookParams, ) -> Result { let now = chrono::Utc::now(); sqlx::query_as::<_, ChannelWebhook>( "INSERT INTO channel_webhook \ (id, channel_id, name, url, secret_ciphertext, events, active, \ created_by, created_at, updated_at) \ VALUES ($1, $2, $3, $4, $5, $6, true, $7, $8, $8) \ RETURNING id, channel_id, name, url, secret_ciphertext, events, active, \ last_delivery_status, last_delivery_at, created_by, created_at, updated_at", ) .bind(Uuid::now_v7()) .bind(channel_id) .bind(¶ms.name) .bind(¶ms.url) .bind(params.secret.as_deref()) .bind(¶ms.events) .bind(ctx.user) .bind(now) .fetch_one(self.ctx.db.writer()) .await .map_err(AppError::Database) } pub async fn webhook_update( &self, _ctx: &ImSession, webhook_id: Uuid, params: UpdateWebhookParams, ) -> Result { let now = chrono::Utc::now(); sqlx::query_as::<_, ChannelWebhook>( "UPDATE channel_webhook SET \ name = COALESCE($1, name), \ url = COALESCE($2, url), \ secret_ciphertext = COALESCE($3, secret_ciphertext), \ events = COALESCE($4, events), \ active = COALESCE($5, active), \ updated_at = $6 \ WHERE id = $7 \ RETURNING id, channel_id, name, url, secret_ciphertext, events, active, \ last_delivery_status, last_delivery_at, created_by, created_at, updated_at", ) .bind(params.name.as_deref()) .bind(params.url.as_deref()) .bind(params.secret.as_deref()) .bind(params.events.as_ref()) .bind(params.active) .bind(now) .bind(webhook_id) .fetch_one(self.ctx.db.writer()) .await .map_err(AppError::Database) } pub async fn webhook_delete( &self, _ctx: &ImSession, webhook_id: Uuid, ) -> Result<(), AppError> { sqlx::query("DELETE FROM channel_webhook WHERE id = $1") .bind(webhook_id) .execute(self.ctx.db.writer()) .await .map_err(AppError::Database)?; Ok(()) } }