Files
llmgw/internal/cli/tui_audit.go
T

72 lines
2.5 KiB
Go

package cli
import (
"fmt"
"strconv"
"strings"
"time"
tea "github.com/charmbracelet/bubbletea"
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
)
// The audit view: who changed what, newest first — the same as `llmgw ctl
// audit`.
func (m consoleModel) auditPanel(pw int) (string, string, string) {
keys := "→ or Enter open · ↑↓ choose · Q leave"
if m.inPanel {
keys = "↑↓ row · Enter what it was given · ← back · Q leave"
}
head := "\n " + sDim.Render("Every change through ctl, this console and the /admin pages, the last week.") + "\n\n"
var rows [][]string
for _, e := range m.data.audit {
status := strconv.Itoa(e.Status)
if e.Status >= 400 {
status = sBad.Render(status)
}
rows = append(rows, []string{ageShort(e.At), e.Actor, e.Source, e.Action, dash(e.Target), status})
}
if len(rows) == 0 {
return "Audit", head + " " + sDim.Render("Nothing changed in the last week, or this control plane has no store."), keys
}
return "Audit", head + table([]string{"WHEN", "WHO", "VIA", "WHAT", "TARGET", "STATUS"}, rows, m.row, pw, m.inPanel), keys
}
// auditDialog is one change in full.
func auditDialog(e store.AuditEvent) (string, string) {
var b strings.Builder
fmt.Fprintf(&b, "%s, by %s through %s\nstatus %d\n", e.At.Format(time.DateTime), e.Actor, e.Source, e.Status)
if e.Target != "" {
fmt.Fprintf(&b, "target %s\n", e.Target)
}
if e.Detail != "" && e.Detail != "{}" {
fmt.Fprintf(&b, "\n%s\n", wrap(e.Detail, 80, ""))
}
b.WriteString("\n" + sDim.Render("any key"))
return e.Action, b.String()
}
// usageExportForm writes usage to a CSV file on this machine, as `llmgw ctl
// usage export` does.
func (m *consoleModel) usageExportForm() {
now := time.Now().UTC()
first := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC)
m.openForm(&formDialog{title: "Export usage as CSV",
fields: []formField{
textF("from", first.AddDate(0, -1, 0).Format("2006-01-02"), "first day"),
textF("to", first.Format("2006-01-02"), "the day after the last"),
textF("period", "day", "hour, day or month"),
textF("by", "project,model", "org, team, project, user, key, model"),
textF("file", "/tmp/llmgw-usage-"+first.AddDate(0, -1, 0).Format("2006-01")+".csv", "written on this machine"),
},
submit: func(m *consoleModel, v []string) (tea.Cmd, error) {
if v[4] == "" {
return nil, fmt.Errorf("a file to write")
}
return m.runCLI("Export usage", "ctl", "usage", "export", "--from", v[0], "--to", v[1],
"--period", v[2], "--by", v[3], "--output", v[4]), nil
}})
}