feat(koreader): device bridge for ratings, reviews and reading-time stats
Release / build-and-push (push) Successful in 3m4s

Extends the device-authed koreader surface with the handoff item 4
Batch B contract:

- GET/POST /api/sync/koreader/rating — the device mirrors the .sdr
  summary's 5-star rating against the 1-10 server scale (POST 0 clears).
- GET/POST /api/sync/koreader/review — raw-markdown review mirror
  (summary.note); empty body clears, matching the first-party PUT.
- POST /api/sync/koreader/stats — the device pushes its CUMULATIVE
  total reading seconds; the server watermark (highest total ever
  converted, read from the koreader sessions' own device_metadata)
  converts only the delta into a reading_history session attributed
  to the koreader device. Reinstalls cannot double-count; sub-minute
  deltas ride forward (no session, no watermark movement); a lagging
  device clamps to zero instead of dragging the total back.

Book identity resolves through the existing priority chain (uuid →
sha256 → alias → title/author). GET binding uses query tags; POST
endpoints bind the ref+payload once (an earlier resolve-then-bind
draft drained the body).

Live-verified against the dev server with the koreader device token:
rating set/get/clear, review mirror, and the stats sequence
900→session, 960→delta session, 970→rides forward, 500→clamps; the
book's stats card and the By Device panel both pick up the split.
This commit is contained in:
John O'Keefe committed 2026-10-15 11:26:47 -04:00
1 parent 2117ec6181
commit 2517baedea
5 files changed
+308

No files matched your search

+5
View File
@@ -28,6 +28,11 @@ func registerSyncRoutes(cfg *Config) {
koreaderSync.GET("/metadata/:uuid", cfg.DeviceAuthMiddleware.Authenticate(cfg.KOReaderHandler.GetMetadata))
koreaderSync.GET("/library", cfg.DeviceAuthMiddleware.Authenticate(cfg.KOReaderHandler.GetLibrary))
koreaderSync.POST("/bookmarks", cfg.DeviceAuthMiddleware.Authenticate(cfg.KOReaderHandler.SyncBookmarks))
koreaderSync.GET("/rating", cfg.DeviceAuthMiddleware.Authenticate(cfg.KOReaderHandler.GetKOReaderRating))
koreaderSync.POST("/rating", cfg.DeviceAuthMiddleware.Authenticate(cfg.KOReaderHandler.SetKOReaderRating))
koreaderSync.GET("/review", cfg.DeviceAuthMiddleware.Authenticate(cfg.KOReaderHandler.GetKOReaderReview))
koreaderSync.POST("/review", cfg.DeviceAuthMiddleware.Authenticate(cfg.KOReaderHandler.SetKOReaderReview))
koreaderSync.POST("/stats", cfg.DeviceAuthMiddleware.Authenticate(cfg.KOReaderHandler.PushKOReaderStats))
// Kobo sync routes (device authentication required)
// Kobo devices use URL path: /api/sync/kobo/{token}/markup