Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
68 lines
2.2 KiB
Go
68 lines
2.2 KiB
Go
package cli
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
|
|
)
|
|
|
|
func newAuditCmd(client func() *ctlClient, asJSON *bool) *cobra.Command {
|
|
var (
|
|
since, actor, contains string
|
|
limit int
|
|
detail bool
|
|
)
|
|
c := &cobra.Command{
|
|
Use: "audit",
|
|
Short: "Who changed what: every change through the admin API and the administration pages",
|
|
Long: "Each change made with `llmgw ctl`, the console, or the pages under /admin —\n" +
|
|
"and a person's own keys and password — is recorded: who, when, through\n" +
|
|
"what, the fields it was given with anything secret taken out, and how it\n" +
|
|
"ended. The newest first.",
|
|
Example: " llmgw ctl audit --since 24h\n llmgw ctl audit --actor ilker --contains models\n llmgw ctl audit --contains bob@example.com --detail",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
q := url.Values{"since": {since}, "limit": {strconv.Itoa(limit)}}
|
|
if actor != "" {
|
|
q.Set("actor", actor)
|
|
}
|
|
if contains != "" {
|
|
q.Set("contains", contains)
|
|
}
|
|
var ev []store.AuditEvent
|
|
if err := client().get(cmd.Context(), "/api/v1/audit?"+q.Encode(), &ev); err != nil {
|
|
return err
|
|
}
|
|
if *asJSON {
|
|
return json.NewEncoder(cmd.OutOrStdout()).Encode(ev)
|
|
}
|
|
if len(ev) == 0 {
|
|
fmt.Fprintln(cmd.OutOrStdout(), "No changes recorded in that time.")
|
|
return nil
|
|
}
|
|
w := tab()
|
|
fmt.Fprintln(w, "WHEN (UTC)\tWHO\tVIA\tWHAT\tTARGET\tSTATUS")
|
|
for _, e := range ev {
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%d\n", e.At.UTC().Format("2006-01-02 15:04:05"),
|
|
e.Actor, e.Source, e.Action, dash(e.Target), e.Status)
|
|
if detail && e.Detail != "" && e.Detail != "{}" {
|
|
fmt.Fprintf(w, "\t\t\t %s\t\t\n", e.Detail)
|
|
}
|
|
}
|
|
return w.Flush()
|
|
},
|
|
}
|
|
f := c.Flags()
|
|
f.StringVar(&since, "since", "7d", "how far back: 90m, 24h, 30d")
|
|
f.StringVar(&actor, "actor", "", "only changes by this person")
|
|
f.StringVar(&contains, "contains", "", "only changes whose action or target contains this")
|
|
f.IntVar(&limit, "limit", 200, "at most this many")
|
|
f.BoolVar(&detail, "detail", false, "show the fields each change was given")
|
|
return c
|
|
}
|