Files

87 lines
1.8 KiB
Go

package admin
import (
"log/slog"
"os"
)
const importAuditIDCap = 20
func defaultAuditLogger() *slog.Logger {
return slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelInfo}))
}
type auditRec struct {
Actor string
TokenID string
Action string
Object string
Result string
IP string
Detail any
}
// audit 写结构化操作日志;不写密码、令牌和正文。
func (h *Handler) audit(actor, action, object, result, ip string) {
h.auditRec(auditRec{Actor: actor, Action: action, Object: object, Result: result, IP: ip})
}
func (h *Handler) auditP(p principal, action, object, result, ip string) {
h.auditPD(p, action, object, result, ip, nil)
}
func (h *Handler) auditPD(p principal, action, object, result, ip string, detail any) {
rec := auditRec{
Actor: actorString(p),
Action: action,
Object: object,
Result: result,
IP: ip,
Detail: detail,
}
if p.Kind == authToken && p.TokenID != "" {
rec.TokenID = p.TokenID
}
h.auditRec(rec)
}
func (h *Handler) auditRec(rec auditRec) {
args := []any{
"actor", rec.Actor,
"action", rec.Action,
"object", rec.Object,
"result", rec.Result,
"ip", rec.IP,
}
if rec.TokenID != "" {
args = append(args, "token_id", rec.TokenID)
}
if rec.Detail != nil {
args = append(args, "detail", rec.Detail)
}
h.auditLog.Info("admin_audit", args...)
}
func (h *Handler) auditAuthFail(ip, reason string) {
h.auditLog.Info("admin_auth_fail", "ip", ip, "reason", reason)
}
func batchAuditResult(okN, failN int) string {
switch {
case failN == 0:
return "ok"
case okN == 0:
return "failed"
default:
return "partial"
}
}
func importAuditDetail(ids []string) map[string]any {
shown := ids
if len(ids) > importAuditIDCap {
shown = ids[:importAuditIDCap]
}
return map[string]any{"ids": shown, "total": len(ids)}
}