diff --git a/backend/internal/api/apikey.go b/backend/internal/api/apikey.go index a33af3d..2e70042 100644 --- a/backend/internal/api/apikey.go +++ b/backend/internal/api/apikey.go @@ -16,7 +16,6 @@ import ( "github.com/go-chi/chi/v5" "margin.at/internal/db" - "margin.at/internal/domain" "margin.at/internal/logger" "margin.at/internal/xrpc" ) @@ -222,8 +221,8 @@ func (h *APIKeyHandler) QuickBookmark(w http.ResponseWriter, r *http.Request) { h.db.UpdateAPIKeyLastUsed(apiKey.ID) capturedTags := append([]string(nil), req.Tags...) - capturedURLHash := db.HashURL(req.URL) - go func(did, url, urlHash string) { + capturedNoteURI := result.URI + go func(did, url, noteURI string) { prefs, dbErr := h.db.GetPreferences(did) communityEnabled := dbErr == nil && prefs != nil && (prefs.EnableCommunityBookmarks == nil || *prefs.EnableCommunityBookmarks) if !communityEnabled { @@ -246,17 +245,9 @@ func (h *APIKeyHandler) QuickBookmark(w http.ResponseWriter, r *http.Request) { defer cancel() communityResult, communityErr := client.CreateRecord(ctx, did, xrpc.CollectionCommunityBookmark, communityRecord) if communityErr == nil && communityResult != nil { - _ = h.db.CreateNote(&domain.Note{ - URI: communityResult.URI, - AuthorDID: did, - Motivation: "bookmarking", - TargetSource: url, - TargetHash: urlHash, - CreatedAt: time.Now(), - IndexedAt: time.Now(), - }) + _ = h.db.SaveCommunityBookmarkRef(noteURI, communityResult.URI) } - }(apiKey.OwnerDID, req.URL, capturedURLHash) + }(apiKey.OwnerDID, req.URL, capturedNoteURI) var titlePtr, bodyValuePtr, tagsJSONPtr *string if req.Title != "" { diff --git a/backend/internal/api/notes.go b/backend/internal/api/notes.go index fddac54..d47b216 100644 --- a/backend/internal/api/notes.go +++ b/backend/internal/api/notes.go @@ -51,7 +51,9 @@ type NoteIndexDB interface { SaveEditHistory(uri, recordType, previousContent string, previousCID *string) error HashURL(rawURL string) string CommunityBookmarkExists(authorDID, targetHash, tagsJSON string) (bool, error) - GetCommunityBookmarkURI(authorDID, targetHash string) (string, error) + SaveCommunityBookmarkRef(noteURI, communityURI string) error + GetCommunityBookmarkURI(noteURI string) (string, error) + DeleteCommunityBookmarkRef(noteURI string) error } type dbAdapter struct{ d *db.DB } @@ -131,8 +133,14 @@ func (a *dbAdapter) HashURL(rawURL string) string { return db.HashURL(rawURL) } func (a *dbAdapter) CommunityBookmarkExists(did, hash, tags string) (bool, error) { return a.d.CommunityBookmarkExists(did, hash, tags) } -func (a *dbAdapter) GetCommunityBookmarkURI(did, hash string) (string, error) { - return a.d.GetCommunityBookmarkURI(did, hash) +func (a *dbAdapter) SaveCommunityBookmarkRef(noteURI, communityURI string) error { + return a.d.SaveCommunityBookmarkRef(noteURI, communityURI) +} +func (a *dbAdapter) GetCommunityBookmarkURI(noteURI string) (string, error) { + return a.d.GetCommunityBookmarkURI(noteURI) +} +func (a *dbAdapter) DeleteCommunityBookmarkRef(noteURI string) error { + return a.d.DeleteCommunityBookmarkRef(noteURI) } type NoteWriteService struct { @@ -1007,6 +1015,7 @@ func (s *NoteWriteService) CreateBookmark(w http.ResponseWriter, r *http.Request capturedTags := append([]string(nil), req.Tags...) capturedURL := req.URL capturedURLHash := urlHash + capturedNoteURI := result.URI go func() { prefs, dbErr := s.db.GetPreferences(capturedSession.DID) communityEnabled := dbErr == nil && prefs != nil && (prefs.EnableCommunityBookmarks == nil || *prefs.EnableCommunityBookmarks) @@ -1037,15 +1046,7 @@ func (s *NoteWriteService) CreateBookmark(w http.ResponseWriter, r *http.Request } communityResult, communityErr := client.CreateRecord(ctx, capturedSession.DID, xrpc.CollectionCommunityBookmark, communityRecord) if communityErr == nil && communityResult != nil { - _ = s.db.CreateNote(&domain.Note{ - URI: communityResult.URI, - AuthorDID: capturedSession.DID, - Motivation: "bookmarking", - TargetSource: capturedURL, - TargetHash: capturedURLHash, - CreatedAt: time.Now(), - IndexedAt: time.Now(), - }) + _ = s.db.SaveCommunityBookmarkRef(capturedNoteURI, communityResult.URI) } }() @@ -1141,16 +1142,13 @@ func (s *NoteWriteService) DeleteBookmark(w http.ResponseWriter, r *http.Request did := session.DID collection := xrpc.CollectionNote - var targetHash string for _, col := range []string{xrpc.CollectionNote, xrpc.CollectionBookmark, xrpc.CollectionCommunityBookmark} { uri := "at://" + did + "/" + col + "/" + rkey if note, dbErr := s.db.GetNoteByURI(uri); dbErr == nil && note != nil { collection = col - targetHash = note.TargetHash break } else if bm, dbErr := s.db.GetBookmarkByURI(uri); dbErr == nil && bm != nil { collection = col - targetHash = bm.SourceHash break } } @@ -1166,8 +1164,8 @@ func (s *NoteWriteService) DeleteBookmark(w http.ResponseWriter, r *http.Request s.db.DeleteBookmark(uri) s.db.DeleteNote(uri) - if collection != xrpc.CollectionCommunityBookmark && targetHash != "" { - if communityURI, err := s.db.GetCommunityBookmarkURI(did, targetHash); err == nil && communityURI != "" { + if collection != xrpc.CollectionCommunityBookmark { + if communityURI, err := s.db.GetCommunityBookmarkURI(uri); err == nil && communityURI != "" { parts := strings.Split(communityURI, "/") if len(parts) == 5 { communityRkey := parts[4] @@ -1175,7 +1173,7 @@ func (s *NoteWriteService) DeleteBookmark(w http.ResponseWriter, r *http.Request return client.DeleteRecord(r.Context(), did, xrpc.CollectionCommunityBookmark, communityRkey) }) } - s.db.DeleteNote(communityURI) + s.db.DeleteCommunityBookmarkRef(uri) } } diff --git a/backend/internal/db/migrations/00006_community_bookmark_refs.sql b/backend/internal/db/migrations/00006_community_bookmark_refs.sql new file mode 100644 index 0000000..aedf7b1 --- /dev/null +++ b/backend/internal/db/migrations/00006_community_bookmark_refs.sql @@ -0,0 +1,8 @@ +-- +goose Up +CREATE TABLE IF NOT EXISTS community_bookmark_refs ( + note_uri TEXT PRIMARY KEY, + community_uri TEXT NOT NULL +); + +-- +goose Down +DROP TABLE IF EXISTS community_bookmark_refs; diff --git a/backend/internal/db/queries_notes.go b/backend/internal/db/queries_notes.go index 54958b6..2d54acf 100644 --- a/backend/internal/db/queries_notes.go +++ b/backend/internal/db/queries_notes.go @@ -75,36 +75,40 @@ func (db *DB) MarginNoteBookmarkExists(authorDID, targetHash string) (bool, erro return true, nil } -func (db *DB) GetCommunityBookmarkURI(authorDID, targetHash string) (string, error) { +func (db *DB) SaveCommunityBookmarkRef(noteURI, communityURI string) error { + _, err := db.Exec(` + INSERT INTO community_bookmark_refs (note_uri, community_uri) + VALUES ($1, $2) + ON CONFLICT (note_uri) DO UPDATE SET community_uri = EXCLUDED.community_uri + `, noteURI, communityURI) + return err +} + +func (db *DB) GetCommunityBookmarkURI(noteURI string) (string, error) { var uri string err := db.QueryRow(` - SELECT uri FROM notes - WHERE author_did = $1 - AND target_hash = $2 - AND uri LIKE 'at://%/community.lexicon.bookmarks.bookmark/%' - LIMIT 1 - `, authorDID, targetHash).Scan(&uri) + SELECT community_uri FROM community_bookmark_refs WHERE note_uri = $1 + `, noteURI).Scan(&uri) if err == sql.ErrNoRows { return "", nil } return uri, err } +func (db *DB) DeleteCommunityBookmarkRef(noteURI string) error { + _, err := db.Exec(`DELETE FROM community_bookmark_refs WHERE note_uri = $1`, noteURI) + return err +} + func (db *DB) CommunityBookmarkExists(authorDID, targetHash, tagsJSON string) (bool, error) { - query := ` - SELECT 1 FROM notes - WHERE author_did = $1 - AND target_hash = $2 - AND uri LIKE 'at://%/community.lexicon.bookmarks.bookmark/%' - AND COALESCE(tags_json, '[]') = COALESCE($3, '[]') - LIMIT 1 - ` - normalized := tagsJSON - if normalized == "" { - normalized = "[]" - } var dummy int - err := db.QueryRow(query, authorDID, targetHash, normalized).Scan(&dummy) + err := db.QueryRow(` + SELECT 1 FROM community_bookmark_refs cbr + JOIN notes n ON n.uri = cbr.note_uri + WHERE n.author_did = $1 + AND n.target_hash = $2 + LIMIT 1 + `, authorDID, targetHash).Scan(&dummy) if err == sql.ErrNoRows { return false, nil }