use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::error::AppError; use crate::models::channels::ImIntegration; use crate::service::ImService; use super::session::ImSession; #[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)] pub struct CreateIntegrationParams { pub provider: String, pub name: String, pub external_workspace_id: Option, pub internal_channel_id: Option, pub external_channel_id: Option, pub bot_token: Option, pub webhook_url: Option, pub sync_direction: String, } #[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)] pub struct UpdateIntegrationParams { pub name: Option, pub external_channel_id: Option, pub webhook_url: Option, pub sync_direction: Option, pub enabled: Option, } impl ImService { pub async fn integration_list( &self, _ctx: &ImSession, workspace_id: Uuid, ) -> Result, AppError> { sqlx::query_as::<_, ImIntegration>( "SELECT id, workspace_id, provider, name, external_workspace_id, \ internal_channel_id, external_channel_id, bot_token_ciphertext, webhook_url, \ sync_direction, user_mapping, enabled, installed_by, last_sync_at, \ created_at, updated_at \ FROM im_integration WHERE workspace_id = $1 ORDER BY created_at", ) .bind(workspace_id) .fetch_all(self.ctx.db.reader()) .await .map_err(AppError::Database) } pub async fn integration_create( &self, ctx: &ImSession, workspace_id: Uuid, params: CreateIntegrationParams, ) -> Result { let now = chrono::Utc::now(); sqlx::query_as::<_, ImIntegration>( "INSERT INTO im_integration \ (id, workspace_id, provider, name, external_workspace_id, \ internal_channel_id, external_channel_id, bot_token_ciphertext, webhook_url, \ sync_direction, enabled, installed_by, created_at, updated_at) \ VALUES ($1, $2, $3::provider, $4, $5, $6, $7, $8, $9, $10::sync_direction, \ true, $11, $12, $12) \ RETURNING id, workspace_id, provider, name, external_workspace_id, \ internal_channel_id, external_channel_id, bot_token_ciphertext, webhook_url, \ sync_direction, user_mapping, enabled, installed_by, last_sync_at, \ created_at, updated_at", ) .bind(Uuid::now_v7()) .bind(workspace_id) .bind(¶ms.provider) .bind(¶ms.name) .bind(params.external_workspace_id.as_deref()) .bind(params.internal_channel_id) .bind(params.external_channel_id.as_deref()) .bind(params.bot_token.as_deref()) .bind(params.webhook_url.as_deref()) .bind(¶ms.sync_direction) .bind(ctx.user) .bind(now) .fetch_one(self.ctx.db.writer()) .await .map_err(AppError::Database) } pub async fn integration_update( &self, _ctx: &ImSession, integration_id: Uuid, params: UpdateIntegrationParams, ) -> Result { let now = chrono::Utc::now(); sqlx::query_as::<_, ImIntegration>( "UPDATE im_integration SET \ name = COALESCE($1, name), \ external_channel_id = COALESCE($2, external_channel_id), \ webhook_url = COALESCE($3, webhook_url), \ sync_direction = COALESCE($4::sync_direction, sync_direction), \ enabled = COALESCE($5, enabled), \ updated_at = $6 \ WHERE id = $7 \ RETURNING id, workspace_id, provider, name, external_workspace_id, \ internal_channel_id, external_channel_id, bot_token_ciphertext, webhook_url, \ sync_direction, user_mapping, enabled, installed_by, last_sync_at, \ created_at, updated_at", ) .bind(params.name.as_deref()) .bind(params.external_channel_id.as_deref()) .bind(params.webhook_url.as_deref()) .bind(params.sync_direction.as_deref()) .bind(params.enabled) .bind(now) .bind(integration_id) .fetch_one(self.ctx.db.writer()) .await .map_err(AppError::Database) } pub async fn integration_delete( &self, _ctx: &ImSession, integration_id: Uuid, ) -> Result<(), AppError> { sqlx::query("DELETE FROM im_integration WHERE id = $1") .bind(integration_id) .execute(self.ctx.db.writer()) .await .map_err(AppError::Database)?; Ok(()) } }