Move cover bytes to content-addressed filesystem storage #65

Merged
sulthan merged 2 commits from issue-56-cover-filesystem into main 2026-08-10 00:11:21 +07:00
Owner

Refs #56

Summary

Moves Kagane cover bytes out of Postgres bytea storage into an immutable, content-addressed filesystem store. Reader-visible behavior remains unchanged: the existing session-gated route serves stored bytes, missing bytes use the existing browser fetch path, and no browser still returns a missing cover.

Changes

  • Added migration 0008, which drops the legacy covers table and recreates it with only address, path, and content_type. Existing byte rows are intentionally dropped.
  • Added SHA-256 source-URL addressing with two-level sharding (ab/cd/<sha256>). Writes use a temp file plus atomic link; reads validate the stored relative path before opening it.
  • Made COVER_DIR required in runtime config and Compose. Compose passes it as a Docker build argument and volume target, so custom durable paths keep image ownership, runtime config, and the named cover-data volume aligned.
  • Updated every store.Open caller and documented configuration, deployment, backup, and troubleshooting behavior.
  • Added filesystem, restart, migration-drop, no-browser, and content-addressing coverage.

Verification

  • go test ./...
  • CGO_ENABLED=0 go build ./...
  • docker build --build-arg COVER_DIR=/data/covers -t manga-bookmark-cover-check-custom ./backend
  • docker compose config --format json confirms custom COVER_DIR is the volume target
  • git diff --check origin/main
  • LSP diagnostics clean for touched Go files

Parents #47 and #55 remain open as required by #56.

Refs #56 ## Summary Moves Kagane cover bytes out of Postgres bytea storage into an immutable, content-addressed filesystem store. Reader-visible behavior remains unchanged: the existing session-gated route serves stored bytes, missing bytes use the existing browser fetch path, and no browser still returns a missing cover. ## Changes - Added migration 0008, which drops the legacy `covers` table and recreates it with only `address`, `path`, and `content_type`. Existing byte rows are intentionally dropped. - Added SHA-256 source-URL addressing with two-level sharding (`ab/cd/<sha256>`). Writes use a temp file plus atomic link; reads validate the stored relative path before opening it. - Made `COVER_DIR` required in runtime config and Compose. Compose passes it as a Docker build argument and volume target, so custom durable paths keep image ownership, runtime config, and the named `cover-data` volume aligned. - Updated every `store.Open` caller and documented configuration, deployment, backup, and troubleshooting behavior. - Added filesystem, restart, migration-drop, no-browser, and content-addressing coverage. ## Verification - `go test ./...` - `CGO_ENABLED=0 go build ./...` - `docker build --build-arg COVER_DIR=/data/covers -t manga-bookmark-cover-check-custom ./backend` - `docker compose config --format json` confirms custom `COVER_DIR` is the volume target - `git diff --check origin/main` - LSP diagnostics clean for touched Go files Parents #47 and #55 remain open as required by #56.
sulthan added 1 commit 2026-08-09 23:50:55 +07:00
sulthan added 1 commit 2026-08-09 23:53:43 +07:00
sulthan merged commit e8d1cba6c5 into main 2026-08-10 00:11:21 +07:00
Sign in to join this conversation.