60 KiB
BaMoRT Backend — Comprehensive Architecture & Code Findings
Generated: 2026-03-13
Source:/home/de31a2/bamort/backend
Module:bamort(Go 1.24, toolchain 1.24.4)
Table of Contents
- Module Structure Overview
- Entry Point — cmd/main.go
- Router & Middleware Setup
- Authentication & Authorization
- Configuration
- Database Layer
- Models (GORM Entities)
- Module-by-Module Analysis
- Complete API Route Listing
- Testing Approach
- Key Dependencies
- Security Observations
- Cross-Module Relationships Diagram
1. Module Structure Overview
backend/
├── cmd/ # Entry point (main.go)
├── config/ # App configuration loading from env/.env
├── database/ # DB connection, migrations, test helpers
├── models/ # All GORM entities and migration orchestration
├── router/ # Gin engine setup, CORS, base route group + JWT guard
├── user/ # User accounts, auth, roles, password reset
├── character/ # Core character CRUD, skills, spells, learning system
├── equipment/ # Character equipment (weapons, containers, gear)
├── gsmaster/ # Game-system master data (skills, spells, weapons, learning costs)
├── gamesystem/ # Game system record management and initial seed data
├── maintenance/ # Admin/maintainer ops – DB checks, migrations, scratchpad endpoints
├── pdfrender/ # HTML→PDF export using chromedp + pdfcpu merge
├── importer/ # VTT-JSON / CSV import and export of characters/spells
├── transfer/ # JSON-based character and full-database import/export
├── appsystem/ # Version and system-info endpoints
├── logger/ # Custom leveled logger (DEBUG/INFO/WARN/ERROR)
├── mail/ # SMTP email client (password reset emails)
├── api/ # Integration tests spanning multiple modules
├── testutils/ # Shared test environment setup helpers
├── testdata/ # Prepared SQLite snapshot used by all tests
├── templates/ # PDF HTML templates (Default_A4_Quer/)
└── scripts/ # Shell scripts (e.g., SQLite→MariaDB transfer)
Each domain module follows the pattern:
module/
handlers.go HTTP handler functions (Gin controllers)
routes.go RegisterRoutes(r *gin.RouterGroup)
*_test.go Tests using setupTestEnvironment(t)
2. Entry Point — cmd/main.go
File: cmd/main.go
Startup Sequence
func main() {
cfg := config.Cfg // 1. Load config (auto-loaded in init())
logger.SetDebugMode(cfg.DebugMode) // 2. Configure logger
logger.SetMinLogLevel(...)
if cfg.IsProduction() {
gin.SetMode(gin.ReleaseMode) // 3. Set Gin mode
}
database.ConnectDatabase() // 4. Connect to database
pdfrender.InitializeTemplates(...) // 5. Sync PDF templates
r := gin.Default()
router.SetupGin(r) // 6. CORS middleware
protected := router.BaseRouterGrp(r) // 7. Public auth routes + JWT-protected group
// 8. Register module routes
user.RegisterRoutes(protected)
gsmaster.RegisterRoutes(protected)
character.RegisterRoutes(protected)
equipment.RegisterRoutes(protected)
maintenance.RegisterRoutes(protected)
importer.RegisterRoutes(protected)
pdfrender.RegisterRoutes(protected)
transfer.RegisterRoutes(protected)
appsystem.RegisterRoutes(protected)
// 9. Public routes (no auth)
pdfrender.RegisterPublicRoutes(r)
appsystem.RegisterPublicRoutes(r)
r.Run(cfg.GetServerAddress()) // 10. Start HTTP server
}
Swagger Annotations
The file contains @title BaMoRT API, @version 1, @host localhost:8180 – indicates Swagger/OpenAPI doc generation is intended.
3. Router & Middleware Setup
CORS (router/setup.go)
func SetupGin(r *gin.Engine) {
allowedOrigins := []string{
config.Cfg.FrontendURL,
"http://localhost:5173",
"http://192.168.0.48:5173",
"http://192.168.0.36:5173",
"https://bamort.trokan.de",
}
r.Use(cors.New(cors.Config{
AllowOrigins: allowedOrigins,
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * 3600,
}))
}
Route Groups (router/routes.go)
Public (unauthenticated) routes:
| Method | Path | Handler |
|---|---|---|
| POST | /register |
user.RegisterUser |
| POST | /login |
user.LoginUser |
| POST | /password-reset/request |
user.RequestPasswordReset |
| GET | /password-reset/validate/:token |
user.ValidateResetToken |
| POST | /password-reset/reset |
user.ResetPassword |
Protected group: /api — guarded by user.AuthMiddleware().
4. Authentication & Authorization
Token Scheme
The project uses a custom MD5-based token, not JWT.
Token Generation (user/handlers.go):
func GenerateToken(u *User) string {
tx := md5.Sum([]byte(u.Username + u.CreatedAt.String()))
hashString := hex.EncodeToString(tx[:])
// Insert ".{userID}:" at position 7
pos := 7
idm := "." + fmt.Sprintf("%d", u.UserID) + ":"
token := hashString[:pos] + idm + hashString[pos:]
return token
}
The token embeds the user ID in a predictable location. Clients send it as the Authorization header value (prefixed Bearer ).
Token Validation (CheckToken):
- Extracts user ID from position
7 + len("Bearer ")in theAuthorizationheader. - Loads the user from the database by that ID.
- Returns the user or
nil.
No cryptographic signature is verified. Any token with a valid user ID at the expected position is accepted.
AuthMiddleware() (user/handlers.go)
Sets userID, username, and user in the Gin context for downstream handlers.
Password Hashing
Passwords are hashed with MD5 (not bcrypt):
hashedPassword := md5.Sum([]byte(user.PasswordHash))
user.PasswordHash = hex.EncodeToString(hashedPassword[:])
The bcrypt implementation is commented out.
Roles (user/model.go)
| Constant | Value | Description |
|---|---|---|
RoleStandardUser |
"standard" |
Default for new registrations |
RoleMaintainer |
"maintainer" |
Can manage master data |
RoleAdmin |
"admin" |
Can manage users |
Role-checking middleware:
RequireAdmin()→ used on/api/users/*admin endpointsRequireMaintainer()→ used on maintenance/gsmaster write endpoints
5. Configuration
File: config/config.go
Config Struct Fields
| Field | Env Var(s) | Default | Description |
|---|---|---|---|
ServerPort |
API_PORT, SERVER_PORT |
"8180" |
HTTP listen port |
DatabaseURL |
DATABASE_URL |
"" |
DSN string for DB driver |
DatabaseType |
DATABASE_TYPE |
"mysql" |
mysql or sqlite |
DebugMode |
DEBUG |
false |
Enables verbose logging |
LogLevel |
LOG_LEVEL |
"INFO" |
DEBUG, INFO, WARN, ERROR |
Environment |
ENVIRONMENT, GO_ENV |
"production" |
production, development, test |
DevTesting |
DEVTESTING |
"no" |
"yes" redirects to SQLite test DB |
FrontendURL |
BASE_URL |
"http://localhost:5173" |
CORS allowed origin |
TemplatesDir |
TEMPLATES_DIR |
"./templates" |
PDF template directory |
ExportTempDir |
EXPORT_TEMP_DIR |
"./xporttemp" |
Temporary PDF output directory |
MailHost |
MAIL_HOST |
"" |
SMTP server hostname |
MailPort |
MAIL_PORT |
465 |
SMTP port (465=TLS, 587=STARTTLS) |
MailUsername |
MAIL_USERNAME |
"" |
SMTP credentials |
MailPassword |
MAIL_PASSWORD |
"" |
SMTP credentials |
MailFrom |
MAIL_FROM |
(falls back to username) | Sender email address |
Loading Logic
- Reads
.envand.env.localfiles from the working directory. - Overrides with env vars.
ENVIRONMENT=developmentautomatically enablesDebugMode=trueandLogLevel=DEBUG.ENVIRONMENT=testorDEVTESTING=yescausesConnectDatabase()to use the SQLite test snapshot.
6. Database Layer
Package: database/
Connection (database/config.go)
func ConnectDatabase() *gorm.DB {
if envIsTest || devTestingIsYes {
SetupTestDB() // Use prepared SQLite snapshot
} else {
ConnectDatabaseOrig() // MySQL or SQLite via DATABASE_URL
}
return DB
}
Supported drivers: mysql (default), sqlite.
Global Variable
database.DB *gorm.DB — all packages import this directly.
Test Database
- Source:
testdata/prepared_test_data.db(SQLite snapshot with real test data including character ID 18 "Fanjo Vetrani") - Mechanism:
SetupTestDB()copies the snapshot to aos.MkdirTempdirectory, opens it, and assigns it todatabase.DB. - Each test run gets a fresh isolated copy.
Migration Tables (database/model.go)
| Table | Purpose |
|---|---|
schema_version |
Current schema version tracking |
migration_history |
Log of applied migrations |
Migration Orchestration (models/database.go)
models.MigrateStructure(db) calls in order:
gameSystemMigrateStructure—game_systemsgsMasterMigrateStructure— skills, spells, equipment, weapons, etc.characterMigrateStructure— characters, skills, spells, equipmentequipmentMigrateStructureskillsMigrateStructure— learning cost tablesimporterMigrateStructure— (currently no-op)learningMigrateStructure— all learning/cost relation tables
StringArray Custom Type
type StringArray []string
// Stored as JSON in TEXT column (Value+Scan implementations)
Used for Char.Spezialisierung (character specializations).
7. Models (GORM Entities)
Base Types
type BamortBase struct {
ID uint `gorm:"primaryKey"`
Name string
}
type BamortCharTrait struct {
BamortBase
CharacterID uint `gorm:"index"`
UserID uint `gorm:"index"`
}
type Magisch struct {
IstMagisch bool
Abw int
Ausgebrannt bool
}
User Model (user/model.go → table: users)
| Field | Type | Notes |
|---|---|---|
UserID |
uint PK | |
Username |
string unique | |
DisplayName |
string | Falls back to Username if empty |
PasswordHash |
string | MD5 hex string |
Email |
string unique | |
Role |
string | standard, maintainer, admin |
PreferredLanguage |
string | default de |
ResetPwHash |
*string | Hidden from JSON serialization |
ResetPwHashExpires |
*time.Time | 14 days validity |
CreatedAt, UpdatedAt |
time.Time |
Character Model (models/model_character.go → table: char_chars)
Main struct Char:
| Field | Type | Notes |
|---|---|---|
ID |
uint PK | (via BamortBase) |
Name |
string | Character name |
GameSystem |
string | e.g. "midgard" |
GameSystemId |
uint | FK to game_systems |
UserID |
uint index | FK to users |
User |
User | FK UserID→users.user_id CASCADE |
Rasse |
string | Race (e.g. "Mensch", "Zwerg") |
Typ |
string | Class code (e.g. "Kr", "Ma") |
Alter |
int | Age |
Anrede |
string | Title/salutation |
Grad |
int | Character grade/level |
Gender |
string | |
SocialClass |
string | |
Groesse |
int | Height (cm) |
Gewicht |
int | Weight (kg) |
Herkunft |
string | Origin/homeland |
Glaube |
string | Faith |
Hand |
string | Dominant hand |
Public |
bool | Publicly visible |
ResistenzKoerper |
int | Body resistance (derived) |
ResistenzGeist |
int | Mental resistance (derived) |
Abwehr |
int | Defense value (derived) |
Zaubern |
int | Spell casting value (derived) |
Raufen |
int | Brawling value (derived) |
Lp |
Lp | Life points (has-one, CASCADE) |
Ap |
Ap | Action points (has-one) |
B |
B | Load/burden (has-one) |
Merkmale |
Merkmale | Physical traits (has-one) |
Eigenschaften |
[]Eigenschaft | Attributes Au/Gs/Gw/In/Ko/St/Wk/Zt/PA |
Fertigkeiten |
[]SkFertigkeit | Skills (has-many) |
Waffenfertigkeiten |
[]SkWaffenfertigkeit | Weapon skills |
Zauber |
[]SkZauber | Spells |
Spezialisierung |
StringArray | TEXT/JSON stored |
Bennies |
Bennies | Gg/Gp/Sg |
Vermoegen |
Vermoegen | GS/SS/KS wealth |
Erfahrungsschatz |
Erfahrungsschatz | EP/ES experience |
Waffen |
[]EqWaffe | Weapons (has-many) |
Behaeltnisse |
[]EqContainer | Containers |
Transportmittel |
[]EqContainer | Transportation |
Ausruestung |
[]EqAusruestung | General equipment |
Image |
string | Base64 or URL |
Helper types:
| Type | Table | Fields |
|---|---|---|
Eigenschaft |
(embedded in preload) | ID, CharacterID, UserID, Name, Value |
Lp |
— | ID, CharacterID, Max, Value |
Ap |
— | ID, CharacterID, Max, Value |
B |
— | ID, CharacterID, Max, Value |
Merkmale |
— | BamortCharTrait + Augenfarbe/Haarfarbe/Sonstige/Breite/Groesse |
Erfahrungsschatz |
— | BamortCharTrait + ES, EP |
Bennies |
— | BamortCharTrait + Gg, Gp, Sg |
Vermoegen |
— | BamortCharTrait + Goldstuecke, Silberstuecke, Kupferstuecke |
FeChar (frontend-facing):
Embeds Char + Git (gift tolerance = 30+Ko/2) + CategorizedSkills map + InnateSkills slice.
Skill Models (models/model_char_skills.go)
| Type | Table | Key Fields |
|---|---|---|
SkFertigkeit |
char_skills |
BamortCharTrait + Fertigkeitswert, BasisWert, Bonus, Pp, Category, Improvable, LearningCost |
SkWaffenfertigkeit |
char_weaponskills |
Embeds SkFertigkeit |
SkAngeboreneFertigkeit |
(no own table) | Embeds SkFertigkeit |
SkZauber |
char_spells |
BamortCharTrait + Beschreibung, Bonus, Quelle |
Equipment Models (models/model_char_equipment.go)
| Type | Table | Key Fields |
|---|---|---|
EqAusruestung |
equi_equipments |
BamortCharTrait + Magisch + Anzahl, ContainedIn, Bonus, Gewicht, Wert |
EqWaffe |
equi_weapons |
BamortCharTrait + Magisch + Abwb, Anb, Anzahl, Schb, NameFuerSpezialisierung |
EqContainer |
equi_containers |
BamortCharTrait + Magisch + IsTransportation, Tragkraft, Volumen, ExtID |
GSMaster Models (models/model_gsmaster.go)
| Type | Table | Key Fields |
|---|---|---|
Skill |
gsm_skills |
ID, GameSystem/Id, Name, Initialwert, BasisWert, Bonuseigenschaft, Improvable, InnateSkill, Category, Difficulty |
WeaponSkill |
(inherits Skill) | Embeds Skill |
Spell |
(via gsmaster) | ID, Name, Stufe, AP, Art, Zauberdauer, Reichweite, Ursprung, Category, LearningCategory |
Equipment |
(via gsmaster) | ID, Name, Gewicht, Wert, PersonalItem |
Weapon |
(extends Equipment) | SkillRequired, Damage, RangeNear/Middle/Far |
Container |
(extends Equipment) | Tragkraft, Volumen |
Transportation |
(extends Container) | — |
Believe |
(via gsmaster) | ID, GameSystem/Id, Name, Beschreibung, SourceID |
MiscLookup |
gsm_misc_lookups |
ID, GameSystem/Id, Key, Value |
LookupList |
gsm_lookup_lists |
Generic lookup |
LearnCost |
(computed, no own table) | GameSystem, Stufe, LE, TE, Ep, Money, PP |
Game System (models/model_game_system.go → table: game_systems)
| Field | Notes |
|---|---|
Code |
Unique, e.g. "M5" |
Name |
e.g. "M-System" |
IsActive |
bool |
Default game system: Code=M5, Name=M-System.
Learning-Cost Models (models/model_learning_costs.go)
| Type | Table | Purpose |
|---|---|---|
Source |
gsm_lit_sources |
Rulebook/sourcebook references |
CharacterClass |
gsm_character_classes |
Class codes (Kr, Ma, Hx …) + source link |
SkillCategory |
learning_skill_categories |
Alltag, Freiland, Halbwelt, Kampf, Körper, Sozial, Unterwelt, Waffen, Wissen |
SkillDifficulty |
learning_skill_difficulties |
leicht, normal, schwer, sehr schwer |
SpellSchool |
learning_spell_schools |
Beherrschen, Bewegen, Erkennen, … |
ClassCategoryEPCost |
learning_class_category_ep_costs |
EP per TE by class+category |
ClassSpellSchoolEPCost |
learning_class_spell_school_ep_costs |
EP per LE by class+school |
SpellLevelLECost |
learning_spell_level_le_costs |
LE required by spell level |
SkillCategoryDifficulty |
learning_skill_category_difficulties |
LE cost for skill in category+difficulty |
WeaponSkillCategoryDifficulty |
(separate table) | LE cost for weapon skills |
SkillImprovementCost |
learning_skill_improvement_costs |
TE to improve by current level |
ClassCategoryLearningPoints |
— | Starting LP per class+category for creation |
AuditLogEntry |
audit_log_entries(?) |
EP/gold change audit trail |
Character Creation Session (models/model_character_creation.go)
| Type | Table | Purpose |
|---|---|---|
CharacterCreationSession |
char_creation_sessions |
Multi-step wizard persistence |
Fields: UserID, Name, Rasse, Typ, Herkunft, Stand, Glaube, Attributes (JSON), DerivedValues (JSON), Skills (JSON), Spells (JSON), SkillPoints (JSON), CurrentStep, ExpiresAt.
Character Share (models/model_character_share.go → table: char_shares)
| Field | Notes |
|---|---|
CharacterID |
FK to char_chars |
UserID |
FK to users (recipient) |
Permission |
"read" or "write" |
Database Schema/Migration Models (database/model.go)
| Type | Table |
|---|---|
SchemaVersion |
schema_version |
MigrationHistory |
migration_history |
8. Module-by-Module Analysis
8.1 user/
Purpose: User account management, authentication, authorization, password reset.
Key Files:
model.go— User struct, CRUD methods, reset-hash helpershandlers.go— RegisterUser, LoginUser, CheckToken, AuthMiddleware, password resetadmin_handlers.go— ListUsers, GetUser, UpdateUserRole, ChangeUserPassword, DeleteUsermiddleware.go— RequireRole, RequireAdmin, RequireMaintainerdatabase.go—MigrateStructureforuserstableroutes.go— Route registration
Business Logic:
- Registration creates users with MD5-hashed passwords and a default
standardrole. - Login returns a custom MD5 token containing the encoded user ID.
- Password reset uses a 32-byte random hex hash (secure) with a 14-day expiry, sent via SMTP.
AuthMiddlewarevalidates the token structure, loads the user from DB, injectsuserID/username/userinto context.
HTTP Endpoints (protected unless noted):
| Method | Path | Handler | Auth level |
|---|---|---|---|
| POST | /register |
RegisterUser |
Public |
| POST | /login |
LoginUser |
Public |
| POST | /password-reset/request |
RequestPasswordReset |
Public |
| GET | /password-reset/validate/:token |
ValidateResetToken |
Public |
| POST | /password-reset/reset |
ResetPassword |
Public |
| GET | /api/user/profile |
GetUserProfile |
Auth |
| PUT | /api/user/display-name |
UpdateDisplayName |
Auth |
| PUT | /api/user/email |
UpdateEmail |
Auth |
| PUT | /api/user/password |
UpdatePassword |
Auth |
| PUT | /api/user/language |
UpdateLanguage |
Auth |
| GET | /api/users |
ListUsers |
Admin |
| GET | /api/users/:id |
GetUser |
Admin (own profile also allowed) |
| PUT | /api/users/:id/role |
UpdateUserRole |
Admin |
| PUT | /api/users/:id/password |
ChangeUserPassword |
Admin |
| DELETE | /api/users/:id |
DeleteUser |
Admin |
8.2 character/
Purpose: Core character management — CRUD, skill/spell learning system, character creation wizard, audit logging, sharing, practice points, PDF-relevant data prep.
Key Files:
handlers.go— ListCharacters, CreateCharacter, GetCharacter, UpdateCharacter, DeleteCharacter, ToFeChar, UpdateCharacterImage, GetDatasheetOptionslerncost_handler.go—GetLernCostNewSystem,ImproveSkill,LearnSkill,LearnSpell— full learning economyderived_values_calculator.go—CalculateStaticFields,CalculateRolledField— character creation mathcreation_rules.go—GetSpecialAbilityByRoll— special ability tableaudit_log.go—CreateAuditLogEntry,GetAuditLogForCharacter/Fieldshare_handlers.go—GetCharacterShares,UpdateCharacterShares,GetAvailableUsersForSharingpractice_points_handler.go—GetPracticePoints,UpdatePracticePoints,AddPracticePoint,UsePracticePointskill_type_helper.go— skill categorization helperssystem_information_handlers.go—GetSkillCategoriesHandlerStaticspell_utils.go— spell availability and creation utilitiesdatabase.go—SaveCharacterToDBimage_handler.go— character image upload/handling
Business Logic:
ListCharacters: Returns{ self_owned: [], others: [] }whereothers= public + shared.GetCharacter: ReturnsFeChar(augmented view with categorized skills).toFeChar: Adds bonus field from attributes, splits skills by category.checkCharacterOwnership: Guards update/delete/share withcharacter.UserID == c.GetUint("userID").- Learning System (
GetLernCostNewSystem): Calculates LE/TE/EP/Money cost for learning or improving a skill/spell/weapon. Considers: character class, skill category, difficulty, current level, practice points (PP) reductions, gold-for-EP conversion, reward types (free learning, half EP). - Derived Values:
CalculateStaticFieldsLogicimplements Midgard 5th edition formulae for Ausdauer-, Schadens-, Angriffs-, Abwehr-, Zauber-Bonus, Resistenz, Abwehr, Zaubern, Raufen.
HTTP Endpoints (/api/characters/...):
| Method | Path | Handler |
|---|---|---|
| GET | /api/characters |
ListCharacters |
| POST | /api/characters |
CreateCharacter |
| GET | /api/characters/:id |
GetCharacter |
| PUT/PATCH | /api/characters/:id |
UpdateCharacter |
| DELETE | /api/characters/:id |
DeleteCharacter |
| PUT | /api/characters/:id/image |
UpdateCharacterImage |
| GET | /api/characters/:id/datasheet-options |
GetDatasheetOptions |
| GET | /api/characters/:id/shares |
GetCharacterShares |
| PUT | /api/characters/:id/shares |
UpdateCharacterShares |
| GET | /api/characters/:id/available-users |
GetAvailableUsersForSharing |
| GET | /api/characters/:id/experience-wealth |
GetCharacterExperienceAndWealth |
| PUT | /api/characters/:id/experience |
UpdateCharacterExperience |
| PUT | /api/characters/:id/wealth |
UpdateCharacterWealth |
| GET | /api/characters/:id/audit-log |
GetCharacterAuditLog |
| GET | /api/characters/:id/audit-log/stats |
GetAuditLogStats |
| POST | /api/characters/lerncost-new |
GetLernCostNewSystem |
| POST | /api/characters/lerncost |
GetLernCostNewSystem |
| POST | /api/characters/improve-skill-new |
ImproveSkill |
| POST | /api/characters/improve-skill |
ImproveSkill |
| POST | /api/characters/:id/learn-skill-new |
LearnSkill |
| POST | /api/characters/:id/learn-skill |
LearnSkill |
| POST | /api/characters/:id/learn-spell-new |
LearnSpell |
| POST | /api/characters/:id/learn-spell |
LearnSpell |
| POST | /api/characters/available-skills-new |
GetAvailableSkillsNewSystem |
| POST | /api/characters/available-skills |
GetAvailableSkillsNewSystem |
| POST | /api/characters/available-skills-creation |
GetAvailableSkillsForCreation |
| POST | /api/characters/available-spells-creation |
GetAvailableSpellsForCreation |
| POST | /api/characters/available-spells-new |
GetAvailableSpellsNewSystem |
| POST | /api/characters/available-spells |
GetAvailableSpellsNewSystem |
| GET | /api/characters/spell-details |
GetSpellDetails |
| GET | /api/characters/:id/reward-types |
GetRewardTypesStatic |
| GET | /api/characters/:id/practice-points |
GetPracticePoints |
| PUT | /api/characters/:id/practice-points |
UpdatePracticePoints |
| POST | /api/characters/:id/practice-points/add |
AddPracticePoint |
| POST | /api/characters/:id/practice-points/use |
UsePracticePoint |
| GET | /api/characters/skill-categories |
GetSkillCategoriesHandlerStatic |
| GET | /api/characters/create-sessions |
ListCharacterSessions |
| POST | /api/characters/create-session |
CreateCharacterSession |
| GET | /api/characters/create-session/:sessionId |
GetCharacterSession |
| PUT | /api/characters/create-session/:sessionId/basic |
UpdateCharacterBasicInfo |
| PUT | /api/characters/create-session/:sessionId/attributes |
UpdateCharacterAttributes |
| PUT | /api/characters/create-session/:sessionId/derived |
UpdateCharacterDerivedValues |
| PUT | /api/characters/create-session/:sessionId/skills |
UpdateCharacterSkills |
| POST | /api/characters/create-session/:sessionId/finalize |
FinalizeCharacterCreation |
| DELETE | /api/characters/create-session/:sessionId |
DeleteCharacterSession |
| GET | /api/characters/races |
GetRaces |
| GET | /api/characters/classes |
GetCharacterClasses |
| GET | /api/characters/classes/learning-points |
GetCharacterClassLearningPoints |
| GET | /api/characters/origins |
GetOrigins |
| GET | /api/characters/beliefs |
SearchBeliefs |
| POST | /api/characters/calculate-static-fields |
CalculateStaticFields |
| POST | /api/characters/calculate-rolled-field |
CalculateRolledField |
8.3 equipment/
Purpose: CRUD for character-owned equipment items and weapons.
Key Files: handlers.go, routes.go
Business Logic:
checkEquipmentOwnership: Loads character by ID, comparescharacter.UserIDto logged-in user.- All operations (CRUD) filter/verify ownership before executing DB writes.
HTTP Endpoints:
| Method | Path | Handler |
|---|---|---|
| POST | /api/equipment |
CreateAusruestung |
| GET | /api/equipment/character/:character_id |
ListAusruestung |
| PUT | /api/equipment/:ausruestung_id |
UpdateAusruestung |
| DELETE | /api/equipment/:ausruestung_id |
DeleteAusruestung |
| POST | /api/weapons |
CreateWaffe |
| GET | /api/weapons/character/:character_id |
ListWaffen |
| PUT | /api/weapons/:waffe_id |
UpdateWaffe |
| DELETE | /api/weapons/:waffe_id |
DeleteWaffe |
8.4 gsmaster/
Purpose: Game-system master data management — the reference library for skills, weapon skills, spells, equipment, weapons, and learning costs.
Key Files:
handlers.go— Generic get/add/update/delete using Go generics (getMDItem[T],getMDItems[T], etc.)routes.go— Public read endpoints + maintainer-guarded write endpointslearning_costs.go—LearningCostsTablewith full EP/TE cost tables for all 15 character classeslearning_costs_init.go—ValidateLearningCostsData,GetLearningCostsSummarylevelup.go—CalculateImprovementCost,CalculateSkillImprovementCostgame_system.go— Game system lookup helpermisclookup.go—GetMiscLookupByKey,GetMiscLookupByKeyForSystem(races, faiths, origins, etc.)skill_enhanced_handlers.go,spell_enhanced_handlers.go,weapon_enhanced_handlers.go,weapon_skill_enhanced_handlers.go,equipment_enhanced_handlers.go— Enhanced CRUD with source/difficulty enrichment
Business Logic:
- Learning costs are stored both as hardcoded Go maps (in
learning_costs.go, 15 classes × 8-9 categories) and in the database (ClassCategoryEPCosttables). GetMiscLookupByKey("faiths")merges entries from bothgsm_misc_lookupsandgsm_believestables.- Generic handlers use interface constraints (
Creator,Saver,FirstIdGetter,Deleter) via Go generics for DRY CRUD.
HTTP Endpoints (/api/maintenance/...):
| Method | Path | Handler | Auth Level |
|---|---|---|---|
| GET | /api/maintenance |
GetMasterData |
Auth |
| GET | /api/maintenance/skills |
GetMDSkills |
Auth |
| GET | /api/maintenance/skills-enhanced |
GetEnhancedMDSkills |
Auth |
| GET | /api/maintenance/skills/:id |
GetMDSkill |
Auth |
| GET | /api/maintenance/skills-enhanced/:id |
GetEnhancedMDSkill |
Auth |
| GET | /api/maintenance/weaponskills |
GetMDWeaponSkills |
Auth |
| GET | /api/maintenance/weaponskills-enhanced |
GetEnhancedMDWeaponSkills |
Auth |
| GET | /api/maintenance/weaponskills/:id |
GetMDWeaponSkill |
Auth |
| GET | /api/maintenance/weaponskills-enhanced/:id |
GetEnhancedMDWeaponSkill |
Auth |
| GET | /api/maintenance/spells |
GetMDSpells |
Auth |
| GET | /api/maintenance/spells-enhanced |
GetEnhancedMDSpells |
Auth |
| GET | /api/maintenance/spells/:id |
GetMDSpell |
Auth |
| GET | /api/maintenance/spells-enhanced/:id |
GetEnhancedMDSpell |
Auth |
| GET | /api/maintenance/equipment |
GetMDEquipments |
Auth |
| GET | /api/maintenance/equipment-enhanced |
GetEnhancedMDEquipment |
Auth |
| GET | /api/maintenance/equipment/:id |
GetMDEquipment |
Auth |
| GET | /api/maintenance/equipment-enhanced/:id |
GetEnhancedMDEquipmentItem |
Auth |
| GET | /api/maintenance/weapons |
GetMDWeapons |
Auth |
| GET | /api/maintenance/weapons-enhanced |
GetEnhancedMDWeapons |
Auth |
| GET | /api/maintenance/weapons/:id |
GetMDWeapon |
Auth |
| GET | /api/maintenance/weapons-enhanced/:id |
GetEnhancedMDWeapon |
Auth |
| POST | /api/maintenance/skills-enhanced |
CreateEnhancedMDSkill |
Maintainer |
| PUT | /api/maintenance/skills/:id |
UpdateMDSkill |
Maintainer |
| PUT | /api/maintenance/skills-enhanced/:id |
UpdateEnhancedMDSkill |
Maintainer |
| POST | /api/maintenance/skills |
AddSkill |
Maintainer |
| DELETE | /api/maintenance/skills/:id |
DeleteMDSkill |
Maintainer |
| PUT | /api/maintenance/weaponskills/:id |
UpdateMDWeaponSkill |
Maintainer |
| PUT | /api/maintenance/weaponskills-enhanced/:id |
UpdateEnhancedMDWeaponSkill |
Maintainer |
| POST | /api/maintenance/weaponskills |
AddWeaponSkill |
Maintainer |
| DELETE | /api/maintenance/weaponskills/:id |
DeleteMDWeaponSkill |
Maintainer |
| PUT | /api/maintenance/spells/:id |
UpdateMDSpell |
Maintainer |
| PUT | /api/maintenance/spells-enhanced/:id |
UpdateEnhancedMDSpell |
Maintainer |
| POST | /api/maintenance/spells |
AddSpell |
Maintainer |
| DELETE | /api/maintenance/spells/:id |
DeleteMDSpell |
Maintainer |
| PUT | /api/maintenance/equipment/:id |
UpdateMDEquipment |
Maintainer |
| PUT | /api/maintenance/equipment-enhanced/:id |
UpdateEnhancedMDEquipmentItem |
Maintainer |
| POST | /api/maintenance/equipment |
AddEquipment |
Maintainer |
| DELETE | /api/maintenance/equipment/:id |
DeleteMDEquipment |
Maintainer |
| PUT | /api/maintenance/weapons/:id |
UpdateMDWeapon |
Maintainer |
| PUT | /api/maintenance/weapons-enhanced/:id |
UpdateEnhancedMDWeapon |
Maintainer |
| POST | /api/maintenance/weapons |
AddWeapon |
Maintainer |
| DELETE | /api/maintenance/weapons/:id |
DeleteMDWeapon |
Maintainer |
8.5 gamesystem/
Purpose: Minimal package that handles migration of the game_systems table and seeds the initial Midgard 5 game system.
Key File: database.go
MigrateStructure(db)— AutoMigratesGameSystem.MigrateDataIfNeeded(db)— Creates{Code:"M5", Name:"M-System"}if no row with ID=1 exists.
No HTTP endpoints registered.
8.6 maintenance/
Purpose: Admin/maintainer tooling — DB health checks, data migrations, test data generation, SQLite→MariaDB transfer.
Key Files:
handlers.go— SetupCheck, MakeTestdataFromLive, ReconnectDataBase, ReloadENV, TransferSQLiteToMariaDB, migrateAllStructuresmasterdata_handlers.go— GetGameSystems, UpdateGameSystem, GetLitSources, UpdateLitSource, GetMisc, UpdateMisc, GetSkillImprovementCost2, UpdateSkillImprovementCost2believe_handlers.go— GetBelieves, UpdateBelieveskill_migration.go— Skill-related migration helpers
Business Logic:
migrateAllStructurescalls all moduleMigrateStructure()functions in correct dependency order.MakeTestdataFromLiveexports current live DB to a SQLite file for test snapshots.TransferSQLiteToMariaDBcopies records table-by-table from SQLite to MariaDB.- All endpoints require
RequireMaintainer()orRequireAdmin()— no public access.
HTTP Endpoints (/api/maintenance/... — Maintainer required):
| Method | Path | Handler |
|---|---|---|
| GET | /api/maintenance/gsm-believes |
GetBelieves |
| PUT | /api/maintenance/gsm-believes/:id |
UpdateBelieve |
| GET | /api/maintenance/game-systems |
GetGameSystems |
| PUT | /api/maintenance/game-systems/:id |
UpdateGameSystem |
| GET | /api/maintenance/gsm-lit-sources |
GetLitSources |
| PUT | /api/maintenance/gsm-lit-sources/:id |
UpdateLitSource |
| GET | /api/maintenance/gsm-misc |
GetMisc |
| PUT | /api/maintenance/gsm-misc/:id |
UpdateMisc |
| GET | /api/maintenance/skill-improvement-cost2 |
GetSkillImprovementCost2 |
| PUT | /api/maintenance/skill-improvement-cost2/:id |
UpdateSkillImprovementCost2 |
| GET | /api/maintenance/setupcheck |
SetupCheck |
| GET | /api/maintenance/setupcheck-dev |
SetupCheckDev |
| GET | /api/maintenance/mktestdata |
MakeTestdataFromLive |
| GET | /api/maintenance/reconndb |
ReconnectDataBase |
| GET | /api/maintenance/reloadenv |
ReloadENV |
| POST | /api/maintenance/transfer-sqlite-to-mariadb |
TransferSQLiteToMariaDB |
(These overlap with the gsmaster maintenance prefix — both modules register under /api/maintenance with gsmaster first, then maintenance module adds its own routes. Both use RequireMaintainer() for writes.)
8.7 pdfrender/
Purpose: Generate multi-page PDF character sheets from HTML templates using headless Chromium + pdfcpu merge.
Key Files:
handlers.go—ListTemplates,ExportCharacterToPDF,CleanupExportTemp,GetPDFFileinit.go—InitializeTemplates— copies bundled templates toTemplatesDiron startupchromedp.go—NewPDFRenderer,RenderHTMLToPDFusing chromedpmapper.go—MapCharacterToViewModel— mapsChartoCharacterSheetViewModelviewmodel.go— All view model types (CharacterSheetViewModel,CharacterInfo,AttributeValues,DerivedValueSet,SkillViewModel,WeaponViewModel,SpellViewModel,EquipmentViewModel,MagicItemViewModel,PageMeta)render_with_continuation.go—RenderPageWithContinuations— auto-generates overflow pagespagination.go/pagination_helper.go— item distribution across pagesfill_capacity.go— reads<!-- MaxItems: N -->comments from HTML templatestemplate_parser.go/template_metadata.go— template loading and Gohtml/templateexecutioninline_resources.go— inlines CSS/images into HTML before renderingfile_management.go— managesxporttempdirectory
Business Logic:
- On startup: sync
./default_templates→cfg.TemplatesDir. ExportCharacterToPDF:- Loads character, maps to view model.
- Renders pages 1–4 (
page_1.htmlthroughpage_4.html) with continuation logic. - Each page checks
<!-- MaxItems: N -->to determine when to create a continuation page. - All rendered PDFs are merged via
pdfcpu. - Saves to
xporttemp/directory, returns{"filename": "..."}.
GetPDFFile: public endpoint, serves files fromxporttemp/by filename.
HTTP Endpoints:
| Method | Path | Handler | Auth |
|---|---|---|---|
| GET | /api/pdf/templates |
ListTemplates |
Auth |
| GET | /api/pdf/export/:id |
ExportCharacterToPDF |
Auth |
| POST | /api/pdf/cleanup |
CleanupExportTemp |
Auth |
| GET | /api/pdf/file/:filename |
GetPDFFile |
Public |
8.8 importer/
Purpose: Import characters from VTT JSON format or CSV; export characters as VTT JSON, CSV, or spell CSV.
Key Files:
handler.go—UploadFiles,ImportSpellCSVHandlerimporter.go—ImportChar,CheckSkill, VTT import logicimportVTTJson.go—ImportVTTJSON— main VTT import driverexporter.go—ExportCharToVTT,ExportCharacterToCSVmodel.go—CharacterImportstruct (VTT format), sub-structs for Fertigkeiten/Spells/Equipmentroutes.go— Route registration
Business Logic:
UploadFiles: Acceptsfile_vtt(required) +file_csv(optional), validates extensions (.csv/.json), callsImportVTTJSON.CheckSkill: Looks up skill ingsm_skills, auto-creates ifautocreate=true.- Source IDs are cached (
sourceIDCache) to reduce DB queries during bulk import. ExportCharacterVTTFileHandler: Returns character data as downloadable.jsonfile.
HTTP Endpoints:
| Method | Path | Handler |
|---|---|---|
| POST | /api/importer/upload |
UploadFiles |
| POST | /api/importer/spells/csv |
ImportSpellCSVHandler |
| GET | /api/importer/export/vtt/:id |
ExportCharacterVTTHandler |
| GET | /api/importer/export/vtt/:id/file |
ExportCharacterVTTFileHandler |
| GET | /api/importer/export/csv/:id |
ExportCharacterCSVHandler |
| GET | /api/importer/export/spells/csv |
ExportSpellsCSVHandler |
8.9 transfer/
Purpose: JSON-serialized character import/export and full database backup/restore.
Key Files:
handlers.go— HTTP handlers for all transfer operationsexporter.go—ExportCharacter— serializes a fullChar+ metadata toCharacterExportimporter.go—ImportCharacter— deserializes and recreates character in DBdatabase.go—ExportDatabase,ImportDatabase— full table serializationroutes.go— Route registration
Business Logic:
ExportCharacter/ImportCharacter: Full round-trip JSON serialization of characters with all associations.ExportDatabase/ImportDatabase: Table-level JSON snapshot used for backup/restore.DownloadCharacterHandlersetsContent-Disposition: attachmentfor browser download.
HTTP Endpoints:
| Method | Path | Handler |
|---|---|---|
| GET | /api/transfer/export/:id |
ExportCharacterHandler |
| GET | /api/transfer/download/:id |
DownloadCharacterHandler |
| POST | /api/transfer/import |
ImportCharacterHandler |
| POST | /api/transfer/database/export |
ExportDatabaseHandler |
| POST | /api/transfer/database/import |
ImportDatabaseHandler |
8.10 appsystem/
Purpose: Exposes application version and system info (user/character counts, DB version).
Key Files:
version.go—Version = "0.2.4",GetInfo(),GetInfo2()handlers.go—Versionsinfo,SystemInforoutes.go— Route registration (both protected and public)
HTTP Endpoints:
| Method | Path | Handler | Auth |
|---|---|---|---|
| GET | /api/version |
Versionsinfo |
Auth |
| GET | /api/systeminfo |
SystemInfo |
Auth |
| GET | /api/public/version |
Versionsinfo |
Public |
| GET | /api/public/systeminfo |
SystemInfo |
Public |
8.11 logger/
Purpose: Custom leveled logger with timestamp prefixing.
Key API:
logger.Debug(format, args...)
logger.Info(format, args...)
logger.Warn(format, args...)
logger.Error(format, args...)
logger.SetDebugMode(bool)
logger.SetMinLogLevel(logger.DEBUG | INFO | WARN | ERROR)
- Outputs to
os.Stdoutwith format:[YYYY-MM-DD HH:MM:SS] LEVEL: message - Debug messages are suppressed unless both
debugEnabled=trueANDminLevel<=DEBUG. - Configured once in
main()fromconfig.Cfg.
8.12 mail/
Purpose: SMTP email client used exclusively for password-reset emails.
Key File: smtp.go
type Client struct {
host, username, password, from string
port int
}
func NewClient() *Client // reads from config.Cfg
func (c *Client) Send(msg Message) error
- Port 465: Direct TLS (
tls.Dial) - Port 587: STARTTLS (
smtp.Dial+TLSConfig) - If
host == "": logs the email content instead of sending (fallback for unconfigured environments).
8.13 router/
Purpose: Central Gin setup and base route group creation.
Files:
setup.go—SetupGin(r)— CORS middlewareroutes.go—BaseRouterGrp(r)— public auth routes + protected/apigrouproutes_test.go— router tests
8.14 config/
Purpose: Application configuration loading and validation.
Key Functions:
init()— auto-loads config on package importLoadConfig()— reads.env/.env.local, then env varsloadEnvFile()/loadEnvFileContent()— parseKEY=VALUElinescfg.IsProduction() boolcfg.GetServerAddress() string— returns":PORT"format
8.15 database/
Purpose: Database connection management, test helpers, and migration execution.
Key Functions:
ConnectDatabase()— routes toSetupTestDB()orConnectDatabaseOrig()based on envConnectDatabaseOrig()— opens MySQL or SQLite with GORMSetupTestDB(opts ...bool)— copies snapshottestdata/prepared_test_data.db→ temp dir, opens itMigrateStructure(db)— migratesschema_version+migration_historyMigrateDataIfNeeded(db)— ensures schema version record existsGetDB()— lazy init getter
Path Constants:
PreparedTestDB = filepath.Join(backendDir, "testdata", "prepared_test_data.db")
TestDataDir = filepath.Join(backendDir, "maintenance", "testdata")
8.16 testutils/
Purpose: Shared test environment setup utility.
func SetupTestEnvironment(t *testing.T)
func SetupTestEnvironmentWithConfig(t *testing.T, envVars map[string]string)
func EnsureTestEnvironment(t *testing.T)
- Sets
ENVIRONMENT=testto redirect DB connection to SQLite test snapshot. - Registers
t.Cleanup()to restore original env vars after each test.
8.17 api/
Purpose: Integration tests that test multi-module interactions via full HTTP stack.
Key test: TestListCharacters — spins up Gin with all essential routes, uses database.SetupTestDB(true).
9. Complete API Route Listing
Public (No Authentication)
| Method | Path | Module | Purpose |
|---|---|---|---|
| POST | /register |
user | Register a new user |
| POST | /login |
user | Login, get token |
| POST | /password-reset/request |
user | Request password reset email |
| GET | /password-reset/validate/:token |
user | Validate reset token |
| POST | /password-reset/reset |
user | Set new password |
| GET | /api/pdf/file/:filename |
pdfrender | Download generated PDF |
| GET | /api/public/version |
appsystem | App version (no auth) |
| GET | /api/public/systeminfo |
appsystem | System info (no auth) |
Protected — All Authenticated Users (/api/...)
User Management:
| Method | Path | Purpose |
|---|---|---|
| GET | /api/user/profile |
Own profile |
| PUT | /api/user/display-name |
Update display name |
| PUT | /api/user/email |
Update email |
| PUT | /api/user/password |
Change password |
| PUT | /api/user/language |
Set preferred language |
Characters:
| Method | Path | Purpose |
|---|---|---|
| GET | /api/characters |
List own + shared + public characters |
| POST | /api/characters |
Create character |
| GET | /api/characters/:id |
Get full character (FeChar) |
| PUT/PATCH | /api/characters/:id |
Update character |
| DELETE | /api/characters/:id |
Delete character |
| PUT | /api/characters/:id/image |
Upload character image |
| GET | /api/characters/:id/datasheet-options |
PDF datasheet options |
| GET | /api/characters/:id/shares |
List share recipients |
| PUT | /api/characters/:id/shares |
Update share recipients |
| GET | /api/characters/:id/available-users |
Users available to share with |
| GET | /api/characters/:id/experience-wealth |
Current EP + wealth |
| PUT | /api/characters/:id/experience |
Update experience points |
| PUT | /api/characters/:id/wealth |
Update wealth |
| GET | /api/characters/:id/audit-log |
Change history |
| GET | /api/characters/:id/audit-log/stats |
Change statistics |
| POST | /api/characters/lerncost |
Calculate learn/improve cost |
| POST | /api/characters/lerncost-new |
(Alias) Calculate learn/improve cost |
| POST | /api/characters/improve-skill |
Apply skill improvement |
| POST | /api/characters/improve-skill-new |
(Alias) Apply skill improvement |
| POST | /api/characters/:id/learn-skill |
Learn new skill |
| POST | /api/characters/:id/learn-skill-new |
(Alias) Learn new skill |
| POST | /api/characters/:id/learn-spell |
Learn new spell |
| POST | /api/characters/:id/learn-spell-new |
(Alias) Learn new spell |
| POST | /api/characters/available-skills |
Available skills to learn |
| POST | /api/characters/available-skills-new |
(Alias) |
| POST | /api/characters/available-skills-creation |
Skills for creation wizard |
| POST | /api/characters/available-spells |
Available spells to learn |
| POST | /api/characters/available-spells-new |
(Alias) |
| POST | /api/characters/available-spells-creation |
Spells for creation wizard |
| GET | /api/characters/spell-details |
Spell detail lookup |
| GET | /api/characters/:id/reward-types |
Reward options for this context |
| GET | /api/characters/:id/practice-points |
Current practice points |
| PUT | /api/characters/:id/practice-points |
Update practice points |
| POST | /api/characters/:id/practice-points/add |
Add a practice point |
| POST | /api/characters/:id/practice-points/use |
Use a practice point |
| GET | /api/characters/skill-categories |
Static skill category listing |
| GET | /api/characters/create-sessions |
List creation wizard sessions |
| POST | /api/characters/create-session |
Start creation wizard session |
| GET | /api/characters/create-session/:sessionId |
Get session data |
| PUT | /api/characters/create-session/:sessionId/basic |
Save basic info |
| PUT | /api/characters/create-session/:sessionId/attributes |
Save attributes |
| PUT | /api/characters/create-session/:sessionId/derived |
Save derived values |
| PUT | /api/characters/create-session/:sessionId/skills |
Save skills selection |
| POST | /api/characters/create-session/:sessionId/finalize |
Finalize creation |
| DELETE | /api/characters/create-session/:sessionId |
Discard session |
| GET | /api/characters/races |
Available races |
| GET | /api/characters/classes |
Available character classes |
| GET | /api/characters/classes/learning-points |
LP by class |
| GET | /api/characters/origins |
Available origins |
| GET | /api/characters/beliefs |
Faith search |
| POST | /api/characters/calculate-static-fields |
Calc derived values (no dice) |
| POST | /api/characters/calculate-rolled-field |
Calc derived value (with dice) |
Equipment:
| Method | Path | Purpose |
|---|---|---|
| POST | /api/equipment |
Add equipment item |
| GET | /api/equipment/character/:character_id |
List equipment for character |
| PUT | /api/equipment/:ausruestung_id |
Update equipment |
| DELETE | /api/equipment/:ausruestung_id |
Delete equipment |
| POST | /api/weapons |
Add weapon |
| GET | /api/weapons/character/:character_id |
List weapons for character |
| PUT | /api/weapons/:waffe_id |
Update weapon |
| DELETE | /api/weapons/:waffe_id |
Delete weapon |
Master Data (read — all authenticated, write — maintainers only):
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /api/maintenance |
Auth | All master data overview |
| GET | /api/maintenance/skills |
Auth | List skills |
| GET | /api/maintenance/skills/:id |
Auth | Get skill |
| GET | /api/maintenance/skills-enhanced |
Auth | List skills (rich) |
| GET | /api/maintenance/skills-enhanced/:id |
Auth | Get skill (rich) |
| POST | /api/maintenance/skills |
Maintainer | Create skill |
| POST | /api/maintenance/skills-enhanced |
Maintainer | Create skill (rich) |
| PUT | /api/maintenance/skills/:id |
Maintainer | Update skill |
| PUT | /api/maintenance/skills-enhanced/:id |
Maintainer | Update skill (rich) |
| DELETE | /api/maintenance/skills/:id |
Maintainer | Delete skill |
| GET | /api/maintenance/weaponskills[/:id] |
Auth | Weapon skill(s) |
| GET | /api/maintenance/weaponskills-enhanced[/:id] |
Auth | Weapon skill(s) (rich) |
| POST/PUT/DELETE | /api/maintenance/weaponskills[/:id] |
Maintainer | Weapon skill CRUD |
| GET | /api/maintenance/spells[/:id] |
Auth | Spell(s) |
| GET | /api/maintenance/spells-enhanced[/:id] |
Auth | Spell(s) (rich) |
| POST/PUT/DELETE | /api/maintenance/spells[/:id] |
Maintainer | Spell CRUD |
| GET | /api/maintenance/equipment[/:id] |
Auth | Equipment item(s) |
| GET | /api/maintenance/equipment-enhanced[/:id] |
Auth | Equipment (rich) |
| POST/PUT/DELETE | /api/maintenance/equipment[/:id] |
Maintainer | Equipment CRUD |
| GET | /api/maintenance/weapons[/:id] |
Auth | Master weapon(s) |
| GET | /api/maintenance/weapons-enhanced[/:id] |
Auth | Master weapon(s) (rich) |
| POST/PUT/DELETE | /api/maintenance/weapons[/:id] |
Maintainer | Weapon CRUD |
Maintenance Admin (Maintainer required):
| Method | Path | Purpose |
|---|---|---|
| GET | /api/maintenance/gsm-believes |
List faith entries |
| PUT | /api/maintenance/gsm-believes/:id |
Update faith entry |
| GET | /api/maintenance/game-systems |
List game systems |
| PUT | /api/maintenance/game-systems/:id |
Update game system |
| GET | /api/maintenance/gsm-lit-sources |
List source books |
| PUT | /api/maintenance/gsm-lit-sources/:id |
Update source book |
| GET | /api/maintenance/gsm-misc |
MiscLookup entries |
| PUT | /api/maintenance/gsm-misc/:id |
Update misc entry |
| GET/PUT | /api/maintenance/skill-improvement-cost2[/:id] |
Improvement cost table |
| GET | /api/maintenance/setupcheck |
DB structure validation |
| GET | /api/maintenance/setupcheck-dev |
Extended setup check |
| GET | /api/maintenance/mktestdata |
Generate test snapshot from live DB |
| GET | /api/maintenance/reconndb |
Reconnect database |
| GET | /api/maintenance/reloadenv |
Reload .env config |
| POST | /api/maintenance/transfer-sqlite-to-mariadb |
Migrate data |
PDF:
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /api/pdf/templates |
Auth | List available PDF templates |
| GET | /api/pdf/export/:id |
Auth | Export character to PDF |
| POST | /api/pdf/cleanup |
Auth | Clean up old PDFs |
| GET | /api/pdf/file/:filename |
Public | Download PDF file |
Import/Export:
| Method | Path | Purpose |
|---|---|---|
| POST | /api/importer/upload |
Import character from VTT JSON |
| POST | /api/importer/spells/csv |
Import spells from CSV |
| GET | /api/importer/export/vtt/:id |
Export character as VTT JSON (response body) |
| GET | /api/importer/export/vtt/:id/file |
Export character as downloadable VTT JSON |
| GET | /api/importer/export/csv/:id |
Export character as CSV |
| GET | /api/importer/export/spells/csv |
Export all spells as CSV |
| GET | /api/transfer/export/:id |
Export character as BaMoRT JSON |
| GET | /api/transfer/download/:id |
Download character as JSON file |
| POST | /api/transfer/import |
Import character from BaMoRT JSON |
| POST | /api/transfer/database/export |
Full DB export to JSON |
| POST | /api/transfer/database/import |
Full DB import from JSON |
System:
| Method | Path | Auth | Purpose |
|---|---|---|---|
| GET | /api/version |
Auth | App version + git commit |
| GET | /api/systeminfo |
Auth | Version + user/char counts + DB version |
Admin (Admin role required):
| Method | Path | Purpose |
|---|---|---|
| GET | /api/users |
List all users |
| GET | /api/users/:id |
Get user by ID |
| PUT | /api/users/:id/role |
Change user role |
| PUT | /api/users/:id/password |
Change user password |
| DELETE | /api/users/:id |
Delete user |
10. Testing Approach
Test Environment Setup
Every test file that touches the database must call:
func setupTestEnvironment(t *testing.T) {
original := os.Getenv("ENVIRONMENT")
os.Setenv("ENVIRONMENT", "test")
t.Cleanup(func() { os.Setenv("ENVIRONMENT", original) })
}
Or the shared utility:
testutils.SetupTestEnvironment(t)
This causes database.ConnectDatabase() to open the SQLite test snapshot instead of MariaDB.
Test Database Mechanism
testdata/prepared_test_data.db— a SQLite snapshot with real-world test data.database.SetupTestDB(true)copies this to aos.MkdirTemplocation and opens it.- Tests run against this isolated copy — no pollution of live data.
- Character ID 18 ("Fanjo Vetrani") is guaranteed to exist in the test DB.
Test Structure Conventions
- All test files use
_test.gosuffix. - Test functions call
database.SetupTestDB(true)ortestutils.SetupTestEnvironment(t)first. - Integration tests use
testify/assert. - Many module tests (
character_test.go,handlers_test.go, etc.) use helper constructors fromtest_data_helper.go. - The
api/api_test.gopackage runs HTTP tests usinghttptest.NewRecorder(). maintenance/copy_db_test.gotests the DB snapshot refresh mechanism.- Test data helpers (
character/test_data_helper.go) provideCreateTestCharacter(), etc.
Key Test Files by Module
| Module | Test Files |
|---|---|
api/ |
api_test.go — Integration |
character/ |
character_test.go, handlers_test.go, lerncost_handler_test.go, skill_update_test.go, learn_spell_test.go, etc. |
database/ |
database_test.go, testhelper_test.go |
gsmaster/ |
gsmaster_test.go, learning_costs_test.go, export_import_test.go |
importer/ |
importer_test.go, charimport_test.go, upload_test.go |
maintenance/ |
handlers_test.go, masterdata_handlers_test.go, copy_db_test.go |
models/ |
model_character_test.go, model_char_skills_test.go, model_learning_costs_test.go |
pdfrender/ |
chromedp_test.go, mapper_test.go, pagination_test.go, integration_test.go |
transfer/ |
database_test.go, exporter_test.go, importer_test.go |
user/ |
handlers_test.go, role_test.go |
11. Key Dependencies
| Dependency | Version | Purpose |
|---|---|---|
github.com/gin-gonic/gin |
v1.10.0 | HTTP framework |
github.com/gin-contrib/cors |
v1.7.3 | CORS middleware |
gorm.io/gorm |
v1.25.12 | ORM |
gorm.io/driver/mysql |
v1.5.7 | MySQL/MariaDB driver |
gorm.io/driver/sqlite |
v1.5.7 | SQLite driver (tests + dev) |
github.com/chromedp/chromedp |
v0.14.2 | Headless Chrome for PDF rendering |
github.com/pdfcpu/pdfcpu |
v0.11.1 | PDF merging |
github.com/stretchr/testify |
v1.10.0 | Test assertions |
12. Security Observations
Critical Issues
-
Weak Token Authentication: The custom token embeds the user ID in a predictable location. Any token with any valid user ID at position
7 + len("Bearer ")is accepted. There is no HMAC or digital signature. An attacker can forge tokens. -
MD5 Password Hashing: Passwords are stored as MD5 hashes. MD5 is cryptographically broken for password storage; brute-force and rainbow-table attacks are trivial. Should be replaced with bcrypt, argon2id, or scrypt.
-
Token Leaks User ID: The token format encodes the user ID in plaintext, revealing enumerable user IDs to clients.
Moderate Concerns
-
Debug Logging of Passwords:
handlers.gohasfmt.Printf("pwdh: %s", ...)that prints password hashes to stdout on every login — a security logging issue. -
Hardcoded Origins in CORS: Local network IPs (
192.168.0.48,192.168.0.36) are hardcoded — appropriate for development but should be env-driven for production. -
Importer File Upload:
UploadFilessaves files to./uploads/relative path without sanitizing filenames for path traversal. The extension validation (isValidFileType) only checks the suffix. -
SQL Injection via
fieldName:GetMiscLookupByKeyForSystemconstructsquery.Order(orderBy)from a user-suppliedorderparameter — however the value is mapped to a safe set of constants before use, so actual injection risk is mitigated. -
No rate limiting: Login and registration endpoints have no rate limiting, making them susceptible to brute-force attacks.
Notes
- The bcrypt implementation is commented out in
handlers.gowith the MD5 hash in use — this is intentional but documents the known deficit. - Password reset tokens use
crypto/rand(secure, 32 bytes), which is correct. - GORM parameterized queries protect most DB operations from SQL injection.
13. Cross-Module Relationships Diagram
cmd/main.go
├── config ──────────────────────────────────── read by all modules
├── logger ──────────────────────────────────── used by all modules
├── database ─────────────────────────────────── .DB used by all models
│ └── testutils ────────────────────────── test env setup
├── router
│ └── user (AuthMiddleware, role guards)
├── user ─────────────────── handlers, model, auth
│ └── mail (SMTP for password reset)
├── models ────────────────── GORM entities, MigrateStructure
│ ├── user.User (FK in Char)
│ └── database.DB (queries)
├── character ─────────────── CRUD + learning + creation wizard
│ ├── models (Char, SkFertigkeit, SkZauber, etc.)
│ ├── gsmaster (LerncostRequest, skill lookups)
│ └── database.DB
├── equipment ─────────────── Equipment/Weapon CRUD
│ ├── models (EqAusruestung, EqWaffe)
│ └── database.DB
├── gsmaster ──────────────── Master data (skills/spells/weapons)
│ ├── models (Skill, Spell, Weapon, etc.)
│ ├── user (RequireMaintainer guard)
│ └── database.DB
├── gamesystem ────────────── GameSystem seed/migration
│ └── models.GameSystem
├── maintenance ───────────── Admin tooling, migration orchestration
│ ├── database, gamesystem, user, models (all MigrateStructure)
│ └── user (RequireMaintainer guard)
├── pdfrender ─────────────── HTML→PDF pipeline
│ ├── models.Char (character data)
│ └── config (TemplatesDir, ExportTempDir)
├── importer ──────────────── VTT/CSV import/export
│ └── models (Char, Skill, Spell)
├── transfer ──────────────── JSON character + DB backup
│ └── models.Char
└── appsystem ─────────────── Version info
└── database.DB (count queries)
End of findings — /home/de31a2/bamort/backend_findings.md