From bee165ebc79a253f61754f0d93ce9f3a39255180 Mon Sep 17 00:00:00 2001 From: damocles Date: Mon, 8 Jun 2026 22:47:33 +0200 Subject: [PATCH] refactor(#1456): extract dashboard reminder endpoints into dashboard/reminders.rs --- hive-c0re/src/dashboard.rs | 52 ++++---------------------- hive-c0re/src/dashboard/reminders.rs | 55 ++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 45 deletions(-) create mode 100644 hive-c0re/src/dashboard/reminders.rs diff --git a/hive-c0re/src/dashboard.rs b/hive-c0re/src/dashboard.rs index 3d2ddd75..51deb00c 100644 --- a/hive-c0re/src/dashboard.rs +++ b/hive-c0re/src/dashboard.rs @@ -32,6 +32,7 @@ use crate::lifecycle::{self, MANAGER_NAME}; mod journal; mod permissions; +mod reminders; mod schedules; mod webhook; @@ -71,7 +72,7 @@ pub async fn serve(port: u16, coord: Arc) -> Result<()> { .route("/api/journal-host", get(journal::get_journal_host)) .route("/api/approval-diff/{id}", get(get_approval_diff)) .route("/api/state-file", get(get_state_file)) - .route("/api/reminders", get(api_reminders)) + .route("/api/reminders", get(reminders::api_reminders)) .route("/api/operator-inbox", get(api_operator_inbox)) .route("/api/stats-hive", get(api_stats_hive)) .route("/api/container-resources", get(api_container_resources)) @@ -81,8 +82,11 @@ pub async fn serve(port: u16, coord: Arc) -> Result<()> { .route("/api/build-logs/id/{id}/stream", get(get_build_log_stream)) .route("/api/build-logs/id/{id}/raw", get(get_build_log_raw)) .route("/api/agent/{name}/mark-all-read", post(post_mark_all_read)) - .route("/cancel-reminder/{id}", post(post_cancel_reminder)) - .route("/retry-reminder/{id}", post(post_retry_reminder)) + .route( + "/cancel-reminder/{id}", + post(reminders::post_cancel_reminder), + ) + .route("/retry-reminder/{id}", post(reminders::post_retry_reminder)) .route("/request-spawn", post(post_request_spawn)) .route("/api/topology/set-parent", post(post_set_parent)) .route("/api/topology/set-parent-bulk", post(post_set_parent_bulk)) @@ -1637,13 +1641,6 @@ fn image_content_type(path: &Path) -> Option<&'static str> { }) } -async fn api_reminders(State(state): State) -> Response { - match state.coord.broker.list_pending_reminders() { - Ok(rows) => axum::Json(rows).into_response(), - Err(e) => error_response(&format!("reminders: {e:#}")), - } -} - /// Unread operator-directed messages for the dashboard's Y3R C4LL inbox /// (#1469). Returns messages addressed to `"operator"` that haven't been /// acked yet (the operator clears them via the existing @@ -1901,41 +1898,6 @@ async fn get_build_log_raw(State(state): State, AxumPath(id): AxumPath } } -async fn post_cancel_reminder( - State(state): State, - AxumPath(id): AxumPath, -) -> Response { - match state.coord.broker.cancel_reminder(id) { - Ok(0) => error_response(&format!("reminder {id} not pending (already delivered?)")), - Ok(_) => { - tracing::info!(%id, "operator cancelled reminder"); - state.coord.emit_reminders_snapshot(); - (StatusCode::OK, "ok").into_response() - } - Err(e) => error_response(&format!("cancel reminder {id} failed: {e:#}")), - } -} - -/// Reset a pending reminder's failure state so the scheduler -/// retries it on the next tick. Useful when the failure was -/// transient (sqlite lock contention, disk full → freed up) and -/// the operator wants delivery to resume immediately instead of -/// the row sitting in attempt-count-capped purgatory. -async fn post_retry_reminder( - State(state): State, - AxumPath(id): AxumPath, -) -> Response { - match state.coord.broker.reset_reminder_failure(id) { - Ok(0) => error_response(&format!("reminder {id} not pending (already delivered?)")), - Ok(_) => { - tracing::info!(%id, "operator reset reminder failure for retry"); - state.coord.emit_reminders_snapshot(); - (StatusCode::OK, "ok").into_response() - } - Err(e) => error_response(&format!("retry reminder {id} failed: {e:#}")), - } -} - /// Validate that a path-param agent name conforms to the hyperhive /// naming whitelist: 1-63 chars of `[a-z0-9_-]`. Rejects empty, /// uppercase, slashes, dots, and any non-ASCII (incl. unicode diff --git a/hive-c0re/src/dashboard/reminders.rs b/hive-c0re/src/dashboard/reminders.rs new file mode 100644 index 00000000..e29e885c --- /dev/null +++ b/hive-c0re/src/dashboard/reminders.rs @@ -0,0 +1,55 @@ +//! Reminder endpoints for the dashboard. +//! +//! Lists pending reminders for the reminders tab, and lets the operator +//! cancel a pending reminder or reset its failure state so the scheduler +//! retries it on the next tick. + +use axum::{ + extract::{Path as AxumPath, State}, + http::StatusCode, + response::{IntoResponse, Response}, +}; + +use super::{AppState, error_response}; + +pub(super) async fn api_reminders(State(state): State) -> Response { + match state.coord.broker.list_pending_reminders() { + Ok(rows) => axum::Json(rows).into_response(), + Err(e) => error_response(&format!("reminders: {e:#}")), + } +} + +pub(super) async fn post_cancel_reminder( + State(state): State, + AxumPath(id): AxumPath, +) -> Response { + match state.coord.broker.cancel_reminder(id) { + Ok(0) => error_response(&format!("reminder {id} not pending (already delivered?)")), + Ok(_) => { + tracing::info!(%id, "operator cancelled reminder"); + state.coord.emit_reminders_snapshot(); + (StatusCode::OK, "ok").into_response() + } + Err(e) => error_response(&format!("cancel reminder {id} failed: {e:#}")), + } +} + +/// Reset a pending reminder's failure state so the scheduler +/// retries it on the next tick. Useful when the failure was +/// transient (sqlite lock contention, disk full → freed up) and +/// the operator wants delivery to resume immediately instead of +/// the row sitting in attempt-count-capped purgatory. +pub(super) async fn post_retry_reminder( + State(state): State, + AxumPath(id): AxumPath, +) -> Response { + match state.coord.broker.reset_reminder_failure(id) { + Ok(0) => error_response(&format!("reminder {id} not pending (already delivered?)")), + Ok(_) => { + tracing::info!(%id, "operator reset reminder failure for retry"); + state.coord.emit_reminders_snapshot(); + (StatusCode::OK, "ok").into_response() + } + Err(e) => error_response(&format!("retry reminder {id} failed: {e:#}")), + } +}