refactor(#1456): extract knowledge push-webhook endpoint into dashboard/webhook.rs
This commit is contained in:
parent
302738362a
commit
1255268f4f
2 changed files with 66 additions and 54 deletions
|
|
@ -32,6 +32,7 @@ use crate::lifecycle::{self, MANAGER_NAME};
|
||||||
|
|
||||||
mod permissions;
|
mod permissions;
|
||||||
mod schedules;
|
mod schedules;
|
||||||
|
mod webhook;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
|
|
@ -118,7 +119,7 @@ pub async fn serve(port: u16, coord: Arc<Coordinator>) -> Result<()> {
|
||||||
)
|
)
|
||||||
.route("/dashboard/stream", get(dashboard_stream))
|
.route("/dashboard/stream", get(dashboard_stream))
|
||||||
.route("/dashboard/history", get(dashboard_history))
|
.route("/dashboard/history", get(dashboard_history))
|
||||||
.route("/webhook/knowledge", post(post_webhook_knowledge))
|
.route("/webhook/knowledge", post(webhook::post_webhook_knowledge))
|
||||||
// Anything not matched by the dynamic routes above falls
|
// Anything not matched by the dynamic routes above falls
|
||||||
// through to the bundled dashboard dist (GET / →
|
// through to the bundled dashboard dist (GET / →
|
||||||
// dist/index.html, /favicon.svg → dist/favicon.svg,
|
// dist/index.html, /favicon.svg → dist/favicon.svg,
|
||||||
|
|
@ -2755,56 +2756,3 @@ async fn get_approval_diff(
|
||||||
fn plain_text(body: String) -> Response {
|
fn plain_text(body: String) -> Response {
|
||||||
(StatusCode::OK, body).into_response()
|
(StatusCode::OK, body).into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Minimal Forgejo push-webhook payload — only the fields we care about.
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct PushWebhookPayload {
|
|
||||||
#[serde(rename = "ref")]
|
|
||||||
git_ref: Option<String>,
|
|
||||||
repository: Option<PushWebhookRepo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct PushWebhookRepo {
|
|
||||||
full_name: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// POST `/webhook/knowledge` — Forgejo push webhook for
|
|
||||||
/// `internal/knowledge`. Runs `git pull` on the local clone so
|
|
||||||
/// agents see up-to-date documents on their next turn.
|
|
||||||
///
|
|
||||||
/// Expected Forgejo webhook configuration:
|
|
||||||
/// - URL: `http://127.0.0.1:<dashboard_port>/webhook/knowledge`
|
|
||||||
/// - Event: "Push" (fires on merge commits to main as well)
|
|
||||||
///
|
|
||||||
/// No signature verification for now; the endpoint is loopback-only
|
|
||||||
/// and only triggers a read-only `git pull` on an operator-curated repo.
|
|
||||||
async fn post_webhook_knowledge(
|
|
||||||
axum::extract::Json(payload): axum::extract::Json<PushWebhookPayload>,
|
|
||||||
) -> Response {
|
|
||||||
let expected_repo = format!("{}/{}", crate::knowledge::ORG, crate::knowledge::REPO);
|
|
||||||
let full_name = payload
|
|
||||||
.repository
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|r| r.full_name.as_deref())
|
|
||||||
.unwrap_or("");
|
|
||||||
if full_name != expected_repo {
|
|
||||||
tracing::debug!(
|
|
||||||
full_name,
|
|
||||||
"webhook/knowledge: ignoring push from unexpected repo"
|
|
||||||
);
|
|
||||||
return (StatusCode::OK, "ignored").into_response();
|
|
||||||
}
|
|
||||||
let git_ref = payload.git_ref.as_deref().unwrap_or("");
|
|
||||||
if git_ref != "refs/heads/main" {
|
|
||||||
tracing::debug!(git_ref, "webhook/knowledge: ignoring non-main push");
|
|
||||||
return (StatusCode::OK, "ignored").into_response();
|
|
||||||
}
|
|
||||||
tracing::info!("webhook/knowledge: pull triggered by push to {expected_repo}");
|
|
||||||
tokio::spawn(async {
|
|
||||||
if let Err(e) = crate::knowledge::pull().await {
|
|
||||||
tracing::warn!(error = ?e, "webhook/knowledge: pull failed");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
(StatusCode::OK, "ok").into_response()
|
|
||||||
}
|
|
||||||
|
|
|
||||||
64
hive-c0re/src/dashboard/webhook.rs
Normal file
64
hive-c0re/src/dashboard/webhook.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
//! Forgejo push-webhook endpoint for the `internal/knowledge` repo.
|
||||||
|
//!
|
||||||
|
//! Loopback-only; on a push to `main` of the knowledge repo it triggers a
|
||||||
|
//! read-only `git pull` on the local clone so agents see up-to-date
|
||||||
|
//! documents on their next turn.
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
http::StatusCode,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
/// Minimal Forgejo push-webhook payload — only the fields we care about.
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(super) struct PushWebhookPayload {
|
||||||
|
#[serde(rename = "ref")]
|
||||||
|
git_ref: Option<String>,
|
||||||
|
repository: Option<PushWebhookRepo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub(super) struct PushWebhookRepo {
|
||||||
|
full_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// POST `/webhook/knowledge` — Forgejo push webhook for
|
||||||
|
/// `internal/knowledge`. Runs `git pull` on the local clone so
|
||||||
|
/// agents see up-to-date documents on their next turn.
|
||||||
|
///
|
||||||
|
/// Expected Forgejo webhook configuration:
|
||||||
|
/// - URL: `http://127.0.0.1:<dashboard_port>/webhook/knowledge`
|
||||||
|
/// - Event: "Push" (fires on merge commits to main as well)
|
||||||
|
///
|
||||||
|
/// No signature verification for now; the endpoint is loopback-only
|
||||||
|
/// and only triggers a read-only `git pull` on an operator-curated repo.
|
||||||
|
pub(super) async fn post_webhook_knowledge(
|
||||||
|
axum::extract::Json(payload): axum::extract::Json<PushWebhookPayload>,
|
||||||
|
) -> Response {
|
||||||
|
let expected_repo = format!("{}/{}", crate::knowledge::ORG, crate::knowledge::REPO);
|
||||||
|
let full_name = payload
|
||||||
|
.repository
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|r| r.full_name.as_deref())
|
||||||
|
.unwrap_or("");
|
||||||
|
if full_name != expected_repo {
|
||||||
|
tracing::debug!(
|
||||||
|
full_name,
|
||||||
|
"webhook/knowledge: ignoring push from unexpected repo"
|
||||||
|
);
|
||||||
|
return (StatusCode::OK, "ignored").into_response();
|
||||||
|
}
|
||||||
|
let git_ref = payload.git_ref.as_deref().unwrap_or("");
|
||||||
|
if git_ref != "refs/heads/main" {
|
||||||
|
tracing::debug!(git_ref, "webhook/knowledge: ignoring non-main push");
|
||||||
|
return (StatusCode::OK, "ignored").into_response();
|
||||||
|
}
|
||||||
|
tracing::info!("webhook/knowledge: pull triggered by push to {expected_repo}");
|
||||||
|
tokio::spawn(async {
|
||||||
|
if let Err(e) = crate::knowledge::pull().await {
|
||||||
|
tracing::warn!(error = ?e, "webhook/knowledge: pull failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(StatusCode::OK, "ok").into_response()
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue