(READ ONLY) Margin is an open annotation layer for the internet. Powered by the AT Protocol. margin.at
extension web atproto comments
98
fork

Configure Feed

Select the types of activity you want to include in your feed.

and fix!

+52 -51
+4 -13
backend/internal/api/apikey.go
··· 16 16 "github.com/go-chi/chi/v5" 17 17 18 18 "margin.at/internal/db" 19 - "margin.at/internal/domain" 20 19 "margin.at/internal/logger" 21 20 "margin.at/internal/xrpc" 22 21 ) ··· 222 221 h.db.UpdateAPIKeyLastUsed(apiKey.ID) 223 222 224 223 capturedTags := append([]string(nil), req.Tags...) 225 - capturedURLHash := db.HashURL(req.URL) 226 - go func(did, url, urlHash string) { 224 + capturedNoteURI := result.URI 225 + go func(did, url, noteURI string) { 227 226 prefs, dbErr := h.db.GetPreferences(did) 228 227 communityEnabled := dbErr == nil && prefs != nil && (prefs.EnableCommunityBookmarks == nil || *prefs.EnableCommunityBookmarks) 229 228 if !communityEnabled { ··· 246 245 defer cancel() 247 246 communityResult, communityErr := client.CreateRecord(ctx, did, xrpc.CollectionCommunityBookmark, communityRecord) 248 247 if communityErr == nil && communityResult != nil { 249 - _ = h.db.CreateNote(&domain.Note{ 250 - URI: communityResult.URI, 251 - AuthorDID: did, 252 - Motivation: "bookmarking", 253 - TargetSource: url, 254 - TargetHash: urlHash, 255 - CreatedAt: time.Now(), 256 - IndexedAt: time.Now(), 257 - }) 248 + _ = h.db.SaveCommunityBookmarkRef(noteURI, communityResult.URI) 258 249 } 259 - }(apiKey.OwnerDID, req.URL, capturedURLHash) 250 + }(apiKey.OwnerDID, req.URL, capturedNoteURI) 260 251 261 252 var titlePtr, bodyValuePtr, tagsJSONPtr *string 262 253 if req.Title != "" {
+16 -18
backend/internal/api/notes.go
··· 51 51 SaveEditHistory(uri, recordType, previousContent string, previousCID *string) error 52 52 HashURL(rawURL string) string 53 53 CommunityBookmarkExists(authorDID, targetHash, tagsJSON string) (bool, error) 54 - GetCommunityBookmarkURI(authorDID, targetHash string) (string, error) 54 + SaveCommunityBookmarkRef(noteURI, communityURI string) error 55 + GetCommunityBookmarkURI(noteURI string) (string, error) 56 + DeleteCommunityBookmarkRef(noteURI string) error 55 57 } 56 58 57 59 type dbAdapter struct{ d *db.DB } ··· 131 133 func (a *dbAdapter) CommunityBookmarkExists(did, hash, tags string) (bool, error) { 132 134 return a.d.CommunityBookmarkExists(did, hash, tags) 133 135 } 134 - func (a *dbAdapter) GetCommunityBookmarkURI(did, hash string) (string, error) { 135 - return a.d.GetCommunityBookmarkURI(did, hash) 136 + func (a *dbAdapter) SaveCommunityBookmarkRef(noteURI, communityURI string) error { 137 + return a.d.SaveCommunityBookmarkRef(noteURI, communityURI) 138 + } 139 + func (a *dbAdapter) GetCommunityBookmarkURI(noteURI string) (string, error) { 140 + return a.d.GetCommunityBookmarkURI(noteURI) 141 + } 142 + func (a *dbAdapter) DeleteCommunityBookmarkRef(noteURI string) error { 143 + return a.d.DeleteCommunityBookmarkRef(noteURI) 136 144 } 137 145 138 146 type NoteWriteService struct { ··· 1007 1015 capturedTags := append([]string(nil), req.Tags...) 1008 1016 capturedURL := req.URL 1009 1017 capturedURLHash := urlHash 1018 + capturedNoteURI := result.URI 1010 1019 go func() { 1011 1020 prefs, dbErr := s.db.GetPreferences(capturedSession.DID) 1012 1021 communityEnabled := dbErr == nil && prefs != nil && (prefs.EnableCommunityBookmarks == nil || *prefs.EnableCommunityBookmarks) ··· 1037 1046 } 1038 1047 communityResult, communityErr := client.CreateRecord(ctx, capturedSession.DID, xrpc.CollectionCommunityBookmark, communityRecord) 1039 1048 if communityErr == nil && communityResult != nil { 1040 - _ = s.db.CreateNote(&domain.Note{ 1041 - URI: communityResult.URI, 1042 - AuthorDID: capturedSession.DID, 1043 - Motivation: "bookmarking", 1044 - TargetSource: capturedURL, 1045 - TargetHash: capturedURLHash, 1046 - CreatedAt: time.Now(), 1047 - IndexedAt: time.Now(), 1048 - }) 1049 + _ = s.db.SaveCommunityBookmarkRef(capturedNoteURI, communityResult.URI) 1049 1050 } 1050 1051 }() 1051 1052 ··· 1141 1142 1142 1143 did := session.DID 1143 1144 collection := xrpc.CollectionNote 1144 - var targetHash string 1145 1145 for _, col := range []string{xrpc.CollectionNote, xrpc.CollectionBookmark, xrpc.CollectionCommunityBookmark} { 1146 1146 uri := "at://" + did + "/" + col + "/" + rkey 1147 1147 if note, dbErr := s.db.GetNoteByURI(uri); dbErr == nil && note != nil { 1148 1148 collection = col 1149 - targetHash = note.TargetHash 1150 1149 break 1151 1150 } else if bm, dbErr := s.db.GetBookmarkByURI(uri); dbErr == nil && bm != nil { 1152 1151 collection = col 1153 - targetHash = bm.SourceHash 1154 1152 break 1155 1153 } 1156 1154 } ··· 1166 1164 s.db.DeleteBookmark(uri) 1167 1165 s.db.DeleteNote(uri) 1168 1166 1169 - if collection != xrpc.CollectionCommunityBookmark && targetHash != "" { 1170 - if communityURI, err := s.db.GetCommunityBookmarkURI(did, targetHash); err == nil && communityURI != "" { 1167 + if collection != xrpc.CollectionCommunityBookmark { 1168 + if communityURI, err := s.db.GetCommunityBookmarkURI(uri); err == nil && communityURI != "" { 1171 1169 parts := strings.Split(communityURI, "/") 1172 1170 if len(parts) == 5 { 1173 1171 communityRkey := parts[4] ··· 1175 1173 return client.DeleteRecord(r.Context(), did, xrpc.CollectionCommunityBookmark, communityRkey) 1176 1174 }) 1177 1175 } 1178 - s.db.DeleteNote(communityURI) 1176 + s.db.DeleteCommunityBookmarkRef(uri) 1179 1177 } 1180 1178 } 1181 1179
+8
backend/internal/db/migrations/00006_community_bookmark_refs.sql
··· 1 + -- +goose Up 2 + CREATE TABLE IF NOT EXISTS community_bookmark_refs ( 3 + note_uri TEXT PRIMARY KEY, 4 + community_uri TEXT NOT NULL 5 + ); 6 + 7 + -- +goose Down 8 + DROP TABLE IF EXISTS community_bookmark_refs;
+24 -20
backend/internal/db/queries_notes.go
··· 75 75 return true, nil 76 76 } 77 77 78 - func (db *DB) GetCommunityBookmarkURI(authorDID, targetHash string) (string, error) { 78 + func (db *DB) SaveCommunityBookmarkRef(noteURI, communityURI string) error { 79 + _, err := db.Exec(` 80 + INSERT INTO community_bookmark_refs (note_uri, community_uri) 81 + VALUES ($1, $2) 82 + ON CONFLICT (note_uri) DO UPDATE SET community_uri = EXCLUDED.community_uri 83 + `, noteURI, communityURI) 84 + return err 85 + } 86 + 87 + func (db *DB) GetCommunityBookmarkURI(noteURI string) (string, error) { 79 88 var uri string 80 89 err := db.QueryRow(` 81 - SELECT uri FROM notes 82 - WHERE author_did = $1 83 - AND target_hash = $2 84 - AND uri LIKE 'at://%/community.lexicon.bookmarks.bookmark/%' 85 - LIMIT 1 86 - `, authorDID, targetHash).Scan(&uri) 90 + SELECT community_uri FROM community_bookmark_refs WHERE note_uri = $1 91 + `, noteURI).Scan(&uri) 87 92 if err == sql.ErrNoRows { 88 93 return "", nil 89 94 } 90 95 return uri, err 91 96 } 92 97 98 + func (db *DB) DeleteCommunityBookmarkRef(noteURI string) error { 99 + _, err := db.Exec(`DELETE FROM community_bookmark_refs WHERE note_uri = $1`, noteURI) 100 + return err 101 + } 102 + 93 103 func (db *DB) CommunityBookmarkExists(authorDID, targetHash, tagsJSON string) (bool, error) { 94 - query := ` 95 - SELECT 1 FROM notes 96 - WHERE author_did = $1 97 - AND target_hash = $2 98 - AND uri LIKE 'at://%/community.lexicon.bookmarks.bookmark/%' 99 - AND COALESCE(tags_json, '[]') = COALESCE($3, '[]') 100 - LIMIT 1 101 - ` 102 - normalized := tagsJSON 103 - if normalized == "" { 104 - normalized = "[]" 105 - } 106 104 var dummy int 107 - err := db.QueryRow(query, authorDID, targetHash, normalized).Scan(&dummy) 105 + err := db.QueryRow(` 106 + SELECT 1 FROM community_bookmark_refs cbr 107 + JOIN notes n ON n.uri = cbr.note_uri 108 + WHERE n.author_did = $1 109 + AND n.target_hash = $2 110 + LIMIT 1 111 + `, authorDID, targetHash).Scan(&dummy) 108 112 if err == sql.ErrNoRows { 109 113 return false, nil 110 114 }