feat(backend): favorite + latest-chapter fields, conditional updated_at
Adds favorite, latest_chapter and latest_chapter_num to the bookmark record, with an idempotent ALTER TABLE migration so the already-deployed database picks them up. updated_at now moves only when a bookmark is new or last_chapter_num changes. Clients order their list by updated_at, so favoriting a series or recording a newly published chapter must not disturb that order. Upsert consequently returns the row as stored and the handler echoes that rather than the request payload, since the candidate timestamp it sends is often discarded. Scanning also tolerates NULL in the optional columns, which a database created before this code can legitimately contain. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+8
-3
@@ -60,14 +60,19 @@ func (h *bookmarkHandler) put(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
b.UpdatedAt = time.Now().UnixMilli() // server-assigned, ignore client value
|
||||
// Candidate timestamp, not a decision: Upsert keeps the stored one unless
|
||||
// reading progress actually moved. Any client value is ignored.
|
||||
b.UpdatedAt = time.Now().UnixMilli()
|
||||
|
||||
if err := h.store.Upsert(b); err != nil {
|
||||
stored, err := h.store.Upsert(b)
|
||||
if err != nil {
|
||||
log.Printf("upsert: %v", err)
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, b)
|
||||
// Echo the stored row: clients adopt this as their cached copy, so it must
|
||||
// carry the authoritative updated_at rather than the candidate above.
|
||||
writeJSON(w, http.StatusOK, stored)
|
||||
}
|
||||
|
||||
// delete removes one bookmark. DELETE /bookmarks/{key}
|
||||
|
||||
Reference in New Issue
Block a user