Write on the margins of the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
1package db
2
3import (
4 "crypto/sha256"
5 "encoding/hex"
6 "encoding/json"
7 "fmt"
8 "net/url"
9 "strings"
10 "time"
11)
12
13type EditHistory struct {
14 ID int `json:"id"`
15 URI string `json:"uri"`
16 RecordType string `json:"recordType"`
17 PreviousContent string `json:"previousContent"`
18 PreviousCID *string `json:"previousCid"`
19 EditedAt time.Time `json:"editedAt"`
20}
21
22func scanAnnotations(rows interface {
23 Next() bool
24 Scan(...interface{}) error
25}) ([]Annotation, error) {
26 var annotations []Annotation
27 for rows.Next() {
28 var a Annotation
29 if err := rows.Scan(&a.URI, &a.AuthorDID, &a.Motivation, &a.BodyValue, &a.BodyFormat, &a.BodyURI, &a.TargetSource, &a.TargetHash, &a.TargetTitle, &a.SelectorJSON, &a.TagsJSON, &a.CreatedAt, &a.IndexedAt, &a.CID); err != nil {
30 return nil, err
31 }
32 annotations = append(annotations, a)
33 }
34 return annotations, nil
35}
36
37func (db *DB) AnnotationExists(uri string) bool {
38 var count int
39 db.QueryRow(db.Rebind(`SELECT COUNT(*) FROM annotations WHERE uri = ?`), uri).Scan(&count)
40 return count > 0
41}
42
43func HashURL(rawURL string) string {
44 parsed, err := url.Parse(rawURL)
45 if err != nil {
46 return hashString(rawURL)
47 }
48
49 normalized := strings.ToLower(parsed.Host) + parsed.Path
50 if parsed.RawQuery != "" {
51 normalized += "?" + parsed.RawQuery
52 }
53 normalized = strings.TrimSuffix(normalized, "/")
54
55 return hashString(normalized)
56}
57
58func hashString(s string) string {
59 h := sha256.New()
60 h.Write([]byte(s))
61 return hex.EncodeToString(h.Sum(nil))
62}
63
64func ToJSON(v interface{}) string {
65 b, _ := json.Marshal(v)
66 return string(b)
67}
68
69func (db *DB) GetAuthorByURI(uri string) (string, error) {
70 var authorDID string
71 err := db.QueryRow(db.Rebind(`SELECT author_did FROM annotations WHERE uri = ?`), uri).Scan(&authorDID)
72 if err == nil {
73 return authorDID, nil
74 }
75
76 err = db.QueryRow(db.Rebind(`SELECT author_did FROM highlights WHERE uri = ?`), uri).Scan(&authorDID)
77 if err == nil {
78 return authorDID, nil
79 }
80
81 err = db.QueryRow(db.Rebind(`SELECT author_did FROM bookmarks WHERE uri = ?`), uri).Scan(&authorDID)
82 if err == nil {
83 return authorDID, nil
84 }
85
86 return "", fmt.Errorf("uri not found or no author")
87}
88
89func buildPlaceholders(n int) string {
90 if n == 0 {
91 return ""
92 }
93 placeholders := make([]string, n)
94 for i := range placeholders {
95 placeholders[i] = "?"
96 }
97 return strings.Join(placeholders, ", ")
98}