This repository has no description
1package db
2
3import (
4 "database/sql"
5 "encoding/json"
6 "fmt"
7 "log"
8 "sort"
9 "strings"
10 "time"
11
12 "github.com/bluesky-social/indigo/api/atproto"
13 "github.com/bluesky-social/indigo/atproto/syntax"
14 "tangled.org/core/api/tangled"
15 "tangled.org/core/appview/models"
16 "tangled.org/core/orm"
17)
18
19func PutComment(tx *sql.Tx, c *models.Comment, references []syntax.ATURI) error {
20 if c.Collection == "" {
21 c.Collection = tangled.FeedCommentNSID
22 }
23
24 var bodyBlobs, replyToUri, replyToCid *string
25 if len(c.Body.Blobs) > 0 {
26 encoded, err := json.Marshal(c.Body.Blobs)
27 if err != nil {
28 return fmt.Errorf("encoding blobs to json: %w", err)
29 }
30 encodedStr := string(encoded)
31 bodyBlobs = &encodedStr
32 }
33 if c.ReplyTo != nil {
34 replyToUri = &c.ReplyTo.Uri
35 replyToCid = &c.ReplyTo.Cid
36 }
37 result, err := tx.Exec(
38 // users can change the 'created' date.
39 // skip update entirely if cid is unchanged.
40 `insert into comments (
41 did,
42 collection,
43 rkey,
44 cid,
45 subject_uri,
46 subject_cid,
47 body_text,
48 body_original,
49 body_blobs,
50 created,
51 reply_to_uri,
52 reply_to_cid,
53 pull_round_idx
54 )
55 values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
56 on conflict(did, collection, rkey)
57 do update set
58 cid = excluded.cid,
59 subject_uri = excluded.subject_uri,
60 subject_cid = excluded.subject_cid,
61 body_text = excluded.body_text,
62 body_original = excluded.body_original,
63 body_blobs = excluded.body_blobs,
64 created = excluded.created,
65 reply_to_uri = excluded.reply_to_uri,
66 reply_to_cid = excluded.reply_to_cid,
67 pull_round_idx = excluded.pull_round_idx,
68 edited = ?
69 where comments.cid is not excluded.cid`,
70 c.Did,
71 c.Collection,
72 c.Rkey,
73 c.Cid,
74 c.Subject.Uri,
75 c.Subject.Cid,
76 c.Body.Text,
77 c.Body.Original,
78 bodyBlobs,
79 c.Created.Format(time.RFC3339),
80 replyToUri,
81 replyToCid,
82 c.PullRoundIdx,
83 time.Now().Format(time.RFC3339),
84 )
85 if err != nil {
86 return err
87 }
88
89 c.Id, err = result.LastInsertId()
90 if err != nil {
91 return err
92 }
93
94 affected, err := result.RowsAffected()
95 if err != nil {
96 return err
97 }
98
99 if affected < 1 {
100 log.Println("record is already stored. skipping operation")
101 return nil
102 }
103
104 // update references when comment is updated
105 if err := putReferences(tx, c.AtUri(), references); err != nil {
106 return fmt.Errorf("put reference_links: %w", err)
107 }
108
109 return nil
110}
111
112// PurgeComments actually purges a comment row from db instead of marking it as "deleted"
113func PurgeComments(e Execer, filters ...orm.Filter) error {
114 var conditions []string
115 var args []any
116 for _, filter := range filters {
117 conditions = append(conditions, filter.Condition())
118 args = append(args, filter.Arg()...)
119 }
120
121 whereClause := ""
122 if conditions != nil {
123 whereClause = " where " + strings.Join(conditions, " and ")
124 }
125
126 _, err := e.Exec(fmt.Sprintf(`delete from comments %s`, whereClause), args...)
127 return err
128}
129
130func DeleteComments(e Execer, filters ...orm.Filter) error {
131 var conditions []string
132 var args []any
133 for _, filter := range filters {
134 conditions = append(conditions, filter.Condition())
135 args = append(args, filter.Arg()...)
136 }
137
138 whereClause := ""
139 if conditions != nil {
140 whereClause = " where " + strings.Join(conditions, " and ")
141 }
142
143 query := fmt.Sprintf(
144 `update comments
145 set body_text = "",
146 body_original = null,
147 body_blobs = null,
148 deleted = strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', 'now')
149 %s`,
150 whereClause,
151 )
152
153 _, err := e.Exec(query, args...)
154 return err
155}
156
157func GetComments(e Execer, filters ...orm.Filter) ([]models.Comment, error) {
158 var comments []models.Comment
159
160 var conditions []string
161 var args []any
162 for _, filter := range filters {
163 conditions = append(conditions, filter.Condition())
164 args = append(args, filter.Arg()...)
165 }
166
167 whereClause := ""
168 if conditions != nil {
169 whereClause = " where " + strings.Join(conditions, " and ")
170 }
171
172 query := fmt.Sprintf(`
173 select
174 id,
175 did,
176 collection,
177 rkey,
178 cid,
179 subject_uri,
180 subject_cid,
181 body_text,
182 body_original,
183 body_blobs,
184 created,
185 reply_to_uri,
186 reply_to_cid,
187 pull_round_idx,
188 edited,
189 deleted
190 from
191 comments
192 %s
193 `, whereClause)
194
195 rows, err := e.Query(query, args...)
196 if err != nil {
197 return nil, err
198 }
199 defer rows.Close()
200
201 for rows.Next() {
202 var comment models.Comment
203 var created string
204 var cid, bodyBlobs, replyToUri, replyToCid, edited, deleted sql.Null[string]
205 err := rows.Scan(
206 &comment.Id,
207 &comment.Did,
208 &comment.Collection,
209 &comment.Rkey,
210 &cid,
211 &comment.Subject.Uri,
212 &comment.Subject.Cid,
213 &comment.Body.Text,
214 &comment.Body.Original,
215 &bodyBlobs,
216 &created,
217 &replyToUri,
218 &replyToCid,
219 &comment.PullRoundIdx,
220 &edited,
221 &deleted,
222 )
223 if err != nil {
224 return nil, err
225 }
226
227 if cid.Valid && cid.V != "" {
228 comment.Cid = syntax.CID(cid.V)
229 }
230
231 if bodyBlobs.Valid && bodyBlobs.V != "" {
232 if err := json.Unmarshal([]byte(bodyBlobs.V), &comment.Body.Blobs); err != nil {
233 return nil, fmt.Errorf("decoding blobs: %w", err)
234 }
235 }
236
237 if t, err := time.Parse(time.RFC3339, created); err == nil {
238 comment.Created = t
239 }
240
241 if replyToUri.Valid && replyToCid.Valid {
242 comment.ReplyTo = &atproto.RepoStrongRef{
243 Uri: replyToUri.V,
244 Cid: replyToCid.V,
245 }
246 }
247
248 if edited.Valid {
249 if t, err := time.Parse(time.RFC3339, edited.V); err == nil {
250 comment.Edited = &t
251 }
252 }
253
254 if deleted.Valid {
255 if t, err := time.Parse(time.RFC3339, deleted.V); err == nil {
256 comment.Deleted = &t
257 }
258 }
259
260 comments = append(comments, comment)
261 }
262
263 if err := rows.Err(); err != nil {
264 return nil, err
265 }
266
267 sort.Slice(comments, func(i, j int) bool {
268 return comments[i].Created.Before(comments[j].Created)
269 })
270
271 return comments, nil
272}