ship: v0.1 Foundation milestone complete (#1)

This commit was merged in pull request #1.
This commit is contained in:
2026-06-03 20:08:57 +00:00
parent 55aae5347e
commit be9afa2d2c
59 changed files with 4152 additions and 36 deletions
+81
View File
@@ -0,0 +1,81 @@
package store
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"time"
)
type AuditEntry struct {
ID int64 `json:"id"`
Timestamp time.Time `json:"timestamp"`
Actor string `json:"actor"`
Action string `json:"action"`
Resource string `json:"resource"`
Result string `json:"result"`
Error string `json:"error,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
type AuditRepo struct {
db *sql.DB
}
func NewAuditRepo(db *sql.DB) *AuditRepo {
return &AuditRepo{db: db}
}
func (r *AuditRepo) Append(ctx context.Context, e *AuditEntry) error {
if e.Timestamp.IsZero() {
e.Timestamp = time.Now().UTC()
}
if e.Actor == "" {
e.Actor = "system"
}
metaJSON, _ := json.Marshal(e.Metadata)
if e.Error == "" {
_, err := r.db.ExecContext(ctx,
`INSERT INTO audit_log (timestamp, actor, action, resource, result, metadata) VALUES (?, ?, ?, ?, ?, ?)`,
e.Timestamp, e.Actor, e.Action, e.Resource, e.Result, string(metaJSON))
if err != nil {
return fmt.Errorf("insert audit: %w", err)
}
return nil
}
_, err := r.db.ExecContext(ctx,
`INSERT INTO audit_log (timestamp, actor, action, resource, result, error, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)`,
e.Timestamp, e.Actor, e.Action, e.Resource, e.Result, e.Error, string(metaJSON))
if err != nil {
return fmt.Errorf("insert audit (with error): %w", err)
}
return nil
}
func (r *AuditRepo) List(ctx context.Context, limit int) ([]*AuditEntry, error) {
if limit <= 0 {
limit = 100
}
rows, err := r.db.QueryContext(ctx,
`SELECT id, timestamp, actor, action, resource, result, COALESCE(error, ''), COALESCE(metadata, '') FROM audit_log ORDER BY id DESC LIMIT ?`, limit)
if err != nil {
return nil, fmt.Errorf("list audit: %w", err)
}
defer rows.Close()
var entries []*AuditEntry
for rows.Next() {
var (
e AuditEntry
metaJSON string
)
if err := rows.Scan(&e.ID, &e.Timestamp, &e.Actor, &e.Action, &e.Resource, &e.Result, &e.Error, &metaJSON); err != nil {
return nil, fmt.Errorf("scan audit: %w", err)
}
if metaJSON != "" {
_ = json.Unmarshal([]byte(metaJSON), &e.Metadata)
}
entries = append(entries, &e)
}
return entries, rows.Err()
}