154 lines
5.2 KiB
Markdown
Executable File
154 lines
5.2 KiB
Markdown
Executable File
# Phase 2 — Database Layer
|
|
|
|
Persistent storage using PostgreSQL via pgx. Migrations run on startup. sqlc generates type-safe query code.
|
|
|
|
Reference schema modeled after Suwayomi-Server:
|
|
- `/Users/achmad/Documents/Belajar/Web/Suwayomi-Server/server/src/main/kotlin/suwayomi/tachidesk/manga/model/table/`
|
|
- Migrations: `/Users/achmad/Documents/Belajar/Web/Suwayomi-Server/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/`
|
|
|
|
---
|
|
|
|
## 2.1 Schema Migration — `internal/db/migrations/000001_init.up.sql`
|
|
|
|
- [x] `sources` table
|
|
- [x] `id BIGINT PRIMARY KEY`
|
|
- [x] `name VARCHAR(128) NOT NULL`
|
|
- [x] `lang VARCHAR(32) NOT NULL`
|
|
- [x] `is_nsfw BOOLEAN NOT NULL DEFAULT FALSE`
|
|
- [x] `manga` table
|
|
- [x] `id SERIAL PRIMARY KEY`
|
|
- [x] `source_id BIGINT NOT NULL REFERENCES sources(id)`
|
|
- [x] `url VARCHAR(2048) NOT NULL`
|
|
- [x] `title VARCHAR(512) NOT NULL`
|
|
- [x] `initialized BOOLEAN NOT NULL DEFAULT FALSE`
|
|
- [x] `artist TEXT`, `author TEXT`, `description TEXT`, `genre TEXT`
|
|
- [x] `status INTEGER NOT NULL DEFAULT 0`
|
|
- [x] `thumbnail_url VARCHAR(2048)`
|
|
- [x] `thumbnail_last_fetched BIGINT NOT NULL DEFAULT 0`
|
|
- [x] `in_library BOOLEAN NOT NULL DEFAULT FALSE`
|
|
- [x] `in_library_at BIGINT NOT NULL DEFAULT 0`
|
|
- [x] `real_url VARCHAR(2048)`
|
|
- [x] `last_fetched_at BIGINT NOT NULL DEFAULT 0`
|
|
- [x] `chapters_last_fetched_at BIGINT NOT NULL DEFAULT 0`
|
|
- [x] `update_strategy VARCHAR(64) NOT NULL DEFAULT 'ALWAYS_UPDATE'`
|
|
- [x] `UNIQUE (source_id, url)`
|
|
- [x] `chapters` table
|
|
- [x] `id SERIAL PRIMARY KEY`
|
|
- [x] `manga_id INTEGER NOT NULL REFERENCES manga(id) ON DELETE CASCADE`
|
|
- [x] `url VARCHAR(2048) NOT NULL`
|
|
- [x] `name VARCHAR(512) NOT NULL`
|
|
- [x] `date_upload BIGINT NOT NULL DEFAULT 0`
|
|
- [x] `chapter_number REAL NOT NULL DEFAULT -1`
|
|
- [x] `scanlator VARCHAR(256)`
|
|
- [x] `source_order INTEGER NOT NULL`
|
|
- [x] `is_read BOOLEAN NOT NULL DEFAULT FALSE`
|
|
- [x] `is_bookmarked BOOLEAN NOT NULL DEFAULT FALSE`
|
|
- [x] `last_page_read INTEGER NOT NULL DEFAULT 0`
|
|
- [x] `last_read_at BIGINT NOT NULL DEFAULT 0`
|
|
- [x] `fetched_at BIGINT NOT NULL DEFAULT 0`
|
|
- [x] `real_url VARCHAR(2048)`
|
|
- [x] `is_downloaded BOOLEAN NOT NULL DEFAULT FALSE`
|
|
- [x] `page_count INTEGER NOT NULL DEFAULT -1`
|
|
- [x] `UNIQUE (manga_id, url)`
|
|
- [x] `pages` table
|
|
- [x] `id SERIAL PRIMARY KEY`
|
|
- [x] `chapter_id INTEGER NOT NULL REFERENCES chapters(id) ON DELETE CASCADE`
|
|
- [x] `"index" INTEGER NOT NULL`
|
|
- [x] `url VARCHAR(2048) NOT NULL`
|
|
- [x] `image_url TEXT`
|
|
- [x] `UNIQUE (chapter_id, "index")` — added for upsert support
|
|
- [x] `source_meta` table
|
|
- [x] `source_id BIGINT NOT NULL REFERENCES sources(id)`
|
|
- [x] `key VARCHAR(256) NOT NULL`
|
|
- [x] `value TEXT NOT NULL`
|
|
- [x] `PRIMARY KEY (source_id, key)`
|
|
- [x] Indexes
|
|
- [x] `CREATE INDEX ON manga (source_id)`
|
|
- [x] `CREATE INDEX ON manga (last_fetched_at)`
|
|
- [x] `CREATE INDEX ON chapters (manga_id)`
|
|
- [x] `CREATE INDEX ON pages (chapter_id)`
|
|
|
|
---
|
|
|
|
## 2.2 DB Initialization — `internal/db/db.go`
|
|
|
|
- [x] `pgxpool.Pool` init from `DATABASE_URL` env var
|
|
- [x] Configurable `MaxConns` via `DB_MAX_CONNS` (default 10)
|
|
- [x] Configurable `MinConns` via `DB_MIN_CONNS` (default 2)
|
|
- [x] Connection health check on startup (`pool.Ping`)
|
|
- [x] Migration runner using `golang-migrate/migrate`
|
|
- [x] Source: `iofs` (embed migration SQL files with `//go:embed`)
|
|
- [x] Driver: `pgx5` (DSN scheme rewritten from `postgres://` → `pgx5://`)
|
|
- [x] Run `migrate.Up()` on startup; log version before/after
|
|
- [x] Non-fatal on "no change" (`migrate.ErrNoChange`)
|
|
- [x] `Queries` struct wrapping sqlc-generated query clients
|
|
- [x] `Close()` method to drain pool on shutdown
|
|
|
|
---
|
|
|
|
## 2.3 SQL Queries — `internal/db/queries/`
|
|
|
|
Generated by `sqlc generate` (config: `sqlc.yaml`, `sql_package: pgx/v5`).
|
|
|
|
### `manga.sql`
|
|
|
|
- [x] `UpsertManga`
|
|
- [x] `GetMangaBySourceURL`
|
|
- [x] `GetMangaByID`
|
|
- [x] `ListMangaBySource`
|
|
- [x] `UpdateMangaDetails`
|
|
- [x] `UpdateMangaFetchedAt`
|
|
- [x] `UpdateChaptersFetchedAt`
|
|
|
|
### `chapter.sql`
|
|
|
|
- [x] `UpsertChapter`
|
|
- [x] `GetChapterByID`
|
|
- [x] `ListChaptersByManga`
|
|
- [x] `UpdateChapterFetchedAt`
|
|
|
|
### `page.sql`
|
|
|
|
- [x] `UpsertPage`
|
|
- [x] `ListPagesByChapter`
|
|
- [x] `UpdatePageImageURL`
|
|
|
|
### `source.sql`
|
|
|
|
- [x] `UpsertSource`
|
|
- [x] `ListSources`
|
|
- [x] `GetSourceByID`
|
|
- [x] `GetSourceMeta`
|
|
- [x] `SetSourceMeta`
|
|
|
|
---
|
|
|
|
## 2.4 sqlc Configuration — `sqlc.yaml`
|
|
|
|
- [x] `version: "2"`
|
|
- [x] engine: `postgresql`
|
|
- [x] schema path: `internal/db/migrations/`
|
|
- [x] queries path: `internal/db/queries/*.sql`
|
|
- [x] output package: `internal/db/queries`
|
|
- [x] `sql_package: "pgx/v5"` — required for pgxpool compatibility
|
|
- [x] `emit_json_tags: true`
|
|
- [x] `emit_db_tags: true`
|
|
- [x] `emit_pointers_for_null_types: true`
|
|
|
|
---
|
|
|
|
## 2.5 Data Flow & Cache Logic
|
|
|
|
TTL env vars implemented in `internal/config/config.go`. Cache check logic and `?refresh=true` bypass live in the Phase 5 API handler using these values.
|
|
|
|
- [x] `MANGA_LIST_TTL_SECONDS` env var (default 600)
|
|
- [x] `MANGA_DETAIL_TTL_SECONDS` env var (default 3600)
|
|
- [x] `CHAPTER_LIST_TTL_SECONDS` env var (default 600)
|
|
|
|
---
|
|
|
|
## Checklist: Phase 2 Done When
|
|
|
|
- [x] `sqlc generate` completes without errors; generated files compile
|
|
- [x] `go build ./...` succeeds
|