Add graph explorer components, API handlers, and dependency updates
Adds code inspector, file tree components, graph visualization JS, graph API handlers, sidebar navigation updates, and misc improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -47,9 +47,10 @@ pub async fn get_graph(
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
let (nodes, edges) = if let Some(ref b) = build {
|
||||
let build_id = b.id.map(|oid| oid.to_hex()).unwrap_or_default();
|
||||
let filter = doc! { "repo_id": &repo_id, "graph_build_id": &build_id };
|
||||
let (nodes, edges) = if build.is_some() {
|
||||
// Filter by repo_id only — delete_repo_graph clears old data before each rebuild,
|
||||
// so there is only one set of nodes/edges per repo.
|
||||
let filter = doc! { "repo_id": &repo_id };
|
||||
|
||||
let nodes: Vec<CodeNode> = match db.graph_nodes().find(filter.clone()).await {
|
||||
Ok(cursor) => collect_cursor_async(cursor).await,
|
||||
@@ -198,6 +199,72 @@ pub async fn search_symbols(
|
||||
}))
|
||||
}
|
||||
|
||||
/// GET /api/v1/graph/:repo_id/file-content — Read source file from cloned repo
|
||||
pub async fn get_file_content(
|
||||
Extension(agent): AgentExt,
|
||||
Path(repo_id): Path<String>,
|
||||
Query(params): Query<FileContentParams>,
|
||||
) -> Result<Json<ApiResponse<FileContent>>, StatusCode> {
|
||||
let db = &agent.db;
|
||||
|
||||
// Look up the repository to get repo name
|
||||
let repo = db
|
||||
.repositories()
|
||||
.find_one(doc! { "_id": mongodb::bson::oid::ObjectId::parse_str(&repo_id).ok() })
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
let base_path = std::path::Path::new(&agent.config.git_clone_base_path);
|
||||
let file_path = base_path.join(&repo.name).join(¶ms.path);
|
||||
|
||||
// Security: ensure we don't escape the repo directory
|
||||
let canonical = file_path
|
||||
.canonicalize()
|
||||
.map_err(|_| StatusCode::NOT_FOUND)?;
|
||||
let base_canonical = base_path
|
||||
.join(&repo.name)
|
||||
.canonicalize()
|
||||
.map_err(|_| StatusCode::NOT_FOUND)?;
|
||||
if !canonical.starts_with(&base_canonical) {
|
||||
return Err(StatusCode::FORBIDDEN);
|
||||
}
|
||||
|
||||
let content = std::fs::read_to_string(&canonical).map_err(|_| StatusCode::NOT_FOUND)?;
|
||||
|
||||
// Cap at 10,000 lines
|
||||
let truncated: String = content.lines().take(10_000).collect::<Vec<_>>().join("\n");
|
||||
|
||||
let language = params
|
||||
.path
|
||||
.rsplit('.')
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
|
||||
Ok(Json(ApiResponse {
|
||||
data: FileContent {
|
||||
content: truncated,
|
||||
path: params.path,
|
||||
language,
|
||||
},
|
||||
total: None,
|
||||
page: None,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FileContentParams {
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FileContent {
|
||||
pub content: String,
|
||||
pub path: String,
|
||||
pub language: String,
|
||||
}
|
||||
|
||||
/// POST /api/v1/graph/:repo_id/build — Trigger graph rebuild
|
||||
pub async fn trigger_build(
|
||||
Extension(agent): AgentExt,
|
||||
|
||||
@@ -43,6 +43,10 @@ pub fn build_router() -> Router {
|
||||
"/api/v1/graph/{repo_id}/search",
|
||||
get(handlers::graph::search_symbols),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/graph/{repo_id}/file-content",
|
||||
get(handlers::graph::get_file_content),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/graph/{repo_id}/build",
|
||||
post(handlers::graph::trigger_build),
|
||||
|
||||
@@ -69,7 +69,19 @@ pub async fn triage_findings(
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
if let Ok(result) = serde_json::from_str::<TriageResult>(&response) {
|
||||
// Strip markdown code fences if present (e.g. ```json ... ```)
|
||||
let cleaned = response.trim();
|
||||
let cleaned = if cleaned.starts_with("```") {
|
||||
let inner = cleaned
|
||||
.trim_start_matches("```json")
|
||||
.trim_start_matches("```")
|
||||
.trim_end_matches("```")
|
||||
.trim();
|
||||
inner
|
||||
} else {
|
||||
cleaned
|
||||
};
|
||||
if let Ok(result) = serde_json::from_str::<TriageResult>(cleaned) {
|
||||
finding.confidence = Some(result.confidence);
|
||||
if let Some(remediation) = result.remediation {
|
||||
finding.remediation = Some(remediation);
|
||||
|
||||
Reference in New Issue
Block a user