Add comix.to and kagane.to support #13
Reference in New Issue
Block a user
Delete Branch "feat/add-kagane-comix"
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?
Tracks read progress on comix.to and kagane.to alongside asura and demonic, in both the userscript and the backend.
Implements
docs/superpowers/plans/2026-08-03-comix-kagane-support.md.Userscript
comixadapter —/title/<id>-<slug>; only the id prefix is identity (the slug follows the title). Noog:image, so the cover is matched byalt.kaganeadapter — reader URLs are uuids with no chapter number, so it comes out ofog:title; anchor scanning is structurally impossible, replaced bylatestChapterFromApiagainst kagane's same-origin JSON API.seriesIdthreaded throughlatestChapterFromAnchorsso comix can scope its scan to its own series and a recommendation strip cannot win the maximum.@matchfor both hosts, panel chips, v1.6.0.Backend
latestChapterFromcases: comix parses the SSR JSON state blob (latestChapterUrl, scoped to the series id); kagane parses API JSON (chapter_no).Poller.BrowserFetchwithfetcherFor(site)routes kagane to a browser fetcher. Nil means kagane is not polled at all — never a fallback to the TLS fetcher, which would only ever retrieve a challenge page.BrowserFetcher: chromedp against aheadless-shellsidecar. kagane sits behind a Cloudflare JS challenge that no TLS fingerprint clears, and the request is made inside the page rather than by replayingcf_clearance.BROWSER_WS_URLwiring, sidecar in both compose files (noports:, dedicated non-external network), Dockerfile ongolang:1.26-alpine— chromedp requires go 1.26.--comix/--kaganetokens in both colour branches.Notes for review
series_urlis client-supplied and a headless browser is a strong SSRF primitive, so kagane's host is pinned twice: infetchableSeriesURLand again inkaganeAPIURL.ALLOWED_ORIGINSnow includes both new origins. Without it every write from comix/kagane silently fails CORS preflight, parks in the retry queue, and drops at the cap.Verification
221 backend tests, 32 userscript tests, static
CGO_ENABLED=0build, both compose configs.Two gaps, both real:
og:title), both chips opening the right sites.All Go files lived flat in backend/ as one package main. Move store, latest-chapter polling, sessions, HTTP middleware, the JSON API, the userscript handler, and the web UI (with its templates/static assets) into backend/internal/{store,latest,session,httpmw,api,userscript,web}, each with an exported API. main.go becomes the composition root wiring them into newRouter; root-level tests cover the assembled router while package-local tests cover unit behavior. Update Dockerfile/.dockerignore for the new internal/ tree and CLAUDE.md to describe the layout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>Extend the test harness's document stub with a querySelectorAll("img[alt]") fake (module-level pageImages fixture, mirroring metaTags), then assert on p.cover for a matching alt and for no match. Previously the guard in coverFromPage() always short-circuited under test since querySelectorAll didn't exist on the stub, so the alt-matching loop had zero coverage.