1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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, ×tampStr); 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:
}
}
|