summaryrefslogtreecommitdiff
path: root/internal/store/loot.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/store/loot.go')
-rw-r--r--internal/store/loot.go103
1 files changed, 103 insertions, 0 deletions
diff --git a/internal/store/loot.go b/internal/store/loot.go
new file mode 100644
index 0000000..c610db1
--- /dev/null
+++ b/internal/store/loot.go
@@ -0,0 +1,103 @@
+package store
+
+import (
+ "fmt"
+ "time"
+)
+
+type LootEntry struct {
+ ID int64
+ Title string
+ Severity string
+ Notes string
+ HistoryID *uint64
+ RawRequest string
+ RawResponse string
+ CreatedAt time.Time
+}
+
+func (s *Store) AllLoot() ([]LootEntry, error) {
+ rows, err := s.db.Query(`
+ SELECT id, title, severity, notes, history_id, raw_request, raw_response, created_at
+ FROM loot ORDER BY
+ CASE severity
+ WHEN 'Critical' THEN 1
+ WHEN 'High' THEN 2
+ WHEN 'Medium' THEN 3
+ WHEN 'Low' THEN 4
+ WHEN 'Info' THEN 5
+ ELSE 6
+ END ASC, id DESC`)
+ if err != nil {
+ return nil, fmt.Errorf("store: query loot: %w", err)
+ }
+ defer rows.Close()
+
+ var entries []LootEntry
+ for rows.Next() {
+ var entry LootEntry
+ var histID *int64
+ var timestampStr string
+ if err := rows.Scan(&entry.ID, &entry.Title, &entry.Severity, &entry.Notes, &histID, &entry.RawRequest, &entry.RawResponse, &timestampStr); err != nil {
+ return nil, fmt.Errorf("store: scan loot: %w", err)
+ }
+ if histID != nil {
+ histIDUint := uint64(*histID)
+ entry.HistoryID = &histIDUint
+ }
+ entry.CreatedAt, _ = time.Parse(time.RFC3339, timestampStr)
+ entries = append(entries, entry)
+ }
+ return entries, rows.Err()
+}
+
+func (s *Store) AddLoot(entry LootEntry) (int64, error) {
+ var id int64
+ err := s.write(func() error {
+ var histID any
+ if entry.HistoryID != nil {
+ histID = *entry.HistoryID
+ }
+ result, err := s.db.Exec(`
+ INSERT INTO loot (title, severity, notes, history_id, raw_request, raw_response, created_at)
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
+ entry.Title, entry.Severity, entry.Notes, histID,
+ entry.RawRequest, entry.RawResponse,
+ time.Now().UTC().Format(time.RFC3339),
+ )
+ if err != nil {
+ return fmt.Errorf("store: add loot: %w", err)
+ }
+ id, err = result.LastInsertId()
+ return err
+ })
+ if err != nil {
+ return 0, err
+ }
+ s.notifyLoot()
+ return id, nil
+}
+
+func (s *Store) DeleteLoot(id int64) error {
+ err := s.write(func() error {
+ _, err := s.db.Exec(`DELETE FROM loot WHERE id = ?`, id)
+ if err != nil {
+ return fmt.Errorf("store: delete loot: %w", err)
+ }
+ return nil
+ })
+ if err != nil {
+ return err
+ }
+ s.notifyLoot()
+ return nil
+}
+
+// notifyLoot signals the UI that loot state has changed.
+// Non-blocking — a full buffer means the UI is already scheduled to reload.
+func (s *Store) notifyLoot() {
+ select {
+ case s.LootEntries <- struct{}{}:
+ default:
+ }
+}