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

3.7 KiB

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

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

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.goRole 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.gorequireRole(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.gorequireAuth and requireUpload middleware; session resolved from "session" cookie; stored in request context via sessionKey.
  • views/pages.templLoginPage(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.