srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/internal/store/store.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/store/store.go')
-rw-r--r--internal/store/store.go54
1 files changed, 45 insertions, 9 deletions
diff --git a/internal/store/store.go b/internal/store/store.go
index d04887b..4de4fa1 100644
--- a/internal/store/store.go
+++ b/internal/store/store.go
@@ -33,7 +33,8 @@ CREATE TABLE IF NOT EXISTS history (
request_exact INTEGER NOT NULL,
response_exact INTEGER NOT NULL,
error TEXT NOT NULL DEFAULT '',
- source TEXT NOT NULL DEFAULT 'proxy'
+ source TEXT NOT NULL DEFAULT 'proxy',
+ flagged INTEGER NOT NULL DEFAULT 0
);
CREATE VIRTUAL TABLE IF NOT EXISTS history_fts USING fts5(
@@ -87,6 +88,7 @@ func Open(path string) (*Store, error) {
// Added after the initial schema; ignore the "duplicate column" error
// on databases that already have it.
db.Exec("ALTER TABLE history ADD COLUMN source TEXT NOT NULL DEFAULT 'proxy'")
+ db.Exec("ALTER TABLE history ADD COLUMN flagged INTEGER NOT NULL DEFAULT 0")
// Backfill history_fts for rows inserted before it existed. A no-op
// once caught up, since every Insert keeps both tables in sync.
if _, err := db.Exec(`
@@ -121,6 +123,7 @@ type Entry struct {
ResponseExact bool
Error string // network/transport error, if the request never got a response
Source string // "proxy" or "repeater"
+ Flagged bool
}
// Summary is the lightweight metadata used for the history list view -
@@ -138,6 +141,7 @@ type Summary struct {
RespSize int
Error string
Source string
+ Flagged bool
}
// Insert stores e (and indexes it for search) and returns its assigned ID.
@@ -207,7 +211,7 @@ func (s *Store) List(limit int, beforeID int64) ([]Summary, error) {
}
rows, err := s.db.Query(
`SELECT id, started_at, duration_ms, method, scheme, host, path,
- COALESCE(status_code, 0), length(request_raw), COALESCE(length(response_raw), 0), error, source
+ COALESCE(status_code, 0), length(request_raw), COALESCE(length(response_raw), 0), error, source, flagged
FROM history WHERE id < ? ORDER BY id DESC LIMIT ?`,
beforeID, limit,
)
@@ -220,10 +224,12 @@ func (s *Store) List(limit int, beforeID int64) ([]Summary, error) {
for rows.Next() {
var sum Summary
var startedAt, durationMs int64
+ var flagged int
if err := rows.Scan(&sum.ID, &startedAt, &durationMs, &sum.Method, &sum.Scheme, &sum.Host, &sum.Path,
- &sum.StatusCode, &sum.ReqSize, &sum.RespSize, &sum.Error, &sum.Source); err != nil {
+ &sum.StatusCode, &sum.ReqSize, &sum.RespSize, &sum.Error, &sum.Source, &flagged); err != nil {
return nil, fmt.Errorf("scan history row: %w", err)
}
+ sum.Flagged = flagged != 0
sum.StartedAt = time.UnixMilli(startedAt)
sum.Duration = time.Duration(durationMs) * time.Millisecond
out = append(out, sum)
@@ -267,6 +273,10 @@ func (s *Store) Search(query string, limit int, beforeID int64) ([]Summary, erro
where = append(where, "h.source = ?")
args = append(args, pred.source)
}
+ if pred.flagged != nil {
+ where = append(where, "h.flagged = ?")
+ args = append(args, boolToInt(*pred.flagged))
+ }
var q string
if remaining == "" {
@@ -274,7 +284,7 @@ func (s *Store) Search(query string, limit int, beforeID int64) ([]Summary, erro
// FTS5 join or ranking needed.
q = `SELECT h.id, h.started_at, h.duration_ms, h.method, h.scheme, h.host, h.path,
COALESCE(h.status_code, 0), length(h.request_raw), COALESCE(length(h.response_raw), 0),
- h.error, h.source
+ h.error, h.source, h.flagged
FROM history h
WHERE ` + strings.Join(where, " AND ") + `
ORDER BY h.id DESC LIMIT ?`
@@ -287,7 +297,7 @@ func (s *Store) Search(query string, limit int, beforeID int64) ([]Summary, erro
args = append([]any{prepareFTSQuery(remaining)}, args...)
q = `SELECT h.id, h.started_at, h.duration_ms, h.method, h.scheme, h.host, h.path,
COALESCE(h.status_code, 0), length(h.request_raw), COALESCE(length(h.response_raw), 0),
- h.error, h.source
+ h.error, h.source, h.flagged
FROM history_fts
JOIN history h ON h.id = history_fts.rowid
WHERE ` + strings.Join(where, " AND ") + `
@@ -305,10 +315,12 @@ func (s *Store) Search(query string, limit int, beforeID int64) ([]Summary, erro
for rows.Next() {
var sum Summary
var startedAt, durationMs int64
+ var flagged int
if err := rows.Scan(&sum.ID, &startedAt, &durationMs, &sum.Method, &sum.Scheme, &sum.Host, &sum.Path,
- &sum.StatusCode, &sum.ReqSize, &sum.RespSize, &sum.Error, &sum.Source); err != nil {
+ &sum.StatusCode, &sum.ReqSize, &sum.RespSize, &sum.Error, &sum.Source, &flagged); err != nil {
return nil, fmt.Errorf("scan search row: %w", err)
}
+ sum.Flagged = flagged != 0
sum.StartedAt = time.UnixMilli(startedAt)
sum.Duration = time.Duration(durationMs) * time.Millisecond
out = append(out, sum)
@@ -322,6 +334,7 @@ type structuredPredicate struct {
statusSQL string
statusArgs []any
source string
+ flagged *bool
}
var (
@@ -358,6 +371,18 @@ func extractStructured(query string) (remaining string, pred structuredPredicate
case strings.HasPrefix(lower, "source:"):
pred.source = strings.ToLower(strings.TrimPrefix(f, "source:"))
continue
+ case strings.HasPrefix(lower, "flagged:"):
+ val := strings.ToLower(strings.TrimPrefix(lower, "flagged:"))
+ switch val {
+ case "true", "1", "yes":
+ b := true
+ pred.flagged = &b
+ continue
+ case "false", "0", "no":
+ b := false
+ pred.flagged = &b
+ continue
+ }
}
kept = append(kept, f)
}
@@ -369,24 +394,35 @@ func (s *Store) Get(id int64) (*Entry, error) {
row := s.db.QueryRow(
`SELECT id, started_at, duration_ms, method, scheme, host, path,
COALESCE(status_code, 0), request_raw, response_raw,
- request_exact, response_exact, error, source
+ request_exact, response_exact, error, source, flagged
FROM history WHERE id = ?`,
id,
)
var e Entry
var startedAt, durationMs int64
- var reqExact, respExact int
+ var reqExact, respExact, flagged int
if err := row.Scan(&e.ID, &startedAt, &durationMs, &e.Method, &e.Scheme, &e.Host, &e.Path,
- &e.StatusCode, &e.RequestRaw, &e.ResponseRaw, &reqExact, &respExact, &e.Error, &e.Source); err != nil {
+ &e.StatusCode, &e.RequestRaw, &e.ResponseRaw, &reqExact, &respExact, &e.Error, &e.Source, &flagged); err != nil {
return nil, fmt.Errorf("get history entry %d: %w", id, err)
}
e.StartedAt = time.UnixMilli(startedAt)
e.Duration = time.Duration(durationMs) * time.Millisecond
e.RequestExact = reqExact != 0
e.ResponseExact = respExact != 0
+ e.Flagged = flagged != 0
return &e, nil
}
+// SetFlagged toggles the flagged marker on a history entry - a simple
+// "mark this, revisit later" bit, filterable via flagged:true/false in
+// Search. Doesn't touch anything else about the entry.
+func (s *Store) SetFlagged(id int64, flagged bool) error {
+ if _, err := s.db.Exec(`UPDATE history SET flagged = ? WHERE id = ?`, boolToInt(flagged), id); err != nil {
+ return fmt.Errorf("set flagged on entry %d: %w", id, err)
+ }
+ return nil
+}
+
// prepareFTSQuery makes free text safe to hand to FTS5's MATCH, whose
// query grammar reserves a wide range of punctuation ('.', '-', '/',
// '@', '(', ')', and more - confirmed empirically, not just from docs)