All checks were successful
## Summary - Add HTTP response status checking to all Gitea tracker methods that were silently swallowing errors - Add fallback in create_pr_review: if inline comments fail, retry as plain PR comment ## Test plan - [ ] Deploy and trigger a PR review, check logs for actual error details - [ ] Verify fallback posts summary comment when inline comments fail Co-authored-by: Sharang Parnerkar <parnerkarsharang@gmail.com> Co-authored-by: Sharang Parnerkar <30073382+mighty840@users.noreply.github.com> Reviewed-on: #47
227 lines
6.1 KiB
Rust
227 lines
6.1 KiB
Rust
use dioxus::prelude::*;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use compliance_core::models::McpServerConfig;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
|
pub struct McpServersResponse {
|
|
pub data: Vec<McpServerConfig>,
|
|
}
|
|
|
|
#[server]
|
|
pub async fn fetch_mcp_servers() -> Result<McpServersResponse, ServerFnError> {
|
|
use mongodb::bson::doc;
|
|
|
|
let state: super::server_state::ServerState =
|
|
dioxus_fullstack::FullstackContext::extract().await?;
|
|
|
|
let mut cursor = state
|
|
.db
|
|
.mcp_servers()
|
|
.find(doc! {})
|
|
.sort(doc! { "created_at": -1 })
|
|
.await
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
let mut data = Vec::new();
|
|
while cursor
|
|
.advance()
|
|
.await
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?
|
|
{
|
|
let server = cursor
|
|
.deserialize_current()
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
data.push(server);
|
|
}
|
|
|
|
Ok(McpServersResponse { data })
|
|
}
|
|
|
|
#[server]
|
|
pub async fn add_mcp_server(
|
|
name: String,
|
|
endpoint_url: String,
|
|
transport: String,
|
|
port: String,
|
|
description: String,
|
|
mongodb_uri: String,
|
|
mongodb_database: String,
|
|
) -> Result<(), ServerFnError> {
|
|
use chrono::Utc;
|
|
use compliance_core::models::{McpServerStatus, McpTransport};
|
|
|
|
let state: super::server_state::ServerState =
|
|
dioxus_fullstack::FullstackContext::extract().await?;
|
|
|
|
let transport = match transport.as_str() {
|
|
"http" => McpTransport::Http,
|
|
_ => McpTransport::Stdio,
|
|
};
|
|
|
|
let port_num: Option<u16> = port.parse().ok();
|
|
|
|
// Generate a random access token
|
|
let token = format!("mcp_{}", uuid::Uuid::new_v4().to_string().replace('-', ""));
|
|
|
|
let all_tools = vec![
|
|
"list_findings".to_string(),
|
|
"get_finding".to_string(),
|
|
"findings_summary".to_string(),
|
|
"list_sbom_packages".to_string(),
|
|
"sbom_vuln_report".to_string(),
|
|
"list_dast_findings".to_string(),
|
|
"dast_scan_summary".to_string(),
|
|
];
|
|
|
|
let now = Utc::now();
|
|
let server = McpServerConfig {
|
|
id: None,
|
|
name,
|
|
endpoint_url,
|
|
transport,
|
|
port: port_num,
|
|
status: McpServerStatus::Stopped,
|
|
access_token: token,
|
|
tools_enabled: all_tools,
|
|
description: if description.is_empty() {
|
|
None
|
|
} else {
|
|
Some(description)
|
|
},
|
|
mongodb_uri: if mongodb_uri.is_empty() {
|
|
None
|
|
} else {
|
|
Some(mongodb_uri)
|
|
},
|
|
mongodb_database: if mongodb_database.is_empty() {
|
|
None
|
|
} else {
|
|
Some(mongodb_database)
|
|
},
|
|
created_at: now,
|
|
updated_at: now,
|
|
};
|
|
|
|
state
|
|
.db
|
|
.mcp_servers()
|
|
.insert_one(server)
|
|
.await
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Probe each MCP server's health endpoint and update status in MongoDB.
|
|
#[server]
|
|
pub async fn refresh_mcp_status() -> Result<(), ServerFnError> {
|
|
use chrono::Utc;
|
|
use compliance_core::models::McpServerStatus;
|
|
use mongodb::bson::doc;
|
|
|
|
let state: super::server_state::ServerState =
|
|
dioxus_fullstack::FullstackContext::extract().await?;
|
|
|
|
let mut cursor = state
|
|
.db
|
|
.mcp_servers()
|
|
.find(doc! {})
|
|
.await
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
let client = reqwest::Client::builder()
|
|
.timeout(std::time::Duration::from_secs(5))
|
|
.build()
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
while cursor
|
|
.advance()
|
|
.await
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?
|
|
{
|
|
let server: compliance_core::models::McpServerConfig = cursor
|
|
.deserialize_current()
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
let Some(oid) = server.id else { continue };
|
|
|
|
// Derive health URL from the endpoint (replace trailing /mcp with /health)
|
|
let health_url = if server.endpoint_url.ends_with("/mcp") {
|
|
format!(
|
|
"{}health",
|
|
&server.endpoint_url[..server.endpoint_url.len() - 3]
|
|
)
|
|
} else {
|
|
format!("{}/health", server.endpoint_url.trim_end_matches('/'))
|
|
};
|
|
|
|
let new_status = match client.get(&health_url).send().await {
|
|
Ok(resp) if resp.status().is_success() => McpServerStatus::Running,
|
|
_ => McpServerStatus::Stopped,
|
|
};
|
|
|
|
let status_bson = match bson::to_bson(&new_status) {
|
|
Ok(b) => b,
|
|
Err(_) => continue,
|
|
};
|
|
|
|
let _ = state
|
|
.db
|
|
.mcp_servers()
|
|
.update_one(
|
|
doc! { "_id": oid },
|
|
doc! { "$set": { "status": status_bson, "updated_at": Utc::now().to_rfc3339() } },
|
|
)
|
|
.await;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[server]
|
|
pub async fn delete_mcp_server(server_id: String) -> Result<(), ServerFnError> {
|
|
use mongodb::bson::doc;
|
|
|
|
let state: super::server_state::ServerState =
|
|
dioxus_fullstack::FullstackContext::extract().await?;
|
|
|
|
let oid = bson::oid::ObjectId::parse_str(&server_id)
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
state
|
|
.db
|
|
.mcp_servers()
|
|
.delete_one(doc! { "_id": oid })
|
|
.await
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[server]
|
|
pub async fn regenerate_mcp_token(server_id: String) -> Result<String, ServerFnError> {
|
|
use chrono::Utc;
|
|
use mongodb::bson::doc;
|
|
|
|
let state: super::server_state::ServerState =
|
|
dioxus_fullstack::FullstackContext::extract().await?;
|
|
|
|
let oid = bson::oid::ObjectId::parse_str(&server_id)
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
let new_token = format!("mcp_{}", uuid::Uuid::new_v4().to_string().replace('-', ""));
|
|
|
|
state
|
|
.db
|
|
.mcp_servers()
|
|
.update_one(
|
|
doc! { "_id": oid },
|
|
doc! { "$set": { "access_token": &new_token, "updated_at": Utc::now().to_rfc3339() } },
|
|
)
|
|
.await
|
|
.map_err(|e| ServerFnError::new(e.to_string()))?;
|
|
|
|
Ok(new_token)
|
|
}
|