# eBook Library (Go + templ) Minimalistic, no-JS eBook library web app for reading EPUB files on a Tolino e-reader browser. UI text/comments are in German. ## Build & run ```bash go mod tidy # install deps templ generate # regenerate views/*_templ.go from views/*.templ (required after editing .templ files) go build ./... # build check go run ./cmd/server # run dev server on :8080 ``` - `templ` CLI is required whenever `views/pages.templ` changes — generated Go code lives in `views/pages_templ.go` and is checked into the repo, so it must be regenerated and committed together with template edits. - No test suite exists yet. - Config via env vars: `ADDR` (default `:8080`), `BOOKS_DIR` (default `books`), `USERS_DB` (default `users.db`). ## Admin CLI ```bash go run ./cmd/admin user add [--role reader|uploader|admin] # Standard: reader go run ./cmd/admin user list go run ./cmd/admin user delete go run ./cmd/admin user set-role ``` ## Architecture - `cmd/server/main.go` — entrypoint; wires `library.Service`, `users.Store` and `web.Handler` together and starts the HTTP server. - `internal/library` — core domain logic, no HTTP dependency (unchanged). - `internal/users/role.go` — `Role` type (`reader`, `uploader`, `admin`), `roleLevel` map für Hierarchie, `AtLeast(min Role)`, `ParseRole(s)`. Neue Rollen: in `roleLevel` eintragen und ggf. Routen anpassen — kein Schema-Change nötig. - `internal/users/store.go` — SQLite-backed user store. Migrations via `_schema_version`-Tabelle (geordnete `migrations [][]string`); neue Migrationen am Ende anhängen. `role TEXT` statt `can_upload INTEGER`. - `internal/web/middleware.go` — `requireRole(minRole users.Role)` ist die zentrale Middleware; `requireAuth` ist ein Spezialfall davon (implizit `RoleReader`). - Auth: `/login` und `/static/` sind die einzigen öffentlichen Routes. Upload-Routes verwenden `requireRole(RoleUploader)`. **Rollen-Hierarchie:** ``` reader (0) < uploader (1) < admin (2) ``` - `cmd/admin/main.go` — CLI binary for user management; reads `USERS_DB` env var. - `internal/web/handlers.go` — HTTP handlers including `loginPage`, `loginSubmit`, `logout`, `uploadPage`, `uploadSubmit`. - `internal/web/middleware.go` — `requireAuth` and `requireUpload` middleware; session resolved from `"session"` cookie; stored in request context via `sessionKey`. - `views/pages.templ` — `LoginPage(errMsg)`, `UploadPage(errMsg)` added; `IndexPage` now takes `canUpload bool`; `Layout` includes logout button. ## Conventions - Path traversal guards matter: `downloadBook` validates the resolved book path stays within `BooksDir()` before serving; `cleanEPUBPath` rejects `../`-escaping hrefs inside EPUB zips; `uploadSubmit` validates the destination path stays within `BooksDir()`. Preserve these checks in any related changes. - Cover image reads are capped (`maxCoverBytes = 10 MiB`) to avoid decompression abuse — keep similar limits when reading zip entries. - Handlers return `404` for missing/empty `id` and `500` on internal errors; keep that pattern for new routes. - Auth: `/login` and `/static/` are the only public routes — everything else goes through `requireAuth`. Routes needing upload permission use `requireUpload` (which wraps `requireAuth`). - Session cookie: `HttpOnly`, `SameSite=Lax`, 30-day expiry; no `Secure` flag set (intended for LAN use without TLS). - `modernc.org/sqlite` is a pure-Go SQLite driver (no CGo). `MaxOpenConns(1)` is set because SQLite doesn't support concurrent writers. - `users.db` is gitignored; create it at runtime with the admin CLI.