Files
ebooks/.github/copilot-instructions.md
T
arnefandCopilot 817763887a refactor: replace can_upload boolean with role-based access control
Introduces a three-tier role hierarchy: reader < uploader < admin.

- internal/users/role.go: Role type, roleLevel map, AtLeast(min),
  ParseRole() — new roles added by inserting into roleLevel only
- internal/users/store.go: versioned migrations via _schema_version table;
  v2 migration adds 'role' column and migrates existing can_upload data;
  SetRole() replaces SetUpload(); Session carries Role instead of CanUpload
- internal/web/middleware.go: generic requireRole(minRole) middleware
  replaces the ad-hoc requireUpload
- internal/web/handlers.go: upload routes use requireRole(RoleUploader);
  listBooks derives canUpload from sess.Role.AtLeast(RoleUploader)
- cmd/admin/main.go: user add --role <reader|uploader|admin>,
  user set-role replaces user set-upload
- README, copilot-instructions updated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-11 19:43:39 +02:00

80 lines
3.7 KiB
Markdown

# eBook Library (Go + templ)
Minimalistic, no-JS eBook library web app for reading files (EPUB/PDF) 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 <name> [--role reader|uploader|admin] # Standard: reader
go run ./cmd/admin user list
go run ./cmd/admin user delete <name>
go run ./cmd/admin user set-role <name> <reader|uploader|admin>
```
## 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.