Files
backend_fiesta/services/assistantAudit.go
2026-09-23 17:26:13 +05:30

69 lines
2.5 KiB
Go

package services
import (
"context"
"log"
"nearle/models"
"nearle/repositories"
"nearle/services/tools"
)
// The database audit sink.
//
// `tools.LogAudit` writes a line and nothing else, which is enough to tail
// during a rollout and useless for the question this trail exists to answer:
// did anything ever try to read a shop it should not have? That needs rows you
// can query.
//
// ── Why it also logs ────────────────────────────────────────────────────────
//
// Both, not either. A database sink that silently stopped writing would take the
// trail with it and nothing would look wrong; the log line is the thing that
// keeps working when the table does not, and it costs one line per assistant
// call — a rate set by people typing questions, not by traffic.
//
// ── Why a write failure is swallowed ────────────────────────────────────────
//
// `AuditSink.Write` returns nothing, deliberately, so a full disk cannot take
// the assistant down. A lost row is worse in theory and better in practice than
// a merchant unable to ask where their orders are because logging broke. The
// failure is logged loudly, which is the most that can be done without giving
// the sink a veto over the product.
type DBAudit struct {
repo repositories.AssistantAuditRepository
// The log sink underneath. Kept as the fallback rather than reimplemented.
line tools.LogAudit
}
func NewDBAudit(repo repositories.AssistantAuditRepository) *DBAudit {
return &DBAudit{repo: repo}
}
func (a *DBAudit) Write(_ context.Context, entry tools.AuditEntry) {
a.line.Write(context.Background(), entry)
if a.repo == nil {
return
}
err := a.repo.Record(models.AssistantAudit{
At: entry.At,
Agent: entry.Agent,
Tool: entry.Tool,
Scope: entry.Scope,
Userid: entry.Userid,
Tenantid: entry.Tenantid,
Args: repositories.EncodeAuditArgs(entry.Args),
Outcome: entry.Outcome,
Detail: entry.Detail,
Rows: entry.Rows,
Tookms: repositories.AuditDuration(entry.Took),
})
if err != nil {
// Loud, because a trail that has quietly stopped recording is worse
// than no trail: somebody will read the empty table as "nothing
// happened" rather than as "nothing was written".
log.Printf("assistant: AUDIT ROW LOST (%s/%s %s): %v", entry.Agent, entry.Tool, entry.Outcome, err)
}
}