added desired module layout

and debugging information
This commit is contained in:
2026-04-12 08:06:49 +02:00
parent 7fadf82c4c
commit c7908663d6
+81 -37
View File
@@ -15,48 +15,65 @@ Bamort is a role-playing game character management system (MOAM replacement) wit
## Backend Architecture (`backend/`)
### Module Structure
Each domain module follows this pattern (e.g., `character/`, `pdfrender/`, `equipment/`):
```
module/
handlers.go # HTTP handlers (Gin controllers)
routes.go # Route registration: RegisterRoutes(r *gin.RouterGroup)
*_test.go # Tests with setupTestEnvironment(t)
```
Every backend module must contain exactly these files:
**Key conventions:**
- **Entry point**: `cmd/main.go` registers all modules via `RegisterRoutes(protected)`
- **Models**: `models/` contains GORM entities (e.g., `Char`, `SkFertigkeit`, `EqWaffe`)
- **Database**: Shared `database.DB` instance, use `models.MigrateStructure(db)` for migrations
- **Configuration**: `config.Cfg` loaded from env vars (see `config/config.go`)
- `TEMPLATES_DIR` for PDF templates (default: `./templates`)
- `ENVIRONMENT=test|development|production`
- `DATABASE_TYPE=mysql|sqlite`
| File | Required | Content |
|---|---|---|
| `handlers.go` | yes | All HTTP handler functions for the module (Gin controllers)|
| `routes.go` | yes | Route definitions; calls `RegisterRoutes` |
| `register.go` | yes | `init()` — registers routes, models, and migrations with the application |
| `model.go` | yes | GORM entity definitions owned by this module |
| `*_test.go` | yes | At least one test file; more files allowed by sub-domain |
| `database.go` | when needed | Module-specific DB query helpers |
Handler files may be split by sub-domain (e.g. `lerncost_handler.go`, `share_handlers.go`) when `handlers.go` grows large. The split files stay in the same package.
**Models**: each module defines its own GORM entities in `model.go`. The shared `models/` package contains only types that are referenced across multiple modules.
**Configuration**: `config.Cfg` loaded from env vars — `ENVIRONMENT`, `DATABASE_TYPE`, `TEMPLATES_DIR`, etc.
### Route Ownership Map
New endpoints must be added to the module that owns their prefix. the prefixes can be foundin the routes.go files.
Use `RequireAdmin()` or `RequireMaintainer()` middleware on sub-routes that need elevated roles — never check roles inside a handler.
### Module Dependencies
Modules should be self contained as far as possible. If a module needs to reference another module's data, it should import that module's package and use its exported functions — do not duplicate logic or data structures. If this means circular references, consider whether the shared logic should be moved to a common package (e.g. `utils/` or `models/`) of an exeption for duplicating logic or data structures must be made.
### Testing Requirements
- **NEVER** create test files with `main()` functions
- **ALWAYS** use `_test.go` suffix
- **ALWAYS** call `setupTestEnvironment(t)` at start of each test:
```go
func setupTestEnvironment(t *testing.T) {
original := os.Getenv("ENVIRONMENT")
os.Setenv("ENVIRONMENT", "test")
t.Cleanup(func() { /* restore */ })
}
```
- Use `testutils.SetupTestDB()` for database tests (creates SQLite test DB)
- Test character ID 18 (Fanjo Vetrani) exists in test database
- **NEVER** create test files with `main()` — always use `_test.go`
- **ALWAYS** call `setupTestEnvironment(t)` at the top of every test function
- Use `testutils.SetupTestDB()` for any test that touches the database (creates an isolated SQLite DB)
- Test character ID 18 (Fanjo Vetrani) is pre-seeded in the test DB
**Every handler must have tests covering:**
1. Happy path — expected 2xx response with correct body
2. Error / not-found path — expected 4xx response
3. Unauthorized access — expected 401 (no token) or 403 (wrong role) for protected routes
Use `t.Run("scenario", ...)` subtests when a handler has multiple input variants. Group related scenarios in one table-driven test rather than writing separate top-level test functions.
### Role Checking
Roles in ascending order: `standard` < `maintainer` < `admin`.
- `user.RequireAdmin()` — allows only `admin`; returns 403 for all other roles
- `user.RequireMaintainer()` — allows `maintainer` and `admin`; returns 403 otherwise
- **Apply these as Gin middleware in `routes.go` on a sub-group — never read the role inside a handler**
```go
func RegisterRoutes(r *gin.RouterGroup) {
group := r.Group("/maintenance")
group.GET("/skills", listSkills) // all authenticated users
protected := group.Group("")
protected.Use(user.RequireMaintainer())
protected.POST("/skills", createSkill) // maintainer + admin only
}
```
### API Patterns
- Protected routes under `/api` prefix require JWT authentication
- Use `respondWithError(c, status, message)` for error responses
- Route registration example:
```go
func RegisterRoutes(r *gin.RouterGroup) {
group := r.Group("/module")
group.GET("/list", ListHandler)
group.POST("/create", CreateHandler)
}
```
- Protected routes under `/api` prefix require JWT `user.AuthMiddleware()` is applied globally
- Use `respondWithError(c, status, message)` for all error responses
## Docker Development Workflow
@@ -97,6 +114,33 @@ go test -v ./character/
- HMR auto-reloads on file save
- Check browser console and `docker logs bamort-frontend-dev`
## Debugging & Bugfixing
Both Docker containers are always running. Use them directly — no restart needed.
**Read live logs (Air/Vite output, compile errors, runtime panics):**
```bash
docker logs bamort-backend-dev -f --tail=50
docker logs bamort-frontend-dev -f --tail=20
```
**Test API endpoints directly:**
```bash
# Public — no token needed
curl -s http://localhost:8180/api/public/version
# Authenticated — copy token from browser DevTools → Application → localStorage → 'token'
curl -s -H "Authorization: Bearer <token>" http://localhost:8180/api/characters
```
**Inspect database:** phpMyAdmin at http://localhost:8082
**Backend test failures** run against an isolated SQLite DB (`testutils.SetupTestDB()`) — they are independent of the running MariaDB container.
**Air** recompiles the backend automatically on file save — compile errors appear immediately in `docker logs bamort-backend-dev`.
**Vite HMR** reloads the frontend on file save — build errors appear in `docker logs bamort-frontend-dev` and the browser console.
## PDF Rendering Module (`pdfrender/`)
- Uses `chromedp` for HTML→PDF (requires Chromium in Docker)