c985df7579
Build and Push Reader API Image / docker (push) Successful in 1m3s
- Updated `.env.example` to remove MongoDB configuration. - Revised `ARCHITECTURE.md` to reflect changes in data orchestration and storage strategy. - Removed `CHAPTER_SAVE_DEBUG.md` and `FIXES_APPLIED.md` as they are no longer relevant. - Updated `CONTRACT.md` to include new error codes. - Adjusted endpoint documentation in `CROSS_REPO_ENDPOINT_MATRIX.md` and `README.md` to align with the new import flow. - Removed legacy import scripts and tables from the codebase.
214 lines
5.3 KiB
Markdown
214 lines
5.3 KiB
Markdown
# reader-api (FastAPI + UV)
|
|
|
|
Shared backend API for both:
|
|
- Web app: reader
|
|
- Mobile app: reader-app
|
|
|
|
This project is Python-first (FastAPI), with production-focused Docker setup and healthcheck.
|
|
|
|
## Stack
|
|
|
|
- Python 3.11+
|
|
- FastAPI
|
|
- UV (package manager / runner)
|
|
- PostgreSQL (metadata, user data, chapter refs; chapter text stored via NAS/files — see `NAS_CONTENT_ROOT` / storage layer)
|
|
|
|
## API Base URL
|
|
|
|
- Local dev: http://localhost:8000
|
|
- Healthcheck: GET /api/health
|
|
|
|
## Environment
|
|
|
|
Create `.env` from `.env.example`.
|
|
|
|
Required keys:
|
|
|
|
```env
|
|
DATABASE_URL=postgresql://reader:reader@localhost:5432/reader
|
|
NEXTAUTH_SECRET=replace-with-strong-secret
|
|
MOBILE_JWT_SECRET=replace-with-strong-secret
|
|
# Comma-separated allowed Google OAuth client IDs
|
|
GOOGLE_CLIENT_ID=web-client-id.apps.googleusercontent.com,android-client-id.apps.googleusercontent.com
|
|
CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
|
|
APP_ENV=development
|
|
```
|
|
|
|
## Dev Setup (UV)
|
|
|
|
1. Install UV
|
|
|
|
```bash
|
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
```
|
|
|
|
2. Sync dependencies
|
|
|
|
```bash
|
|
uv sync
|
|
```
|
|
|
|
3. Run API in dev mode
|
|
|
|
```bash
|
|
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
|
```
|
|
|
|
4. Verify health
|
|
|
|
```bash
|
|
curl http://localhost:8000/api/health
|
|
```
|
|
|
|
## Docker Compose
|
|
|
|
Current `docker-compose.yml` supports a unified deployment for both web + API.
|
|
|
|
### Web + API (use external DBs)
|
|
|
|
```bash
|
|
docker compose up -d --build api web
|
|
```
|
|
|
|
Required env for web OAuth in `.env`:
|
|
|
|
```env
|
|
WEB_GOOGLE_CLIENT_ID=web-client-id.apps.googleusercontent.com
|
|
WEB_GOOGLE_CLIENT_SECRET=replace-with-web-google-client-secret
|
|
```
|
|
|
|
### Full local stack (API local + Postgres)
|
|
|
|
```bash
|
|
docker compose --profile localdb up -d --build api-local postgres
|
|
```
|
|
|
|
Notes:
|
|
- `api` listens on port `8000` and is intended for external DB deployments.
|
|
- `api-local` listens on port `8001` and automatically points to `postgres` container.
|
|
- `web` listens on port `3000` and calls API internally through `http://api:8000`.
|
|
|
|
### NAS mount points (chapter content + EPUB source)
|
|
|
|
API containers now reserve two mount folders:
|
|
|
|
- `/data/content`: converted chapter files (`txt` + `raw_html`)
|
|
- `/data/epub-source`: source EPUB library
|
|
|
|
Default env mapping (already wired in compose):
|
|
|
|
```env
|
|
NAS_CONTENT_ROOT=/data/content
|
|
EPUB_SOURCE_ROOT=/data/epub-source
|
|
```
|
|
|
|
If you want to bind to host folders for local testing:
|
|
|
|
```yaml
|
|
services:
|
|
api:
|
|
volumes:
|
|
- /absolute/local/path/content:/data/content
|
|
- /absolute/local/path/epub-source:/data/epub-source
|
|
```
|
|
|
|
If you want to use NFS-backed docker volumes, define them under `volumes:`. Example:
|
|
|
|
```yaml
|
|
volumes:
|
|
nas_chapter_content:
|
|
driver: local
|
|
driver_opts:
|
|
type: nfs
|
|
o: addr=100.93.79.10,nolock,soft,rw
|
|
device: ":/volume2/apps/reader-content"
|
|
|
|
nas_epub_source:
|
|
driver: local
|
|
driver_opts:
|
|
type: nfs
|
|
o: addr=100.93.79.10,nolock,soft,rw
|
|
device: ":/volume2/apps/reader-epub"
|
|
```
|
|
|
|
For your EPUB structure (folder per novel, multiple `.epub` parts inside), mount the parent folder to `/data/epub-source`.
|
|
|
|
## Implemented Endpoints (snapshot)
|
|
|
|
**Public / user**
|
|
|
|
- `GET /api/health`
|
|
- `GET /api/genres`, `GET /api/genres/{slug}`
|
|
- `GET /api/novels/browse`, `GET /api/novels/{idOrSlug}`
|
|
- `GET /api/truyen` (query `slug`), `GET /api/truyen/{novel_id}/chapters`, `GET /api/truyen/{novel_id}/chapters/by-number/{n}`
|
|
- `GET /api/chapters/{chapter_id}`
|
|
- `GET /api/truyen/suggest`
|
|
- `GET/POST /api/truyen/{novel_id}/comments`, `POST /api/truyen/{novel_id}/rate`
|
|
|
|
**Auth**
|
|
|
|
- `POST /api/auth/mobile-login` (JWT cho mobile)
|
|
- `GET /api/auth/session` (session bridge cho web — xem handler trong `main.py`)
|
|
|
|
**User (login)**
|
|
|
|
- `GET /api/user/profile`
|
|
- `GET/POST /api/user/bookmarks`, `DELETE /api/user/bookmarks/{novel_id}`
|
|
- `POST /api/user/reading-progress`
|
|
- `GET/POST /api/user/settings`
|
|
- `GET/POST/DELETE /api/user/recommendations`
|
|
|
|
**MOD / ADMIN** — prefix `/api/mod/*` (thể loại, truyện, chương, overview, đề cử, upload bìa, EPUB…). Liệt kê đầy đủ trong `app/main.py`.
|
|
|
|
**Import (MOD — web)**
|
|
|
|
- `POST /api/import/uploads/preview` — upload EPUB multipart để lấy preview metadata/cover gợi ý.
|
|
- `POST /api/mod/epub`, `POST /api/mod/epub/ai-suggest` — luồng import EPUB chính.
|
|
- `GET/POST/PUT/DELETE /api/mod/the-loai` — quản lý thể loại trong wizard.
|
|
|
|
Luồng SourceAsset / `/api/import/assets/*` và job pipeline cũ đã **gỡ khỏi codebase** (không còn endpoint).
|
|
|
|
## NAS Migration Ops
|
|
|
|
### 1) Apply SQL migration manually
|
|
|
|
Run SQL in `migrations/2026_04_nas_content_storage.sql` against PostgreSQL.
|
|
|
|
### 2) Backfill existing chapter content to NAS + ChapterContentRef
|
|
|
|
Dry-run first:
|
|
|
|
```bash
|
|
python scripts/backfill_chapter_content_refs.py --limit 1000 --dry-run
|
|
```
|
|
|
|
Then execute:
|
|
|
|
```bash
|
|
python scripts/backfill_chapter_content_refs.py --limit 1000
|
|
```
|
|
|
|
You can run multiple batches by increasing/changing `--limit`.
|
|
|
|
Checkpoint/resume mode:
|
|
|
|
```bash
|
|
python scripts/backfill_chapter_content_refs.py --limit 1000 --state-file .backfill_state.json
|
|
```
|
|
|
|
## Chapter Read Cutover Flag
|
|
|
|
Set in `.env`:
|
|
|
|
```env
|
|
CHAPTER_CONTENT_MODE=nas_first
|
|
```
|
|
|
|
Values:
|
|
- `nas_first` (default): read NAS ref first.
|
|
|
|
## Notes
|
|
|
|
- Web session auth is supported via NextAuth session cookies (next-auth.session-token and secure variants).
|
|
- Mobile auth is supported via Bearer JWT from /api/auth/mobile-login.
|