sulthan c597bb5d5b fix(userscript): move boot after TEMPLATE/CSS consts to avoid TDZ crash
init() ran at line 514 (document.body exists at @run-at document-idle),
but buildUI() reads the TEMPLATE/CSS consts declared lower in the IIFE.
Accessing them before initialization threw ReferenceError: Cannot access
'CSS' before initialization, so the script died before mounting the FAB
and no UI appeared in Cromite. Move the boot invocation to the end of the
IIFE, after both consts are initialized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 18:46:10 +07:00
2026-07-24 16:23:24 +07:00
2026-07-24 16:23:24 +07:00
2026-07-24 16:23:24 +07:00
2026-07-24 16:23:24 +07:00
2026-07-24 16:23:24 +07:00

Manga Bookmark

Track manga read-progress on asurascans.com (a.k.a. asuracomic.net) and demonicscans.org from a phone (Bromite / mobile Chromium), synced to a self-hosted Go backend so bookmarks unify across both sites and all devices.

Two parts:

  • backend/ — tiny Go (net/http + pure-Go SQLite) sync service. 4 routes, static binary, distroless container.
  • userscript/manga-bookmark.user.js — single Bromite-compatible userscript (no GM_* APIs) that injects an on-page bookmark UI and syncs via fetch().
Bromite userscript (isolated world, Shadow DOM UI, localStorage cache)
    -- fetch() HTTPS -->  reverse proxy (TLS + CORS)  -->  Go net/http  -->  SQLite (volume)

1. Backend

Config (env)

Var Default Notes
API_TOKEN (required) Bearer token shared with the userscript.
ALLOWED_ORIGINS Asura + Demonic origins Comma-separated CORS allowlist.
DB_PATH /data/bookmarks.db SQLite file location.
PORT 8080 Plain HTTP; TLS terminated by the proxy.

Endpoints

Method Path Auth Description
GET /bookmarks Bearer All bookmarks (single-user).
PUT /bookmarks/{key} Bearer Upsert one series; server sets updated_at.
DELETE /bookmarks/{key} Bearer Remove one.
GET /healthz none 200 ok.

key is <site>:<series_id> — e.g. asura:trash-of-the-counts-family-f886a8af or demonic:Infinite-Level-Up-in-Murim. Sync is last-write-wins.

Develop / test

cd backend
go test ./...                          # unit + handler tests
CGO_ENABLED=0 go build                 # static binary

Run the stack

cp .env.example .env
# edit .env: set API_TOKEN (openssl rand -hex 32)

docker compose up -d --build           # binds 127.0.0.1:8080

Smoke test:

TOKEN=$(grep '^API_TOKEN=' .env | cut -d= -f2)
curl -s localhost:8080/healthz                                    # ok
curl -s localhost:8080/bookmarks                                  # 401
curl -s -H "Authorization: Bearer $TOKEN" localhost:8080/bookmarks # []
curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"title":"Test","last_chapter":"Chapter 1","last_chapter_num":1}' \
  localhost:8080/bookmarks/asura:test-1
curl -s -i -X OPTIONS -H 'Origin: https://asurascans.com' \
  -H 'Access-Control-Request-Method: PUT' \
  localhost:8080/bookmarks/asura:test-1 | grep -i access-control   # 204 + CORS headers

Deploy behind your reverse proxy

Route https://manga-api.<domain> → the service on :8080 (TLS at the proxy).

  • Host proxy (nginx/Caddy on the host): the base compose already binds 127.0.0.1:8080; point the proxy proxy_pass http://127.0.0.1:8080;.

  • Docker proxy (Traefik/nginx in a container on its own network): use the override, which drops the published port and joins the shared network:

    docker network create proxy            # once, if it doesn't exist
    docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
    

    Set PROXY_NETWORK in .env if your network isn't named proxy.

Verify: https://manga-api.<domain>/healthz returns ok over valid TLS (no mixed-content), and an OPTIONS preflight from a real site origin returns the CORS headers.


2. Userscript

Configure

Edit the config block at the top of userscript/manga-bookmark.user.js:

const API_BASE = "https://manga-api.<domain>"; // no trailing slash
const API_TOKEN = "<same token as backend>";

The token lives in the userscript's isolated world — the manga sites' own JS cannot read it.

Install on Bromite (mobile)

Bromite runs Chromium's native userscript engine (no Tampermonkey needed):

  1. Bromite → Settings → User scripts → enable user scripts (allow the permission prompt).
  2. Save the configured manga-bookmark.user.js to the device (or open its raw URL). Bromite detects the .user.js and offers to install it.
  3. Confirm the install; the @match list covers both sites.
  4. Open a series on either site — a 📑 button appears bottom-right.

Exact menu wording varies by Bromite build; if "User scripts" is absent, update Bromite or use a build with userscript support.

Desktop iteration (optional)

The script is GM_*-free, so it also runs in Tampermonkey/Violentmonkey on desktop for faster testing — install the same file unchanged.

Use

  • Bookmark: on a series or chapter page, open the panel → + Bookmark this.
  • Auto-progress: opening a chapter of a bookmarked series records it when the chapter number ≥ the stored one (re-reading older chapters never regresses progress; unparseable numbers set the current chapter).
  • Manual override: panel → Edit on any row forces a specific chapter.
  • Continue: jumps to the last-read chapter (or the series page).
  • Bookmarks made on Asura appear when the panel is opened on Demonic, and vice versa — the backend is the shared store.

Offline / backend down: changes are cached in localStorage and retried on the next successful load (last-write-wins).


Adapter reference (verified live 2026-07-24)

The site adapters key everything off URL regex, with title/cover from og:title / og:image. Confirmed against live pages via Playwright:

Site Series URL Chapter URL series_id
Asura (asurascans.com; asuracomic.net 301s here) /comics/<slug-hash> /comics/<slug-hash>/chapter/<n> <slug-hash>
Demonic (demonicscans.org) /manga/<slug> /title/<slug>/chapter/<n>/<page> (chaptered.php?manga=<id>&chapter=<n> 301s here) <slug>

Notes:

  • Asura og:title carries a Chapter N - Read Online \| Asura Scans suffix that the adapter strips; Demonic chapter og:title is <Title> Chapter N.
  • Demonic's <slug> is identical on /manga/… and the canonical /title/… reader, so a bookmark set from the series page and the auto-update from the reader resolve to the same key.
  • Asura showed no Next.js markers on the live site, so navigation uses a framework-agnostic watcher (history patch + polling) rather than a Next-only hook — works for client-routed and full-reload sites alike.

If either site changes its URL shape, update the regex in the matching adapter in userscript/manga-bookmark.user.js and re-verify.

S
Description
No description provided
Readme 13 MiB
Languages
Go 75.9%
JavaScript 15.1%
CSS 4.6%
HTML 3.6%
Shell 0.5%
Other 0.3%