P3 follow-up: log download (txt + ndjson) on the live job page
The diff job's full output streams to the standard live job log page,
which can be a lot of text the operator wants to grep through or paste
into a ticket. Add a Download button.
Source of truth is the persisted job_logs table — works any time
(running or finished) and doesn't need to pause the live WS stream.
The download is 'everything the server has up to right now'; if the
operator wants a fuller snapshot of a still-running job, they hit
Download again.
- New endpoint GET /api/jobs/{id}/log.{txt,ndjson} (chi {format}
matcher constrained to the two known suffixes). Auth via session
cookie. 404 on unknown job.
- internal/server/http/job_download.go writeLogsText emits a small
header + 'HH:MM:SS.mmm TAG payload' rows mirroring what the live
page shows. writeLogsNDJSON emits one self-contained {seq,ts,stream,
payload} JSON object per line — appending stays valid (each line
stands alone), and the whole file pipes cleanly into jq. NDJSON is
newline-delimited JSON; not the same as a JSON array.
- web/templates/pages/job_detail.html grows two header buttons:
'Download log' (txt) + '.ndjson' ghost variant for tooling.
Tests cover the txt format (header + per-row shape), the ndjson
format (each line round-trips through json.Unmarshal), unknown job
404, unauthenticated 401.
This commit is contained in:
@@ -304,6 +304,11 @@ func (s *Server) routes(r chi.Router) {
|
||||
if s.deps.JobHub != nil {
|
||||
r.Get("/api/jobs/{id}/stream", s.handleJobStream)
|
||||
}
|
||||
|
||||
// Job log download (txt + ndjson). Source of truth is the
|
||||
// persisted job_logs table; safe to call any time, no pause
|
||||
// needed against the live stream.
|
||||
r.Get("/api/jobs/{id}/log.{format:txt|ndjson}", s.handleJobLogDownload)
|
||||
}
|
||||
|
||||
// Start begins listening. Blocks until ListenAndServe returns
|
||||
|
||||
Reference in New Issue
Block a user