Files
llmgw/internal/web/admin_guard.go
T

109 lines
3.4 KiB
Go

package web
import (
"context"
"net/http"
"slices"
"time"
"gitea.mynodes.xyz/ilker/llmgw/internal/guard"
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
)
// GuardAdminSource is what the guardrails page reads.
type GuardAdminSource interface {
GuardEvents(ctx context.Context, since time.Time, limit int) ([]store.GuardEvent, error)
GuardCounts(ctx context.Context, since time.Time) ([]store.GuardCount, error)
GuardAttachments(ctx context.Context) ([]guard.Attachment, error)
AllProjects(ctx context.Context) ([]store.ProjectPath, error)
}
type guardPageData struct {
Nav Nav
Version string
Window string
Windows []string
// Policies are the library, Default those applied to every request.
Policies []string
Default []string
Attachments []guardAttachmentRow
Counts []guardCountRow
Events []guardEventRow
Error string
}
type guardAttachmentRow struct{ Scope, Who, Policy string }
type guardCountRow struct {
Where, Action, Policy, Category string
Count int64
}
type guardEventRow struct {
At, RequestID, Where, Model, Action, Policy, Category string
Status int
Ms int64
}
var guardWindows = map[string]time.Duration{"24h": 24 * time.Hour, "7d": 7 * 24 * time.Hour, "30d": 30 * 24 * time.Hour}
// RegisterAdminGuard mounts /admin/guard: what the guardrails did, where, by
// which policy — names and counts, never text.
func (p *AccountPages) RegisterAdminGuard(mux *http.ServeMux, s GuardAdminSource, library guard.Config) {
p.hasGuard = true
mux.HandleFunc("GET /admin/guard", p.adminOnly(func(w http.ResponseWriter, r *http.Request, admin store.User) {
ctx := r.Context()
d := guardPageData{Nav: p.navFor(r, admin, "guard"), Version: p.version, Window: r.URL.Query().Get("window"),
Windows: []string{"24h", "7d", "30d"}, Default: library.Default}
since, ok := guardWindows[d.Window]
if !ok {
d.Window, since = "7d", guardWindows["7d"]
}
for name := range library.Policies {
d.Policies = append(d.Policies, name)
}
slices.Sort(d.Policies)
where := map[string]string{"": "personal"}
if projects, err := s.AllProjects(ctx); err == nil {
for _, pp := range projects {
where[pp.ID] = pp.Path
}
}
name := func(id string) string {
if n, ok := where[id]; ok {
return n
}
return id
}
if att, err := s.GuardAttachments(ctx); err == nil {
for _, a := range att {
who := a.ScopeID
if a.Scope == "project" {
who = name(a.ScopeID)
}
d.Attachments = append(d.Attachments, guardAttachmentRow{Scope: a.Scope, Who: who, Policy: a.Policy})
}
}
counts, err := s.GuardCounts(ctx, time.Now().Add(-since))
if err != nil {
d.Error = err.Error()
}
for _, c := range counts {
d.Counts = append(d.Counts, guardCountRow{Where: name(c.ProjectID), Action: c.Action,
Policy: c.Policy, Category: c.Category, Count: c.Count})
}
events, err := s.GuardEvents(ctx, time.Now().Add(-since), 100)
if err != nil {
d.Error = err.Error()
}
for _, e := range events {
d.Events = append(d.Events, guardEventRow{At: e.At.UTC().Format("2006-01-02 15:04:05"),
RequestID: e.RequestID, Where: name(e.ProjectID), Model: e.Model, Action: e.Action,
Policy: e.Policy, Category: e.Category, Status: e.Status, Ms: e.GuardMs})
}
noStore(w)
render(w, "admin_guard", d)
}))
}