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:
parent
a8f3aa6f7e
commit
485d01ce45
14 changed files with 151 additions and 71 deletions
|
|
@ -222,7 +222,7 @@ func TestAPIGetFave(t *testing.T) {
|
|||
|
||||
// Create a public fave directly.
|
||||
user, _ := users.GetByUsername("testuser")
|
||||
fave, _ := h.deps.Faves.Create(user.ID, "Test fave", "https://example.com", "", "public")
|
||||
fave, _ := h.deps.Faves.Create(user.ID, "Test fave", "https://example.com", "", "", "public")
|
||||
h.deps.Tags.SetFaveTags(fave.ID, []string{"test"})
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/faves/"+faveIDStr(fave.ID), nil)
|
||||
|
|
@ -257,7 +257,7 @@ func TestAPIPrivateFaveHiddenFromOthers(t *testing.T) {
|
|||
|
||||
// User A creates a private fave.
|
||||
userA, _ := users.Create("usera", "pass123", "user")
|
||||
fave, _ := h.deps.Faves.Create(userA.ID, "Secret", "", "", "private")
|
||||
fave, _ := h.deps.Faves.Create(userA.ID, "Secret", "", "", "", "private")
|
||||
|
||||
// User B tries to access it.
|
||||
cookieB := apiLogin(t, users, sessions, "userb", "pass123", "user")
|
||||
|
|
@ -276,7 +276,7 @@ func TestAPIPrivateFaveVisibleToOwner(t *testing.T) {
|
|||
h, mux, users, sessions := testAPIServer(t)
|
||||
|
||||
userA, _ := 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, _ := sessions.Create(userA.ID)
|
||||
cookieA := &http.Cookie{Name: "session", Value: tokenA}
|
||||
|
|
@ -295,7 +295,7 @@ func TestAPIUpdateFave(t *testing.T) {
|
|||
h, mux, users, sessions := testAPIServer(t)
|
||||
|
||||
user, _ := users.Create("testuser", "pass123", "user")
|
||||
fave, _ := h.deps.Faves.Create(user.ID, "Original", "https://old.com", "", "public")
|
||||
fave, _ := h.deps.Faves.Create(user.ID, "Original", "https://old.com", "", "", "public")
|
||||
token, _ := sessions.Create(user.ID)
|
||||
cookie := &http.Cookie{Name: "session", Value: token}
|
||||
|
||||
|
|
@ -322,7 +322,7 @@ func TestAPIUpdateFaveNotOwner(t *testing.T) {
|
|||
h, mux, users, sessions := testAPIServer(t)
|
||||
|
||||
userA, _ := users.Create("usera", "pass123", "user")
|
||||
fave, _ := h.deps.Faves.Create(userA.ID, "A's fave", "", "", "public")
|
||||
fave, _ := h.deps.Faves.Create(userA.ID, "A's fave", "", "", "", "public")
|
||||
|
||||
cookieB := apiLogin(t, users, sessions, "userb", "pass123", "user")
|
||||
|
||||
|
|
@ -341,7 +341,7 @@ func TestAPIDeleteFave(t *testing.T) {
|
|||
h, mux, users, sessions := testAPIServer(t)
|
||||
|
||||
user, _ := users.Create("testuser", "pass123", "user")
|
||||
fave, _ := h.deps.Faves.Create(user.ID, "Delete me", "", "", "public")
|
||||
fave, _ := h.deps.Faves.Create(user.ID, "Delete me", "", "", "", "public")
|
||||
token, _ := sessions.Create(user.ID)
|
||||
cookie := &http.Cookie{Name: "session", Value: token}
|
||||
|
||||
|
|
@ -368,7 +368,7 @@ func TestAPIDeleteFaveNotOwner(t *testing.T) {
|
|||
h, mux, users, sessions := testAPIServer(t)
|
||||
|
||||
userA, _ := users.Create("usera", "pass123", "user")
|
||||
fave, _ := h.deps.Faves.Create(userA.ID, "A's fave", "", "", "public")
|
||||
fave, _ := h.deps.Faves.Create(userA.ID, "A's fave", "", "", "", "public")
|
||||
|
||||
cookieB := apiLogin(t, users, sessions, "userb", "pass123", "user")
|
||||
|
||||
|
|
@ -386,9 +386,9 @@ func TestAPIListFaves(t *testing.T) {
|
|||
h, mux, users, sessions := testAPIServer(t)
|
||||
|
||||
user, _ := users.Create("testuser", "pass123", "user")
|
||||
h.deps.Faves.Create(user.ID, "Fave 1", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Fave 2", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Fave 3", "", "", "private")
|
||||
h.deps.Faves.Create(user.ID, "Fave 1", "", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Fave 2", "", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Fave 3", "", "", "", "private")
|
||||
token, _ := sessions.Create(user.ID)
|
||||
cookie := &http.Cookie{Name: "session", Value: token}
|
||||
|
||||
|
|
@ -413,7 +413,7 @@ func TestAPIListFavesPagination(t *testing.T) {
|
|||
|
||||
user, _ := users.Create("testuser", "pass123", "user")
|
||||
for i := 0; i < 5; i++ {
|
||||
h.deps.Faves.Create(user.ID, "Fave", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Fave", "", "", "", "public")
|
||||
}
|
||||
token, _ := sessions.Create(user.ID)
|
||||
cookie := &http.Cookie{Name: "session", Value: token}
|
||||
|
|
@ -443,7 +443,7 @@ func TestAPISearchTags(t *testing.T) {
|
|||
h, mux, users, _ := testAPIServer(t)
|
||||
|
||||
user, _ := 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{"golang", "goroutines", "python"})
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/tags?q=go", nil)
|
||||
|
|
@ -531,8 +531,8 @@ func TestAPIGetDisabledUser(t *testing.T) {
|
|||
func TestAPIGetUserFaves(t *testing.T) {
|
||||
h, mux, users, _ := testAPIServer(t)
|
||||
user, _ := users.Create("testuser", "pass123", "user")
|
||||
h.deps.Faves.Create(user.ID, "Public fave", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Private fave", "", "", "private")
|
||||
h.deps.Faves.Create(user.ID, "Public fave", "", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Private fave", "", "", "", "private")
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/v1/users/testuser/faves", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
|
@ -555,8 +555,8 @@ func TestAPIExport(t *testing.T) {
|
|||
h, mux, users, sessions := testAPIServer(t)
|
||||
|
||||
user, _ := users.Create("testuser", "pass123", "user")
|
||||
h.deps.Faves.Create(user.ID, "Fave 1", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Fave 2", "", "", "private")
|
||||
h.deps.Faves.Create(user.ID, "Fave 1", "", "", "", "public")
|
||||
h.deps.Faves.Create(user.ID, "Fave 2", "", "", "", "private")
|
||||
token, _ := sessions.Create(user.ID)
|
||||
cookie := &http.Cookie{Name: "session", Value: token}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue