Delete the kagane-specific cover path (#63) #73
Reference in New Issue
Block a user
Delete Branch "feat/63-delete-kagane-cover-path"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #63
Deletes the second way to reach a Cover. Since #62, every Site's cover bytes land in the content-addressed store at creation or on the poll, and the one public route serves them all — nothing needs the kagane proxy anymore.
What went
Bookmark.CoverURL()and both templates' use of it. Cards and chrome now render.Cover— the wire value — and nothing else.Bookmark.CoverSourcewas dead onceCoverURLwent, so it and itsbookmarkColumnsentry are gone too.GET /img/kagane/{id},web.CoverFetcher,coverIDRe, and the wholeinternal/web/cover.go.store.KaganeImageID,GetKaganeCover,PutKaganeCover,kaganeCoverSourceURL,kaganeCoverRe.fetchCoverBytesno longer takes asiteargument and no longer names a Site. The URL shape kagane's API publishes is claimed by the browser module itself —kaganeImageURLRe+browserCoverURLlive inlatest/browser.gowith the rest of the per-Site knowledge — andBrowserFetcher.Imageis now URL-driven (it validates the URL it will navigate to, same SSRF discipline as before). The no-plain-TLS-fallback rule for a claimed URL is preserved: a claimed address with no browser is an error, never a challenge-page fetch.What stayed (deliberately)
BrowserFetcher.Imageand the browser-backed acquisition path: kagane genuinely serves cover bytes behind the challenge +cross-origin-resource-policy: same-origin, so the sidecar remains the only fetcher for them — it just routes by URL claim now instead of by Site name.fetcherFor's per-Site page routing (kagane/novelfull page fetches) — that is the page path, not a cover path.Acceptance criteria
TestPublicCoverRejectsUnknownAddress), non-image content types never echoed (TestPublicCoverNeverEchoesNonImage— new; the store-side gate was already pinned byTestCoverStoreAcceptsAnySourceURL). Store reopen-persistence and filesystem content-addressing tests rewritten againstPutCover/GetCover, no guarantee lost.grep kagane backend: store/web/templates/api are clean; remaining hits arelatest/browser.go+latest/sites.go, tests, docs)b.cover— untouched, it never had a kagane path)go test ./...greenVerification
go vet ./...cleango test ./...— all packages pass (root 16.9s, latest 12.7s, store 12.7s, web 0.004s)CGO_ENABLED=0 go buildproduces the static binaryTestPublicCoverServesStoredBytesUnauthenticated,TestPublicCoverRejectsUnknownAddress(unknown/malformed/traversal/empty),TestPublicCoverNeverEchoesNonImage,TestListRendersAcquiredCover,TestAcquireKaganeCoverThroughBrowser,TestRunOncePrefetchesKaganeCover,TestRunOnceRoutesNonKaganeCoverToPublicFetcherall pass; the threeSMOKE_*tests skip without the browser sidecar, as designedLive browser verification of the "web UI and panel render Covers for all six Sites" criterion is being run separately with Playwright against real Site pages and a locally mocked backend.
- Move the kagane cover URL shape into the extraction module (sites.go): browserOnlyCoverURL + kaganeImageURLRe now own the claim; the byte-fetch router and BrowserFetcher.Image reference it. One shape gate for producer and fetcher (the id regex is folded into the full-URL match), so no Site name appears in a cover path outside the extraction module and the producer cannot emit an address the fetch would refuse. - Restore the serving-boundary guarantee: GET /covers/{addr} re-checks the stored media type via store.CoverContentType and 404s a poisoned row; TestPublicCoverNeverEchoesNonImage now seeds one directly behind the write gate and pins the refusal where bytes leave. - Restore the SSRF rationale (client-supplied stored URL, headless browser as a strong primitive) on the URL regex.