feat: add notes field to favorites and enhance OG meta tags

Add an optional long-form "notes" text field to each favorite for
reviews, thoughts, or extended descriptions. The field is stored in
SQLite via a new migration (002_add_fave_notes.sql) and propagated
through the entire stack:

- Model: Notes field on Fave struct
- Store: All SQL queries (Create, GetByID, Update, list methods,
  scanFaves) updated with notes column
- Web handlers: Read/write notes in create, edit, update forms
- API handlers: Notes in create, update, get, import request/response
- Export: Notes included in both JSON and CSV exports
- Import: Notes parsed from both JSON and CSV imports
- Feed: Notes used as Atom feed item summary when present
- Form template: New textarea between URL and image fields
- Detail template: Display notes, enhanced og:description with
  cascade: notes (truncated) → URL → generic fallback text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ole-Morten Duesund 2026-04-04 00:40:08 +02:00
commit 485d01ce45
14 changed files with 151 additions and 71 deletions

View file

@ -220,7 +220,7 @@ func TestPrivateFaveHiddenFromOthers(t *testing.T) {
// User A creates a private fave.
userA, _ := h.deps.Users.Create("usera", "pass123", "user")
fave, _ := h.deps.Faves.Create(userA.ID, "Secret fave", "", "", "private")
fave, _ := h.deps.Faves.Create(userA.ID, "Secret fave", "", "", "", "private")
// User B tries to view it.
cookieB := loginUser(t, h, "userb", "pass123", "user")
@ -239,7 +239,7 @@ func TestPrivateFaveVisibleToOwner(t *testing.T) {
h, mux := testServer(t)
userA, _ := h.deps.Users.Create("usera", "pass123", "user")
fave, _ := h.deps.Faves.Create(userA.ID, "My secret", "", "", "private")
fave, _ := h.deps.Faves.Create(userA.ID, "My secret", "", "", "", "private")
tokenA, _ := h.deps.Sessions.Create(userA.ID)
cookieA := &http.Cookie{Name: "session", Value: tokenA}
@ -290,7 +290,7 @@ func TestTagSearchEndpoint(t *testing.T) {
// Create some tags via faves.
user, _ := h.deps.Users.Create("testuser", "pass123", "user")
fave, _ := h.deps.Faves.Create(user.ID, "Test", "", "", "public")
fave, _ := h.deps.Faves.Create(user.ID, "Test", "", "", "", "public")
h.deps.Tags.SetFaveTags(fave.ID, []string{"music", "movies", "manga"})
req := httptest.NewRequest("GET", "/tags/search?q=mu", nil)
@ -310,7 +310,7 @@ func TestFeedGlobal(t *testing.T) {
h, mux := testServer(t)
user, _ := h.deps.Users.Create("testuser", "pass123", "user")
h.deps.Faves.Create(user.ID, "Public fave", "", "", "public")
h.deps.Faves.Create(user.ID, "Public fave", "", "", "", "public")
req := httptest.NewRequest("GET", "/feed.xml", nil)
rr := httptest.NewRecorder()