feat(gitea+mcp): PR 状态轮询 + poll_now agent 按钮(公网 Gitea webhook 不可达降级方案)
This commit is contained in:
parent
97b116857f
commit
6d10c71d15
@ -535,9 +535,85 @@ pub fn poll_all_repos() -> Result<(usize, usize), String> {
|
||||
crate::db::log_event("gitea_poll", "info",
|
||||
&format!("轮询 {polled} 个仓库,新采集 {ingested} 条 commit"), None);
|
||||
}
|
||||
|
||||
// PR 状态轮询:弥补公网 Gitea + 内网炼境时 webhook 不可达的场景
|
||||
let mut pr_ingested = 0usize;
|
||||
for (project_id, owner, repo, base_url, token) in &repos {
|
||||
pr_ingested += poll_pr_events(&conn, project_id, owner, repo, base_url, token);
|
||||
}
|
||||
if pr_ingested > 0 {
|
||||
crate::db::log_event("gitea_poll", "info",
|
||||
&format!("PR 轮询新采集 {pr_ingested} 条事件"), None);
|
||||
}
|
||||
|
||||
Ok((polled, ingested))
|
||||
}
|
||||
|
||||
/// 轮询单个仓库的 PR 状态,将未记录的事件写入 pr_events,返回新增条数
|
||||
fn poll_pr_events(
|
||||
conn: &rusqlite::Connection,
|
||||
project_id: &str,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
base_url: &str,
|
||||
token: &str,
|
||||
) -> usize {
|
||||
let mut count = 0;
|
||||
for state in ["open", "closed"] {
|
||||
let url = format!(
|
||||
"{base_url}/api/v1/repos/{owner}/{repo}/pulls?state={state}&limit=50&type=pullrequest"
|
||||
);
|
||||
let resp = match ureq::get(&url)
|
||||
.set("Authorization", &format!("token {token}"))
|
||||
.call()
|
||||
{
|
||||
Ok(r) => r,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let prs: serde_json::Value = match resp.into_json() {
|
||||
Ok(j) => j,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let prs = match prs.as_array() {
|
||||
Some(a) => a,
|
||||
None => continue,
|
||||
};
|
||||
for pr in prs {
|
||||
let pr_number = match pr["number"].as_i64() {
|
||||
Some(n) => n,
|
||||
None => continue,
|
||||
};
|
||||
let title = pr["title"].as_str().unwrap_or("").to_string();
|
||||
let head_branch = pr["head"]["ref"].as_str().unwrap_or("").to_string();
|
||||
let base_branch = pr["base"]["ref"].as_str().unwrap_or("").to_string();
|
||||
let author = pr["user"]["login"].as_str().unwrap_or("").to_string();
|
||||
let merged = pr["merged"].as_bool().unwrap_or(false);
|
||||
let pr_state = pr["state"].as_str().unwrap_or("open");
|
||||
|
||||
// 确定动作:已合并 > 关闭 > 开启
|
||||
let action = if merged { "merged" } else if pr_state == "closed" { "closed" } else { "opened" };
|
||||
|
||||
let existing: i64 = conn.query_row(
|
||||
"SELECT COUNT(*) FROM pr_events WHERE project_id = ?1 AND pr_number = ?2 AND action = ?3",
|
||||
params![project_id, pr_number, action],
|
||||
|r| r.get(0),
|
||||
).unwrap_or(0);
|
||||
|
||||
if existing == 0 {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
if conn.execute(
|
||||
"INSERT INTO pr_events (id, project_id, pr_number, title, action, head_branch, base_branch, author)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
|
||||
params![id, project_id, pr_number, title, action, head_branch, base_branch, author],
|
||||
).is_ok() {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/// 手动触发一次轮询(UI「立即拉取」按钮)
|
||||
#[tauri::command]
|
||||
pub fn gitea_poll_now() -> Result<serde_json::Value, String> {
|
||||
|
||||
@ -540,6 +540,15 @@ fn tools_list_result() -> Value {
|
||||
"required": ["project_id"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "poll_now",
|
||||
"description": "立即触发一次 Gitea 轮询:拉取所有绑定仓库的最新 commit 和 PR 状态写入飞轮。等价于炼境 UI「立即拉取」按钮。适用于公网 Gitea + 内网炼境(webhook 不可达)场景。",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
"required": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "setup_webhook",
|
||||
"description": "为项目重新注册 Gitea webhook(订阅 push + pull_request 事件)。等价于炼境 UI「重设」按钮。导入新项目后、或需要更新事件订阅时调用。",
|
||||
@ -651,6 +660,10 @@ async fn tools_call(group_id: &str, params: Option<&Value>) -> Value {
|
||||
let body = args.get("body").and_then(|v| v.as_str()).unwrap_or("");
|
||||
crate::commands::gitea::gitea_api_create_pull_request(pid, head, base, title, body)
|
||||
}
|
||||
"poll_now" => {
|
||||
crate::commands::gitea::poll_all_repos()
|
||||
.map(|(polled, ingested)| format!("轮询完成:{polled} 个仓库,新 commit {ingested} 条,PR 事件已同步"))
|
||||
}
|
||||
"setup_webhook" => {
|
||||
let pid = args.get("project_id").and_then(|v| v.as_str()).unwrap_or("");
|
||||
let port = read_port();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user