package audit import ( "encoding/json" "fmt" "os" "path/filepath" "time" "github.com/bams/backend/internal/config" "github.com/bams/backend/internal/logger" ) type AuditEntry struct { Timestamp time.Time `json:"timestamp"` User string `json:"user"` Action string `json:"action"` Resource string `json:"resource"` Result string `json:"result"` // "success" or "failure" Details string `json:"details,omitempty"` } type Service struct { config *config.Config logger *logger.Logger logFile *os.File } func NewService(cfg *config.Config, log *logger.Logger) *Service { auditDir := filepath.Join(cfg.DataDir, "audit") os.MkdirAll(auditDir, 0755) logPath := filepath.Join(auditDir, fmt.Sprintf("audit-%s.log", time.Now().Format("2006-01-02"))) file, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0640) if err != nil { log.Error("Failed to open audit log", "error", err) } return &Service{ config: cfg, logger: log, logFile: file, } } func (s *Service) Log(user, action, resource, result, details string) { entry := AuditEntry{ Timestamp: time.Now(), User: user, Action: action, Resource: resource, Result: result, Details: details, } data, err := json.Marshal(entry) if err != nil { s.logger.Error("Failed to marshal audit entry", "error", err) return } if s.logFile != nil { s.logFile.WriteString(string(data) + "\n") s.logFile.Sync() } s.logger.Info("Audit log", "user", user, "action", action, "resource", resource, "result", result) } func (s *Service) Close() { if s.logFile != nil { s.logFile.Close() } }