Security fixes: - Remove WS token from plaintext log output (ws_universal.rs) - Replace weak LCG PRNG with rand::thread_rng() for access key generation - Add project membership check to issue triage endpoint (prevent unauthorized AI usage) - Validate deepLinkUrl to prevent javascript: navigation (XSS defense-in-depth) Data integrity fixes: - Fix UUID truncation in AI model sync (as_u128() as i64 -> timestamp_millis) - Wrap PR cascade delete in database transaction - Add missing cascade deletes for room_message_reaction, room_message_edit_history, room_notifications - Fix N+1 query for last_commit_times (single grouped query instead of per-repo) Panic prevention: - Replace unwrap() with safe fallbacks in health/metrics endpoints (email, git-hook apps) - Replace unwrap() in access key scopes serialization - Replace expect() in tool executor result map with synthetic error - Replace expect() in log level parsing with default fallback Logic bugs: - Fix users_online metric double-decrement (decrement only when count reaches 0) - Fix Map iteration + deletion bug in universal-ws.ts onclose handler - Fix stale audioStream reference in catch block (use local stream variable) - Add missing reInit event cleanup in carousel.tsx - Fix email retry backoff integer overflow ((1 << i) as u64 -> 1u64 << i) React fixes: - Use message.id instead of index as key in message-list - Add audio stream cleanup on unmount in use-audio-recording
47 lines
1.6 KiB
Rust
47 lines
1.6 KiB
Rust
use actix_web::{HttpResponse, Result, web};
|
|
use service::AppService;
|
|
use session::Session;
|
|
|
|
use crate::ApiResponse;
|
|
|
|
#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
|
|
pub struct TriageIssueQuery {
|
|
pub issue_number: i64,
|
|
}
|
|
|
|
#[utoipa::path(
|
|
get,
|
|
path = "/api/agents/{project}/triage",
|
|
params(
|
|
("project" = String, Path, description = "Project name"),
|
|
("issue_number" = i64, Query, description = "Issue number to triage"),
|
|
),
|
|
responses(
|
|
(status = 200, description = "Issue triage result", body = ApiResponse<service::agent::issue_triage::IssueTriageResponse>),
|
|
(status = 401, description = "Unauthorized"),
|
|
(status = 404, description = "Issue not found"),
|
|
),
|
|
tag = "Agent"
|
|
)]
|
|
pub async fn triage_issue(
|
|
service: web::Data<AppService>,
|
|
session: Session,
|
|
path: web::Path<String>,
|
|
query: web::Query<TriageIssueQuery>,
|
|
) -> Result<HttpResponse, crate::error::ApiError> {
|
|
let project_name = path.into_inner();
|
|
let user_id = session.user().ok_or(crate::error::ApiError(
|
|
service::error::AppError::Unauthorized,
|
|
))?;
|
|
let project = service.utils_find_project_by_name(project_name.clone()).await?;
|
|
// Verify user has access to the project before triggering AI triage
|
|
let _member = service
|
|
.get_project_member(project.id, user_id)
|
|
.await
|
|
.map_err(|_| crate::error::ApiError(service::error::AppError::Forbidden))?;
|
|
let resp = service
|
|
.triage_issue(project_name, query.issue_number)
|
|
.await?;
|
|
Ok(crate::ApiResponse::ok(resp).to_response())
|
|
}
|