86 lines
3.1 KiB
Go
86 lines
3.1 KiB
Go
package webui
|
|
|
|
import (
|
|
"fmt"
|
|
"html"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
func (h *handler) handleTaskPage(w http.ResponseWriter, r *http.Request) {
|
|
id := r.PathValue("id")
|
|
task, ok := globalQueue.findByID(id)
|
|
if !ok {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
snapshot := *task
|
|
body := renderTaskDetailPage(h.opts, snapshot)
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
_, _ = w.Write([]byte(body))
|
|
}
|
|
|
|
func renderTaskDetailPage(opts HandlerOptions, task Task) string {
|
|
title := task.Name
|
|
if strings.TrimSpace(title) == "" {
|
|
title = task.ID
|
|
}
|
|
var body strings.Builder
|
|
body.WriteString(`<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap">`)
|
|
body.WriteString(`<a class="btn btn-secondary btn-sm" href="/tasks">Back to Tasks</a>`)
|
|
body.WriteString(`<span style="font-size:12px;color:var(--muted)">Artifacts are saved in the task folder under <code>./tasks</code>.</span>`)
|
|
body.WriteString(`</div>`)
|
|
|
|
if report := loadTaskReportFragment(task); report != "" {
|
|
body.WriteString(report)
|
|
} else {
|
|
body.WriteString(`<div class="card"><div class="card-head">Task Summary</div><div class="card-body">`)
|
|
body.WriteString(`<div style="font-size:18px;font-weight:700">` + html.EscapeString(title) + `</div>`)
|
|
body.WriteString(`<div style="margin-top:8px">` + renderTaskStatusBadge(task.Status) + `</div>`)
|
|
if strings.TrimSpace(task.ErrMsg) != "" {
|
|
body.WriteString(`<div style="margin-top:8px;color:var(--crit-fg)">` + html.EscapeString(task.ErrMsg) + `</div>`)
|
|
}
|
|
body.WriteString(`</div></div>`)
|
|
}
|
|
|
|
if task.Status == TaskRunning || task.Status == TaskPending {
|
|
body.WriteString(`<div class="card"><div class="card-head">Live Logs</div><div class="card-body">`)
|
|
body.WriteString(`<div id="task-live-log" class="terminal" style="max-height:none;white-space:pre-wrap">Connecting...</div>`)
|
|
body.WriteString(`</div></div>`)
|
|
body.WriteString(`<script>
|
|
var _taskDetailES = new EventSource('/api/tasks/` + html.EscapeString(task.ID) + `/stream');
|
|
var _taskDetailTerm = document.getElementById('task-live-log');
|
|
_taskDetailES.onopen = function(){ _taskDetailTerm.textContent = ''; };
|
|
_taskDetailES.onmessage = function(e){ _taskDetailTerm.textContent += e.data + "\n"; _taskDetailTerm.scrollTop = _taskDetailTerm.scrollHeight; };
|
|
_taskDetailES.addEventListener('done', function(){ _taskDetailES.close(); setTimeout(function(){ window.location.reload(); }, 1000); });
|
|
_taskDetailES.onerror = function(){ _taskDetailES.close(); };
|
|
</script>`)
|
|
}
|
|
|
|
return layoutHead(opts.Title+" — "+title) +
|
|
layoutNav("tasks", opts.BuildLabel) +
|
|
`<div class="main"><div class="topbar"><h1>` + html.EscapeString(title) + `</h1></div><div class="content">` +
|
|
body.String() +
|
|
`</div></div></body></html>`
|
|
}
|
|
|
|
func loadTaskReportFragment(task Task) string {
|
|
if strings.TrimSpace(task.ReportHTMLPath) == "" {
|
|
return ""
|
|
}
|
|
data, err := os.ReadFile(task.ReportHTMLPath)
|
|
if err != nil || len(data) == 0 {
|
|
return ""
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func taskArtifactDownloadLink(task Task, absPath string) string {
|
|
if strings.TrimSpace(absPath) == "" {
|
|
return ""
|
|
}
|
|
return fmt.Sprintf(`/export/file?path=%s`, absPath)
|
|
}
|