generated from coulomb/repo-seed
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
package audit
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/netkingdom/flex-auth/pkg/api"
|
|
)
|
|
|
|
// JSONLDecisionLog persists compact decision envelopes for local development.
|
|
type JSONLDecisionLog struct {
|
|
path string
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewJSONLDecisionLog returns a JSONL-backed decision log.
|
|
func NewJSONLDecisionLog(path string) *JSONLDecisionLog {
|
|
return &JSONLDecisionLog{path: path}
|
|
}
|
|
|
|
// Append writes one decision envelope as one JSON line.
|
|
func (l *JSONLDecisionLog) Append(decision api.DecisionEnvelope) error {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
if err := os.MkdirAll(filepath.Dir(l.path), 0o755); err != nil {
|
|
return fmt.Errorf("create decision log directory: %w", err)
|
|
}
|
|
|
|
file, err := os.OpenFile(l.path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
|
if err != nil {
|
|
return fmt.Errorf("open decision log: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
data, err := json.Marshal(decision)
|
|
if err != nil {
|
|
return fmt.Errorf("marshal decision envelope: %w", err)
|
|
}
|
|
if _, err := file.Write(append(data, '\n')); err != nil {
|
|
return fmt.Errorf("write decision log: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ReadAll returns every decision envelope from the log in file order.
|
|
func (l *JSONLDecisionLog) ReadAll() ([]api.DecisionEnvelope, error) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
file, err := os.Open(l.path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, nil
|
|
}
|
|
return nil, fmt.Errorf("open decision log: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
var decisions []api.DecisionEnvelope
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
if scanner.Text() == "" {
|
|
continue
|
|
}
|
|
var decision api.DecisionEnvelope
|
|
if err := json.Unmarshal(scanner.Bytes(), &decision); err != nil {
|
|
return nil, fmt.Errorf("unmarshal decision log line %d: %w", len(decisions)+1, err)
|
|
}
|
|
decisions = append(decisions, decision)
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, fmt.Errorf("read decision log: %w", err)
|
|
}
|
|
return decisions, nil
|
|
}
|
|
|
|
// Find returns one decision envelope by id.
|
|
func (l *JSONLDecisionLog) Find(id string) (api.DecisionEnvelope, bool, error) {
|
|
decisions, err := l.ReadAll()
|
|
if err != nil {
|
|
return api.DecisionEnvelope{}, false, err
|
|
}
|
|
for _, decision := range decisions {
|
|
if decision.ID == id {
|
|
return decision, true, nil
|
|
}
|
|
}
|
|
return api.DecisionEnvelope{}, false, nil
|
|
}
|