feat(#2591): add GET /api/build-log/<node_id> {,/raw} query endpoints

The raw-graph wire drops the inline build_log_id; the client fetches a
node's captured output on demand. Two handlers resolve node id -> log-row
id (JobQueue::build_log_id_of, now keyed by the wire u64) then delegate to
the existing get_full / raw handlers: /api/build-log/<node_id> serves the
BuildLogFull JSON ({stdout, stderr} + header), /raw serves text/plain.
404 when the node has no linked log.
This commit is contained in:
atlas 2026-07-23 15:24:04 +02:00 committed by mara
commit 52dd9ede23
4 changed files with 55 additions and 7 deletions

View file

@ -90,6 +90,42 @@ pub(super) async fn get_build_log_full(
}
}
/// `GET /api/build-log/{node_id}` — the build log for a **queue node**,
/// resolved node id → log-row id → full log. Same `BuildLogFull` JSON
/// (`stdout` / `stderr` + header) as `get_build_log_full`; HTTP 404 when the
/// node has no linked log (the client gates the request on `NodeView.has_log`,
/// but a vacuum race can still 404). This is the on-demand log fetch the
/// raw-graph dashboard uses instead of an inline `build_log_id` on the wire.
pub(super) async fn get_build_log_for_node(
State(state): State<AppState>,
AxumPath(node_id): AxumPath<u64>,
) -> Response {
match state.coord.job_queue.build_log_id_of(node_id) {
Some(log_id) => get_build_log_full(State(state), AxumPath(log_id)).await,
None => (
StatusCode::NOT_FOUND,
format!("node #{node_id} has no build log"),
)
.into_response(),
}
}
/// `GET /api/build-log/{node_id}/raw` — the node's build log as `text/plain`
/// for download (delegates to `get_build_log_raw` after resolving the node id).
pub(super) async fn get_build_log_raw_for_node(
State(state): State<AppState>,
AxumPath(node_id): AxumPath<u64>,
) -> Response {
match state.coord.job_queue.build_log_id_of(node_id) {
Some(log_id) => get_build_log_raw(State(state), AxumPath(log_id)).await,
None => (
StatusCode::NOT_FOUND,
format!("node #{node_id} has no build log"),
)
.into_response(),
}
}
/// JSON frame sent on the `/api/build-logs/id/{id}/stream` SSE channel.
/// `stdout_append` / `stderr_append` carry only the new bytes since the
/// last frame; `done = true` means the build finished and the stream

View file

@ -100,6 +100,14 @@ pub async fn serve(
)
.route("/api/audit-log", get(misc_api::api_audit_log))
.route("/api/build-logs", get(build_logs::get_build_logs_all))
.route(
"/api/build-log/{node_id}",
get(build_logs::get_build_log_for_node),
)
.route(
"/api/build-log/{node_id}/raw",
get(build_logs::get_build_log_raw_for_node),
)
.route(
"/api/build-logs/{agent}",
get(build_logs::get_build_logs_agent),