refactor(#1456): extract dashboard reminder endpoints into dashboard/reminders.rs
This commit is contained in:
parent
e2fdaae841
commit
bee165ebc7
2 changed files with 62 additions and 45 deletions
|
|
@ -32,6 +32,7 @@ use crate::lifecycle::{self, MANAGER_NAME};
|
||||||
|
|
||||||
mod journal;
|
mod journal;
|
||||||
mod permissions;
|
mod permissions;
|
||||||
|
mod reminders;
|
||||||
mod schedules;
|
mod schedules;
|
||||||
mod webhook;
|
mod webhook;
|
||||||
|
|
||||||
|
|
@ -71,7 +72,7 @@ pub async fn serve(port: u16, coord: Arc<Coordinator>) -> Result<()> {
|
||||||
.route("/api/journal-host", get(journal::get_journal_host))
|
.route("/api/journal-host", get(journal::get_journal_host))
|
||||||
.route("/api/approval-diff/{id}", get(get_approval_diff))
|
.route("/api/approval-diff/{id}", get(get_approval_diff))
|
||||||
.route("/api/state-file", get(get_state_file))
|
.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/operator-inbox", get(api_operator_inbox))
|
||||||
.route("/api/stats-hive", get(api_stats_hive))
|
.route("/api/stats-hive", get(api_stats_hive))
|
||||||
.route("/api/container-resources", get(api_container_resources))
|
.route("/api/container-resources", get(api_container_resources))
|
||||||
|
|
@ -81,8 +82,11 @@ pub async fn serve(port: u16, coord: Arc<Coordinator>) -> Result<()> {
|
||||||
.route("/api/build-logs/id/{id}/stream", get(get_build_log_stream))
|
.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/build-logs/id/{id}/raw", get(get_build_log_raw))
|
||||||
.route("/api/agent/{name}/mark-all-read", post(post_mark_all_read))
|
.route("/api/agent/{name}/mark-all-read", post(post_mark_all_read))
|
||||||
.route("/cancel-reminder/{id}", post(post_cancel_reminder))
|
.route(
|
||||||
.route("/retry-reminder/{id}", post(post_retry_reminder))
|
"/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("/request-spawn", post(post_request_spawn))
|
||||||
.route("/api/topology/set-parent", post(post_set_parent))
|
.route("/api/topology/set-parent", post(post_set_parent))
|
||||||
.route("/api/topology/set-parent-bulk", post(post_set_parent_bulk))
|
.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<AppState>) -> 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
|
/// Unread operator-directed messages for the dashboard's Y3R C4LL inbox
|
||||||
/// (#1469). Returns messages addressed to `"operator"` that haven't been
|
/// (#1469). Returns messages addressed to `"operator"` that haven't been
|
||||||
/// acked yet (the operator clears them via the existing
|
/// acked yet (the operator clears them via the existing
|
||||||
|
|
@ -1901,41 +1898,6 @@ async fn get_build_log_raw(State(state): State<AppState>, AxumPath(id): AxumPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn post_cancel_reminder(
|
|
||||||
State(state): State<AppState>,
|
|
||||||
AxumPath(id): AxumPath<i64>,
|
|
||||||
) -> 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<AppState>,
|
|
||||||
AxumPath(id): AxumPath<i64>,
|
|
||||||
) -> 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
|
/// Validate that a path-param agent name conforms to the hyperhive
|
||||||
/// naming whitelist: 1-63 chars of `[a-z0-9_-]`. Rejects empty,
|
/// naming whitelist: 1-63 chars of `[a-z0-9_-]`. Rejects empty,
|
||||||
/// uppercase, slashes, dots, and any non-ASCII (incl. unicode
|
/// uppercase, slashes, dots, and any non-ASCII (incl. unicode
|
||||||
|
|
|
||||||
55
hive-c0re/src/dashboard/reminders.rs
Normal file
55
hive-c0re/src/dashboard/reminders.rs
Normal file
|
|
@ -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<AppState>) -> 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<AppState>,
|
||||||
|
AxumPath(id): AxumPath<i64>,
|
||||||
|
) -> 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<AppState>,
|
||||||
|
AxumPath(id): AxumPath<i64>,
|
||||||
|
) -> 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:#}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue