tinyrelaycommit

Commit fec17a90a7f8

browse files at this commit | history

1diff --git a/internal/storage/maintenance.go b/internal/storage/maintenance.go
2index 325e8c0..324405c 100644
3--- a/internal/storage/maintenance.go
4+++ b/internal/storage/maintenance.go
5@@ -50,6 +50,7 @@ func (s *Store) Vanish(ctx context.Context, pubkey string, until int64) error {
6 {"DELETE FROM events WHERE pubkey=? AND created_at<=?", []any{pubkey, until}},
7 {"DELETE FROM events WHERE kind=1059 AND id IN(SELECT event_id FROM tags WHERE name='p' AND value=?)", []any{pubkey}},
8 {"DELETE FROM list_history WHERE owner=?", []any{pubkey}},
9+ {"DELETE FROM wiki_revisions WHERE author=?", []any{pubkey}},
10 }
11 for _, q := range statements {
12 if _, err := tx.ExecContext(ctx, q.sql, q.args...); err != nil {
13@@ -93,7 +94,10 @@ func (s *Store) EraseAuthor(ctx context.Context, pubkey string) (int64, error) {
14 if err != nil {
15 return err
16 }
17- _, err = tx.ExecContext(ctx, "DELETE FROM list_history WHERE owner=?", pubkey)
18+ if _, err = tx.ExecContext(ctx, "DELETE FROM list_history WHERE owner=?", pubkey); err != nil {
19+ return err
20+ }
21+ _, err = tx.ExecContext(ctx, "DELETE FROM wiki_revisions WHERE author=?", pubkey)
22 return err
23 })
24 return n, err
25diff --git a/internal/storage/save.go b/internal/storage/save.go
26index 9f8835e..8543a0c 100644
27--- a/internal/storage/save.go
28+++ b/internal/storage/save.go
29@@ -159,6 +159,14 @@ func replace(ctx context.Context, tx *sql.Tx, e event.Event, now int64) error {
30 return fmt.Errorf("archive replaced list: %w", err)
31 }
32 }
33+ if e.Kind == kindWikiArticle {
34+ // A wiki page keeps its history: the version being replaced moves
35+ // to wiki_revisions with the id of the event that replaced it.
36+ _, err := tx.ExecContext(ctx, "INSERT OR IGNORE INTO wiki_revisions(event_id,author,d,created_at,raw,superseded_by) SELECT id,pubkey,d,created_at,raw,? FROM events WHERE pubkey=? AND kind=? AND d=?", e.ID, e.PubKey, e.Kind, d)
37+ if err != nil {
38+ return fmt.Errorf("archive replaced wiki revision: %w", err)
39+ }
40+ }
41 _, err = tx.ExecContext(ctx, "DELETE FROM events WHERE pubkey=? AND kind=? AND d=?", e.PubKey, e.Kind, d)
42 if err != nil {
43 return fmt.Errorf("replace event: %w", err)
44@@ -182,6 +190,11 @@ func applyDeletion(ctx context.Context, tx *sql.Tx, e event.Event, tag []string)
45 if _, err := tx.ExecContext(ctx, "DELETE FROM list_history WHERE kind=? AND owner=? AND d=? AND created_at<=?", kind, e.PubKey, parts[2], e.CreatedAt); err != nil {
46 return fmt.Errorf("delete list history: %w", err)
47 }
48+ if kind == kindWikiArticle {
49+ if _, err := tx.ExecContext(ctx, "DELETE FROM wiki_revisions WHERE author=? AND d=? AND created_at<=?", e.PubKey, parts[2], e.CreatedAt); err != nil {
50+ return fmt.Errorf("delete wiki revisions: %w", err)
51+ }
52+ }
53 } else {
54 if _, err := tx.ExecContext(ctx, "DELETE FROM events WHERE id=? AND (pubkey=? OR (kind=1059 AND EXISTS(SELECT 1 FROM tags WHERE event_id=events.id AND name='p' AND value=?)))", tag[1], e.PubKey, e.PubKey); err != nil {
55 return fmt.Errorf("delete event: %w", err)
56@@ -189,6 +202,9 @@ func applyDeletion(ctx context.Context, tx *sql.Tx, e event.Event, tag []string)
57 if _, err := tx.ExecContext(ctx, "DELETE FROM list_history WHERE owner=? AND event_id=?", e.PubKey, tag[1]); err != nil {
58 return fmt.Errorf("delete list version: %w", err)
59 }
60+ if _, err := tx.ExecContext(ctx, "DELETE FROM wiki_revisions WHERE author=? AND event_id=?", e.PubKey, tag[1]); err != nil {
61+ return fmt.Errorf("delete wiki revision: %w", err)
62+ }
63 }
64 _, err := tx.ExecContext(ctx, "INSERT INTO deletions(author,target_type,target,until) VALUES(?,?,?,?) ON CONFLICT(author,target_type,target) DO UPDATE SET until=max(until,excluded.until)", e.PubKey, tag[0], tag[1], e.CreatedAt)
65 if err != nil {
66@@ -243,6 +259,10 @@ func indexed(kind int, mode string) bool {
67
68 func isListKind(kind int) bool { return kind == 3 || kind == 10002 || kind == 10003 || kind == 30003 }
69
70+// kindWikiArticle is the NIP-54 article kind, whose replaced versions are
71+// kept as revisions.
72+const kindWikiArticle = 30818
73+
74 func AddIntents(ctx context.Context, tx *sql.Tx, intents []Intent, now int64) error {
75 for _, intent := range intents {
76 key := sha256.Sum256([]byte(intent.Kind + "\x00" + intent.EventID + "\x00" + intent.Target))
77diff --git a/internal/storage/schema.go b/internal/storage/schema.go
78index 75e4e0d..293c9c4 100644
79--- a/internal/storage/schema.go
80+++ b/internal/storage/schema.go
81@@ -27,6 +27,9 @@ CREATE TABLE IF NOT EXISTS marmot_principals(event_id TEXT PRIMARY KEY REFERENCE
82 CREATE INDEX IF NOT EXISTS marmot_principal_pk ON marmot_principals(pubkey);
83 CREATE TABLE IF NOT EXISTS list_history(owner TEXT NOT NULL,kind INTEGER NOT NULL,d TEXT NOT NULL DEFAULT '',event_id TEXT NOT NULL,created_at INTEGER NOT NULL,saved_at INTEGER NOT NULL,expires INTEGER NOT NULL DEFAULT 0,raw TEXT NOT NULL,PRIMARY KEY(owner,kind,d,event_id));
84 CREATE INDEX IF NOT EXISTS list_history_owner ON list_history(owner,saved_at DESC);
85+CREATE TABLE IF NOT EXISTS wiki_revisions(event_id TEXT PRIMARY KEY,author TEXT NOT NULL,d TEXT NOT NULL,created_at INTEGER NOT NULL,raw TEXT NOT NULL,superseded_by TEXT NOT NULL);
86+CREATE INDEX IF NOT EXISTS wiki_revisions_page ON wiki_revisions(d,created_at DESC);
87+CREATE INDEX IF NOT EXISTS wiki_revisions_author ON wiki_revisions(author,d,created_at DESC);
88 CREATE TABLE IF NOT EXISTS settings(key TEXT PRIMARY KEY,value TEXT NOT NULL);
89 CREATE TABLE IF NOT EXISTS work_intents (
90 id TEXT PRIMARY KEY,kind TEXT NOT NULL,event_id TEXT NOT NULL,target TEXT NOT NULL,payload TEXT NOT NULL,
91diff --git a/internal/storage/wiki.go b/internal/storage/wiki.go
92new file mode 100644
93index 0000000..b217746
94--- /dev/null
95+++ b/internal/storage/wiki.go
96@@ -0,0 +1,66 @@
97+package storage
98+
99+// Wiki revisions. A kind 30818 article is addressable, so a newer version
100+// from the same author replaces the older one in events. The replaced
101+// event is kept here with the id of the event that replaced it, until the
102+// author deletes it with a kind 5 naming the revision or the page.
103+
104+import (
105+ "context"
106+ "database/sql"
107+ "encoding/json"
108+ "errors"
109+ "fmt"
110+
111+ "github.com/FelineStateMachine/tinyrelay/internal/event"
112+)
113+
114+// WikiRevision is one archived version of a wiki page.
115+type WikiRevision struct {
116+ ID string
117+ Author string
118+ D string
119+ CreatedAt int64
120+ SupersededBy string
121+ Event event.Event
122+}
123+
124+// WikiRevisions lists archived revisions newest first. An empty author or
125+// d matches every author or page.
126+func (s *Store) WikiRevisions(ctx context.Context, author, d string) ([]WikiRevision, error) {
127+ rows, err := s.db.QueryContext(ctx, "SELECT event_id,author,d,created_at,superseded_by,raw FROM wiki_revisions WHERE (?='' OR author=?) AND (?='' OR d=?) ORDER BY created_at DESC,event_id", author, author, d, d)
128+ if err != nil {
129+ return nil, fmt.Errorf("query wiki revisions: %w", err)
130+ }
131+ defer rows.Close()
132+ result := []WikiRevision{}
133+ for rows.Next() {
134+ var row WikiRevision
135+ var raw string
136+ if err := rows.Scan(&row.ID, &row.Author, &row.D, &row.CreatedAt, &row.SupersededBy, &raw); err != nil {
137+ return nil, err
138+ }
139+ if err := json.Unmarshal([]byte(raw), &row.Event); err != nil {
140+ return nil, fmt.Errorf("decode wiki revision %s: %w", row.ID, err)
141+ }
142+ result = append(result, row)
143+ }
144+ return result, rows.Err()
145+}
146+
147+// WikiRevision reads one archived revision by event id.
148+func (s *Store) WikiRevision(ctx context.Context, id string) (WikiRevision, bool, error) {
149+ var row WikiRevision
150+ var raw string
151+ err := s.db.QueryRowContext(ctx, "SELECT event_id,author,d,created_at,superseded_by,raw FROM wiki_revisions WHERE event_id=?", id).Scan(&row.ID, &row.Author, &row.D, &row.CreatedAt, &row.SupersededBy, &raw)
152+ if errors.Is(err, sql.ErrNoRows) {
153+ return row, false, nil
154+ }
155+ if err != nil {
156+ return row, false, fmt.Errorf("read wiki revision: %w", err)
157+ }
158+ if err := json.Unmarshal([]byte(raw), &row.Event); err != nil {
159+ return row, false, fmt.Errorf("decode wiki revision %s: %w", id, err)
160+ }
161+ return row, true, nil
162+}

About

ownerc01503e8035f
visibilitypublic
headfec17a90a7f8

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs