refactor(repo): conform structure to AGENTS.md (web->frontend, internal/api->cmd/webui/{api,handlers}, docs/README+CHANGELOGs, module .gitignores, drop stray library/ and committed debug bins)
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
All non-WebUI user-visible changes. Newest version on top.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
- Repo structure conformed to `AGENTS.md`: `web/` renamed to `frontend/`; backend HTTP layer moved from `internal/api` to `cmd/webui/{api,handlers}` (`cmd/server` → `cmd/webui`); README/CHANGELOGs relocated under `docs/` (`README_zh.md` added as Chinese mirror); module-level `.gitignore`s added (`backend/`, `deploy/`); stray root `library/` removed (book files live in `deploy/api/storage/`); debug binaries untracked.
|
||||
- Docs: `docs/README.md` is now the English primary; previous Chinese README mirrored to `docs/README_zh.md`.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Changelog (WebUI)
|
||||
|
||||
All WebUI user-visible changes. Newest version on top.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
_No WebUI user-visible changes in this release; the structural refactor (`web/` → `frontend/`) does not affect the served app._
|
||||
@@ -0,0 +1,75 @@
|
||||
# Book & Comic Library
|
||||
|
||||
Personal book/comic library: Go+Gin backend (scan/upload ingestion, multi-user JWT, reading progress, disk+Redis cache) + Docker Compose deployment. Design: `superpowers/specs/2026-09-04-book-comic-library-design.md`; deployment spec: `superpowers/specs/2026-09-07-docker-deploy-spec-design.md`. Chinese mirror: `README_zh.md`.
|
||||
|
||||
## Deploy mode
|
||||
|
||||
- release: `deploy/docker-compose.yml` — multi-stage `backend|frontend/Dockerfile.prod` (target runner) compiled artifacts, no source mounts; infra ports stay on the internal network.
|
||||
- dev: `deploy/docker-compose.dev.yml` — all four services containerized, source mounted as `../backend:/app` and `../frontend:/app` (frontend with an anonymous `node_modules` volume), `target: dev` images; api runs `go mod download && dlv debug ./cmd/webui` (hot restart without rebuild, delve :2345); infra exposed to host PG 5432 / Redis 6379; healthcheck-gated startup.
|
||||
|
||||
## Volume Mount
|
||||
|
||||
- Shared named volumes: `booklib_postgres_data`, `booklib_redis_data` (identical names in both composes, so dev/release see the same data; only `down -v` clears them).
|
||||
- Config/log bind mounts: `deploy/nginx/{nginx.conf,conf.d/default.conf}`, `deploy/redis/redis.conf` (`:ro`) and `deploy/logs/nginx` — all rendered by `deploy/prepare.sh` (templates are the `*.tpl` files beside each output); **wrong/missing config in the container = you forgot to rerun it**.
|
||||
- File storage: `deploy/api/storage` → container `/data/books` (cache writes to `/data/books/cache`).
|
||||
|
||||
## Run it (production shape)
|
||||
|
||||
```bash
|
||||
cp deploy/.env.example deploy/.env # set JWT_SECRET, ADMIN_USER, ADMIN_PASSWORD (>= 8 chars; seed skips and logs below 8)
|
||||
deploy/prepare.sh
|
||||
docker compose -f deploy/docker-compose.yml up -d --build
|
||||
bash scripts/smoke.sh && bash scripts/smoke-web.sh
|
||||
```
|
||||
|
||||
- web: `http://localhost:8080` (change via `WEB_PORT`); the API goes through the nginx `/api/` prefix reverse proxy to stateless api replicas (`--scale api=N`).
|
||||
- Drop raw books into `deploy/api/storage/` (mounted at `/data/books`); the scanner ingests periodically (default 60s).
|
||||
- nginx access/error logs: `deploy/logs/nginx/`.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
deploy/prepare.sh
|
||||
docker compose -f deploy/docker-compose.dev.yml up -d --build
|
||||
```
|
||||
|
||||
- Frontend: http://localhost:5173 (vite, HMR works directly; `/api` proxied to the in-container api).
|
||||
- After editing Go code: `docker compose -f deploy/docker-compose.dev.yml restart api` (recompiles the mounted source, no rebuild).
|
||||
- Breakpoint debugging: delve headless at `localhost:2345` (VSCode launch: `{"type":"go","request":"attach","mode":"remote","host":"localhost","port":2345,"substitutePath":[{"from":"${workspaceFolder}/backend","to":"/app"}]}`; continue via dlv commands after hitting a breakpoint).
|
||||
- Run tests against directly reachable infra: PG `localhost:5432` (lib/lib/lib), Redis `localhost:6379`:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
export DATABASE_URL='postgres://lib:lib@localhost:5432/lib?sslmode=disable'
|
||||
export REDIS_URL='redis://localhost:6379'
|
||||
go vet ./... && gofmt -l .
|
||||
go test -p 1 -count=1 ./...
|
||||
```
|
||||
|
||||
`-p 1` is required: integration tests share one PG database and each clears tables with `DELETE FROM ...` — running in parallel deletes each other's data and fails randomly. Bring the dev stack down with `down` (not `rm`), or the anonymous node_modules volume becomes an orphan. Tests that need PG/Redis skip automatically when absent; Redis downtime doesn't break functionality (the whole chain degrades to miss/passthrough, see spec §9).
|
||||
|
||||
Frontend gate: `cd frontend && npm run check` (tsc + vitest + vite build).
|
||||
|
||||
## Old-volume migration (one-off, upgrading from the previous deploy layout)
|
||||
|
||||
```bash
|
||||
# old PG data → new shared volume
|
||||
docker run --rm -v book-comic-library_pgdata:/from -v booklib_postgres_data:/to alpine cp -a /from/. /to/
|
||||
# the old cache volume is derived data (covers/unzipped pages), just drop it (auto-rebuilt)
|
||||
docker volume rm book-comic-library_pgdata book-comic-library_cache
|
||||
```
|
||||
|
||||
Book files: move the contents of the old host `./library/` into `deploy/api/storage/`.
|
||||
|
||||
## Trusted proxies & rate limiting
|
||||
|
||||
- nginx lives inside the compose network, so api's `ClientIP` only trusts `TRUSTED_PROXY_CIDRS` (comma-separated CIDRs, default `172.16.0.0/12`, the compose subnet). Spoofed external `X-Forwarded-For` can't bypass rate-limit buckets; change this env when the deploy network changes.
|
||||
- Login is limited to 5 attempts/min/IP **counted per attempt, successful logins included** — brute force and normal high-frequency login share the budget.
|
||||
|
||||
## Read this before changing the schema
|
||||
|
||||
`db.Migrate` only runs the `CREATE TABLE IF NOT EXISTS` statements of `schema.sql` — column adds/changes **do not take effect** on existing databases. Before any column change, introduce a `schema_migrations` version table + ordered migrations, otherwise old deployments silently run on the old shape.
|
||||
|
||||
## PWA
|
||||
|
||||
Immutable assets (covers/CBZ pages/raw files) are SW cache-first; read content stays available offline; logging out clears the SW cache.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Book & Comic Library
|
||||
|
||||
个人书库/漫画库:Go+Gin 后端(扫描/上传入库、多用户 JWT、阅读进度、磁盘+Redis 缓存)+ Docker Compose 部署。设计见 `superpowers/specs/2026-09-04-book-comic-library-design.md`;部署规范见 `superpowers/specs/2026-09-07-docker-deploy-spec-design.md`。英文镜像:`README.md`。
|
||||
|
||||
## Deploy mode
|
||||
|
||||
- release:`deploy/docker-compose.yml` — 多阶段 `backend|frontend/Dockerfile.prod`(target runner)编译产物,不挂源码;infra 端口只在容器网络。
|
||||
- dev:`deploy/docker-compose.dev.yml` — 四服务全容器化,源码挂 `../backend:/app`、`../frontend:/app`(web 服务带匿名 `node_modules` 卷),`target: dev` 镜像;api 跑 `go mod download && dlv debug ./cmd/webui`(热重启不 rebuild,delve :2345);infra 暴露宿主 PG 5432 / Redis 6379;healthcheck 门控。
|
||||
|
||||
## Volume Mount
|
||||
|
||||
- 共享 named volumes:`booklib_postgres_data`、`booklib_redis_data`(两个 compose 同名,dev/release 看到同一份数据;仅 `down -v` 清除)。
|
||||
- 配置/日志绑定挂载:`deploy/nginx/{nginx.conf,conf.d/default.conf}`、`deploy/redis/redis.conf`(`:ro`)与 `deploy/logs/nginx` —— 全部来自 `deploy/prepare.sh`(模板在各产物同目录的 `*.tpl`),**容器里配置不对/缺失 = 忘了重跑它**。
|
||||
- 文件存储:`deploy/api/storage` → 容器 `/data/books`(缓存写 `/data/books/cache`)。
|
||||
|
||||
## 跑起来(生产形态)
|
||||
|
||||
```bash
|
||||
cp deploy/.env.example deploy/.env # 填 JWT_SECRET、ADMIN_USER、ADMIN_PASSWORD(≥8 位,低于 8 位 seed 会跳过并 log)
|
||||
deploy/prepare.sh
|
||||
docker compose -f deploy/docker-compose.yml up -d --build
|
||||
bash scripts/smoke.sh && bash scripts/smoke-web.sh
|
||||
```
|
||||
|
||||
- web: `http://localhost:8080`(`WEB_PORT` 可改),API 走 nginx `/api/` 前缀反代到无状态 api 副本(`--scale api=N`)。
|
||||
- 原始书放进 `deploy/api/storage/`(挂到 `/data/books`),scanner 周期入库(默认 60s)。
|
||||
- nginx access/error 日志:`deploy/logs/nginx/`。
|
||||
|
||||
## 开发
|
||||
|
||||
```bash
|
||||
deploy/prepare.sh
|
||||
docker compose -f deploy/docker-compose.dev.yml up -d --build
|
||||
```
|
||||
|
||||
- 前端: http://localhost:5173(vite,HMR 直接生效;`/api` 代理到容器内 api)。
|
||||
- Go 改码后:`docker compose -f deploy/docker-compose.dev.yml restart api`(重编译挂载源码,无需 rebuild)。
|
||||
- 断点调试:delve headless 在 `localhost:2345`(VSCode launch:`{"type":"go","request":"attach","mode":"remote","host":"localhost","port":2345,"substitutePath":[{"from":"${workspaceFolder}/backend","to":"/app"}]}`;命中断点后用 dlv 命令继续)。
|
||||
- 直连基础设施跑测试:PG `localhost:5432`(lib/lib/lib)、Redis `localhost:6379`:
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
export DATABASE_URL='postgres://lib:lib@localhost:5432/lib?sslmode=disable'
|
||||
export REDIS_URL='redis://localhost:6379'
|
||||
go vet ./... && gofmt -l .
|
||||
go test -p 1 -count=1 ./...
|
||||
```
|
||||
|
||||
`-p 1` 是必须的:集成测试共用同一个 PG 库,各自 `DELETE FROM ...` 清表——并行跑会互相删数据导致随机失败。dev 栈起停用 `down`(不是 `rm`),否则匿名 node_modules 卷成孤儿。无 PG/Redis 时依赖它们的测试自动 skip;Redis 挂掉不影响功能(全链路降级为 miss/放行,见 spec §9)。
|
||||
|
||||
前端门槛:`cd frontend && npm run check`(tsc + vitest + vite build)。
|
||||
|
||||
## 旧卷迁移(一次性,升级自上一版部署)
|
||||
|
||||
```bash
|
||||
# 老 PG 数据 → 新共享卷
|
||||
docker run --rm -v book-comic-library_pgdata:/from -v booklib_postgres_data:/to alpine cp -a /from/. /to/
|
||||
# 老 cache 卷是封面/解压派生数据,直接丢弃(自动重建)
|
||||
docker volume rm book-comic-library_pgdata book-comic-library_cache
|
||||
```
|
||||
|
||||
书库文件:原宿主 `./library/` 的内容移入 `deploy/api/storage/`。
|
||||
|
||||
## 可信代理与限流
|
||||
|
||||
- nginx 在 compose 网络内,api 的 `ClientIP` 只信 `TRUSTED_PROXY_CIDRS`(逗号分隔 CIDR,默认 `172.16.0.0/12`,即 compose 网段)。外部伪造 `X-Forwarded-For` 换不掉限流桶;换部署网络时改这个 env。
|
||||
- 登录限流 5 次/分钟/IP **按尝试计数,成功登录也计**——爆破和正常高频登录同账。
|
||||
|
||||
## 改 schema 前必读
|
||||
|
||||
`db.Migrate` 只执行 `schema.sql` 的 `CREATE TABLE IF NOT EXISTS`——对已存在的库**加列/改列不会生效**。任何列变更之前,必须先引入 `schema_migrations` 版本表 + 有序迁移脚本,否则老部署会静默跑在旧结构上。
|
||||
|
||||
## PWA
|
||||
|
||||
不可变资源(封面/CBZ 页/原文件)SW cache-first,读过的内容离线可翻;登出会清 SW 缓存。
|
||||
Reference in New Issue
Block a user