042a1d4773
* introduced central package registry by package init function * dynamic registration of routes, model, migrations and initializers. * setting a docker compose project name to prevent shutdown of other containers with the same (composer)name * ai documentation * app template * Create tests for ALL API entpoints in ALL packages Based on current data. Ensure that all API endpoints used in frontend are tested. These tests are crucial for the next refactoring tasks. * adopting agent instructions for a more consistent coding style * added desired module layout and debugging information * Fix All Failing tests All failing tests are fixed now that makes the refactoring more easy since all tests must pass * restored routes for maintenance * added common translations * added new tests for API Endpoint * Merge branch 'separate_business_logic' * added lern and skill improvement cost editing * Set Docker image tag when building to prevent rebuild when nothing has changed * add and remove PP for Weaponskill fixed * add and remove PP for same named skills fixed * add new task
704 lines
26 KiB
Markdown
704 lines
26 KiB
Markdown
# Bamort Infrastructure Findings
|
|
|
|
> Generated: 2026-03-13
|
|
> Scope: Docker infrastructure, scripts, networking, persistence, dev/prod differences
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Docker Compose Configurations](#1-docker-compose-configurations)
|
|
2. [Dockerfiles](#2-dockerfiles)
|
|
3. [Nginx Configuration](#3-nginx-configuration)
|
|
4. [Scripts](#4-scripts)
|
|
5. [Database Initialization](#5-database-initialization)
|
|
6. [Network Architecture](#6-network-architecture)
|
|
7. [Data Persistence](#7-data-persistence)
|
|
8. [Development vs Production Differences](#8-development-vs-production-differences)
|
|
|
|
---
|
|
|
|
## 1. Docker Compose Configurations
|
|
|
|
### 1.1 `docker/docker-compose.dev.yml` — Development Stack
|
|
|
|
**Compose project name**: `bamort` (set via `COMPOSE_PROJECT_NAME` env var)
|
|
|
|
#### Service: `backend-dev`
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Container name | `bamort-backend-dev` |
|
|
| Build context | `../backend` |
|
|
| Dockerfile | `../docker/Dockerfile.backend.dev` |
|
|
| Host port → Container port | `8180 → 8180` |
|
|
| Working directory | `/app` |
|
|
| Restart policy | `unless-stopped` |
|
|
| Depends on | `mariadb-dev` (condition: `service_healthy`) |
|
|
|
|
**Environment variables:**
|
|
```
|
|
GO_ENV=development
|
|
CGO_ENABLED=1
|
|
DATABASE_TYPE=${DATABASE_TYPE:-mysql}
|
|
DATABASE_URL=${DATABASE_URL:-<user>:<pass>@tcp(mariadb-dev:3306)/<db>?charset=utf8mb4&parseTime=True&loc=Local}
|
|
API_PORT=${API_PORT:-8180}
|
|
TEMPLATES_DIR=${TEMPLATES_DIR:-./templatesx}
|
|
EXPORT_TEMP_DIR=${EXPORT_TEMP_DIR:-./export_tempx}
|
|
GIT_COMMIT=${GIT_COMMIT:-unknown}
|
|
```
|
|
> Note: `TEMPLATES_DIR` defaults to `./templatesx` (with trailing 'x') in Docker — this means the container uses the source-mounted `../backend:/app` volume and the actual templates directory at `/app/templates`.
|
|
|
|
**Volumes:**
|
|
- `../backend:/app` — full backend source mounted for live-reload via Air
|
|
- `go-mod-cache:/go/pkg/mod` — named volume to cache Go module downloads
|
|
|
|
---
|
|
|
|
#### Service: `frontend-dev`
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Container name | `bamort-frontend-dev` |
|
|
| Build context | `../frontend` |
|
|
| Dockerfile | `../docker/Dockerfile.frontend.dev` |
|
|
| Host port → Container port | `5173 → 5173` |
|
|
| Restart policy | `unless-stopped` |
|
|
| Depends on | `backend-dev` |
|
|
|
|
**Environment variables:**
|
|
```
|
|
NODE_ENV=development
|
|
VITE_API_URL=${API_URL:-http://192.168.0.1:8180}
|
|
VITE_BASE_URL=${BASE_URL:-http://bamort.trokan.de}
|
|
VITE_API_PORT=${API_PORT:-8180}
|
|
```
|
|
> The default `VITE_API_URL` points to `192.168.0.1` — a LAN IP. In practice this is overridden via `.env.dev` to `http://localhost:8180`.
|
|
|
|
**Volumes:**
|
|
- `../frontend:/app` — full frontend source mounted for Vite HMR
|
|
- `/app/node_modules` — anonymous volume to prevent host from overwriting container's installed `node_modules`
|
|
|
|
---
|
|
|
|
#### Service: `mariadb-dev`
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Container name | `bamort-mariadb-dev` |
|
|
| Image | `mariadb:11.4` |
|
|
| Host port → Container port | `3306 → 3306` |
|
|
| Restart policy | `unless-stopped` |
|
|
|
|
**Environment variables:**
|
|
```
|
|
MARIADB_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD:-secure_root_password}
|
|
MARIADB_DATABASE=${MARIADB_DATABASE:-bamort}
|
|
MARIADB_USER=${MARIADB_USER:-bamort}
|
|
MARIADB_PASSWORD=${MARIADB_PASSWORD:-secure_user_password}
|
|
MARIADB_CHARSET=utf8mb4
|
|
MARIADB_COLLATION=utf8mb4_unicode_ci
|
|
```
|
|
|
|
**Volumes:**
|
|
- `./bamort-db-dev:/var/lib/mysql` — persistent database files stored in `docker/bamort-db-dev/`
|
|
- `./init-db:/docker-entrypoint-initdb.d` — SQL initialization scripts (run only on first container creation)
|
|
|
|
**Health check:**
|
|
```
|
|
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
|
start_period: 10s
|
|
timeout: 5s
|
|
retries: 3
|
|
```
|
|
|
|
---
|
|
|
|
#### Service: `phpmyadmin-dev`
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Container name | `bamort-phpmyadmin-dev` |
|
|
| Image | `phpmyadmin/phpmyadmin:5.2` |
|
|
| Host port → Container port | `8081 → 80` |
|
|
| Restart policy | `unless-stopped` |
|
|
| Depends on | `mariadb-dev` (condition: `service_healthy`) |
|
|
|
|
**Environment variables:**
|
|
```
|
|
PMA_HOST=mariadb-dev
|
|
PMA_PORT=3306
|
|
PMA_USER=root
|
|
PMA_PASSWORD=${MARIADB_ROOT_PASSWORD:-secure_root_password}
|
|
MYSQL_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD:-secure_root_password}
|
|
PMA_ARBITRARY=1
|
|
```
|
|
|
|
**Named volumes declared:**
|
|
```yaml
|
|
volumes:
|
|
go-mod-cache:
|
|
```
|
|
|
|
---
|
|
|
|
### 1.2 `docker/docker-compose.yml` — Production Stack
|
|
|
|
#### Service: `backend`
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Container name | `bamort-backend` |
|
|
| Build context | `../backend` |
|
|
| Dockerfile | `../docker/Dockerfile.backend` |
|
|
| Host port → Container port | `8182 → 8180` |
|
|
| Working directory | `/app` |
|
|
| Restart policy | `unless-stopped` |
|
|
| Depends on | `mariadb` (condition: `service_healthy`) |
|
|
|
|
**Environment variables:**
|
|
```
|
|
GO_ENV=production
|
|
CGO_ENABLED=1
|
|
DATABASE_TYPE=${DATABASE_TYPE:-mysql}
|
|
DATABASE_URL=<user>:<pass>@tcp(mariadb:3306)/<db>?charset=utf8mb4&parseTime=True&loc=Local
|
|
BASE_URL=${BASE_URL:-https://bamort.trokan.de}
|
|
API_PORT=${API_PORT:-8180}
|
|
```
|
|
> Note: `DATABASE_URL` in production is NOT wrapped in `${}` with a fallback — it's constructed inline, which means any unset env vars will result in empty credentials.
|
|
|
|
**Volumes:**
|
|
- `./templates:/app/templates` — mounts host templates directory (no source code mounted)
|
|
|
|
---
|
|
|
|
#### Service: `frontend`
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Container name | `bamort-frontend` |
|
|
| Build context | `../frontend` |
|
|
| Dockerfile | `../docker/Dockerfile.frontend` |
|
|
| Host port → Container port | `8181 → 80` |
|
|
| Restart policy | `unless-stopped` |
|
|
| Depends on | `backend` |
|
|
|
|
**Build args (baked into the image at build time):**
|
|
```
|
|
VITE_API_URL: ${API_URL:-https://bamort-api.trokan.de}
|
|
VITE_BASE_URL: ${BASE_URL:-https://bamort.trokan.de}
|
|
VITE_API_PORT: ${API_PORT:-443}
|
|
```
|
|
> These are compile-time args embedded into the Vite bundle — runtime `environment:` vars have no effect on the built static bundle.
|
|
|
|
**Environment variables (runtime — informational only):**
|
|
```
|
|
NODE_ENV=production
|
|
VITE_API_URL=${API_URL:-https://bamort.trokan.de:8180}
|
|
VITE_BASE_URL=${BASE_URL:-https://bamort.trokan.de}
|
|
VITE_API_PORT=${API_PORT:-8180}
|
|
```
|
|
|
|
---
|
|
|
|
#### Service: `mariadb`
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Container name | `bamort-mariadb` |
|
|
| Image | `mariadb:11.4` |
|
|
| Host port | **NOT EXPOSED** (ports section commented out) |
|
|
| Restart policy | `unless-stopped` |
|
|
|
|
**Environment variables:** Same as dev
|
|
**Volumes:**
|
|
- `./bamort-db:/var/lib/mysql` — persistent data in `docker/bamort-db/`
|
|
- `./init-db:/docker-entrypoint-initdb.d`
|
|
|
|
**Health check:** Same, but with longer timeouts (`start_period: 20s`, `timeout: 10s`, `retries: 3`)
|
|
|
|
---
|
|
|
|
#### Service: `phpmyadmin` (COMMENTED OUT in production)
|
|
The service definition exists in the file but is fully commented out for security. Can be re-enabled via:
|
|
```bash
|
|
sed -i 's/^ # phpmyadmin:/ phpmyadmin:/' docker-compose.yml
|
|
docker-compose up -d phpmyadmin
|
|
```
|
|
If re-enabled: port `8081 → 80`
|
|
|
|
---
|
|
|
|
## 2. Dockerfiles
|
|
|
|
### 2.1 `docker/Dockerfile.backend` — Production Backend (Multi-stage)
|
|
|
|
**Stage 1: `builder`** — `golang:1.25-alpine`
|
|
```dockerfile
|
|
FROM golang:1.25-alpine AS builder
|
|
RUN apk add --no-cache gcc musl-dev sqlite-dev
|
|
WORKDIR /app
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY . .
|
|
RUN go build -v -o server cmd/main.go
|
|
```
|
|
- Installs GCC (required for CGO), musl-dev, and sqlite-dev (CGO is enabled for SQLite support)
|
|
- Builds a static binary named `server` from `cmd/main.go`
|
|
|
|
**Stage 2: Runtime** — `alpine:3.23`
|
|
```dockerfile
|
|
FROM alpine:3.23
|
|
RUN apk add --no-cache \
|
|
chromium chromium-chromedriver \
|
|
nss freetype harfbuzz ca-certificates ttf-freefont
|
|
ENV CHROME_BIN=/usr/bin/chromium-browser \
|
|
CHROME_PATH=/usr/bin/chromium-browser
|
|
WORKDIR /app
|
|
COPY --from=builder /app/server /app
|
|
COPY --from=builder /app/templates /app/default_templates
|
|
EXPOSE 8180
|
|
CMD ["./server"]
|
|
```
|
|
- Installs Chromium for PDF rendering via chromedp
|
|
- Copies compiled binary and templates from builder stage
|
|
- Templates copied to `default_templates/` (runtime templates come from volume mount `./templates:/app/templates`)
|
|
- Result: minimal Alpine image with only Chromium and the binary
|
|
|
|
---
|
|
|
|
### 2.2 `docker/Dockerfile.backend.dev` — Development Backend (Single-stage)
|
|
|
|
**Base**: `golang:1.25-alpine` (single stage — no build separation)
|
|
|
|
```dockerfile
|
|
FROM golang:1.25-alpine
|
|
RUN apk add --no-cache gcc musl-dev sqlite-dev
|
|
RUN apk add --no-cache chromium chromium-chromedriver nss freetype harfbuzz ca-certificates ttf-freefont
|
|
ENV CHROME_BIN=/usr/bin/chromium-browser \
|
|
CHROME_PATH=/usr/bin/chromium-browser
|
|
RUN go install github.com/air-verse/air@latest
|
|
WORKDIR /app
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
EXPOSE 8180
|
|
CMD ["air", "-c", ".air.toml"]
|
|
```
|
|
|
|
**Key differences from production:**
|
|
- Includes the full Go toolchain (for recompilation on file changes)
|
|
- Installs `air` (live-reload tool from `github.com/air-verse/air@latest`)
|
|
- Full source code is mounted at runtime via volume, not copied into image
|
|
- Starts with `air -c .air.toml` instead of the compiled binary
|
|
|
|
**Air configuration** (`.air.toml`):
|
|
```toml
|
|
root = "."
|
|
tmp_dir = "tmp"
|
|
|
|
[build]
|
|
bin = "./tmp/main"
|
|
cmd = "go build -o ./tmp/main ./cmd/main.go"
|
|
delay = 1000 # 1 second delay before rebuild
|
|
exclude_dir = ["assets", "tmp", "vendor", "testdata", "uploads", "transfer/xporttemp", "export_temp"]
|
|
exclude_regex = ["_test.go"] # Test files excluded from watch
|
|
include_ext = ["go", "tpl", "tmpl", "html"]
|
|
log = "build-errors.log"
|
|
```
|
|
Air watches `.go`, `.tpl`, `.tmpl`, `.html` files, excludes test files, and rebuilds into `./tmp/main` on any change.
|
|
|
|
---
|
|
|
|
### 2.3 `docker/Dockerfile.frontend` — Production Frontend (Multi-stage)
|
|
|
|
**Stage 1: `build`** — `node:22-alpine`
|
|
```dockerfile
|
|
FROM node:22-alpine AS build
|
|
ARG VITE_API_URL
|
|
ARG VITE_BASE_URL
|
|
ARG VITE_API_PORT
|
|
ENV VITE_API_URL=$VITE_API_URL
|
|
ENV VITE_BASE_URL=$VITE_BASE_URL
|
|
ENV VITE_API_PORT=$VITE_API_PORT
|
|
WORKDIR /usr/src/app
|
|
COPY package*.json ./
|
|
RUN npm install
|
|
COPY . .
|
|
RUN npm run build
|
|
```
|
|
- Accepts `VITE_*` values as build ARGs → embedded into the static bundle by Vite
|
|
- Runs `npm run build` which produces `dist/`
|
|
|
|
**Stage 2: `serve`** — `nginx:alpine`
|
|
```dockerfile
|
|
FROM nginx:alpine
|
|
COPY --from=build /usr/src/app/dist /usr/share/nginx/html
|
|
COPY --from=build /usr/src/app/nginx.conf /etc/nginx/conf.d/default.conf
|
|
EXPOSE 80
|
|
CMD ["nginx", "-g", "daemon off;"]
|
|
```
|
|
- Serves the static Vue bundle via Nginx
|
|
- Uses `frontend/nginx.conf` for SPA routing (see section 3)
|
|
|
|
---
|
|
|
|
### 2.4 `docker/Dockerfile.frontend.dev` — Development Frontend (Single-stage)
|
|
|
|
**Base**: `node:22-alpine`
|
|
|
|
```dockerfile
|
|
FROM node:22-alpine
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm install
|
|
EXPOSE 5173
|
|
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
|
```
|
|
|
|
- No build step — Vite dev server handles hot module replacement (HMR)
|
|
- `--host 0.0.0.0` binds Vite to all interfaces (required for Docker port exposure)
|
|
- Full source mounted at `/app` via volume at runtime
|
|
- `node_modules` protected inside container via anonymous volume in compose
|
|
|
|
---
|
|
|
|
## 3. Nginx Configuration
|
|
|
|
**Note:** Both `docker/nginx.conf` and `frontend/nginx.conf` are **identical** in content. The `frontend/nginx.conf` is the one copied into the production Docker image.
|
|
|
|
### Configuration (both files are the same):
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name localhost;
|
|
|
|
# SPA routing: try files, fallback to index.html
|
|
location / {
|
|
try_files $uri $uri/ /index.html;
|
|
root /usr/share/nginx/html;
|
|
index index.html index.htm;
|
|
}
|
|
|
|
error_page 500 502 503 504 /50x.html;
|
|
location = /50x.html {
|
|
root /usr/share/nginx/html;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Key behavior:**
|
|
- **SPA routing**: `try_files $uri $uri/ /index.html` — any URL that doesn't match a static file falls through to `index.html`, enabling Vue Router to handle client-side navigation
|
|
- **No backend proxy**: Nginx does NOT proxy `/api` requests to the backend. The frontend communicates with the backend API directly via the `VITE_API_URL` environment variable baked into the bundle (e.g., `https://bamort-api.trokan.de`)
|
|
- **Static file serving**: All files served from `/usr/share/nginx/html`
|
|
- **No HTTPS**: The Nginx container handles only HTTP on port 80; TLS termination is expected to happen at an external reverse proxy (e.g., Traefik, HAProxy, or a hosting provider)
|
|
- **No API proxy block**: Unlike many SPA setups, there is no `location /api` proxy directive — the API is on a separate domain/port
|
|
|
|
---
|
|
|
|
## 4. Scripts
|
|
|
|
### 4.1 `docker/start-dev.sh`
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Verifies Docker is running
|
|
# Changes to docker/ directory
|
|
# Exports GIT_COMMIT=$(git rev-parse --short HEAD)
|
|
# Runs: docker-compose -f docker-compose.dev.yml down
|
|
# Runs: docker-compose -f docker-compose.dev.yml up --build -d
|
|
```
|
|
|
|
**What it does:**
|
|
1. Checks `docker info` — exits with error if Docker daemon is not running
|
|
2. `cd`s to the script's own directory (so paths in compose file work)
|
|
3. Exports current short git commit hash as `GIT_COMMIT`
|
|
4. Stops any existing dev containers first (`down`)
|
|
5. Rebuilds images and starts all dev services in detached mode (`up --build -d`)
|
|
|
|
---
|
|
|
|
### 4.2 `docker/stop-dev.sh`
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Changes to docker/ directory
|
|
# Runs: docker-compose -f docker-compose.dev.yml down
|
|
```
|
|
|
|
Stops and removes all dev containers (but preserves volumes and images).
|
|
|
|
---
|
|
|
|
### 4.3 `docker/start-prd.sh`
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Verifies Docker is running
|
|
# Changes to docker/ directory
|
|
# Runs: docker-compose -f docker-compose.yml build (pre-build while old containers run)
|
|
# Runs: docker-compose -f docker-compose.yml down (stop old containers)
|
|
# Runs: docker-compose -f docker-compose.yml up --build -d
|
|
```
|
|
|
|
**Key difference from dev:** Runs `build` before `down` to minimize downtime. Old containers continue running while new images are being built, then a quick switch happens.
|
|
|
|
---
|
|
|
|
### 4.4 `docker/stop-prd.sh`
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Changes to docker/ directory
|
|
# Runs: docker-compose -f docker-compose.yml down
|
|
```
|
|
|
|
Stops and removes production containers. Note: the script message says "Stopping Development Environment" — this is a copy-paste bug in the echo message.
|
|
|
|
---
|
|
|
|
### 4.5 `backend/startserver.sh`
|
|
|
|
This file is **not a standard shell script** — it is actually a `.env`-style configuration file that also contains the final command to start the server locally (outside Docker):
|
|
|
|
```bash
|
|
# Environment variables for BaMoRT development environment
|
|
ENVIRONMENT=development
|
|
DATABASE_TYPE=mysql
|
|
DATABASE_URL="bamort:bG4)efozrc@tcp(192.168.0.36:3306)/bamort?charset=utf8mb4&parseTime=True&loc=Local"
|
|
MARIADB_ROOT_PASSWORD=root_password_dev
|
|
MARIADB_PASSWORD="bG4)efozrc"
|
|
MARIADB_DATABASE=bamort
|
|
MARIADB_USER=bamort
|
|
API_URL="http://localhost:8180"
|
|
VITE_API_URL="http://localhost:8180"
|
|
API_PORT=8180
|
|
BASE_URL="http://localhost:5173"
|
|
TEMPLATES_DIR=./templates
|
|
EXPORT_TEMP_DIR=./export_temp
|
|
GIT_COMMIT=d0c177b
|
|
LOG_LEVEL=debug
|
|
CHROME_BIN="/usr/bin/chromium"
|
|
|
|
echo $DATABASE_URL
|
|
/home/de31a2/.local/bin/go/bin/go run ./cmd/main.go
|
|
```
|
|
|
|
Used as a local dev helper to set env vars and launch the Go backend directly (no Docker). Points to a MariaDB at `192.168.0.36:3306` (a LAN server).
|
|
|
|
> **Security note**: Contains hardcoded credentials (`bG4)efozrc` for MariaDB user) — this is development-only but should be in `.gitignore`.
|
|
|
|
---
|
|
|
|
### 4.6 `backend/transfer_sqlite_to_mariadb.sh`
|
|
|
|
```bash
|
|
BACKEND_URL="http://localhost:8180"
|
|
ENDPOINT="/api/maintenance/transfer-sqlite-to-mariadb"
|
|
```
|
|
|
|
**What it does:**
|
|
1. Optionally accepts argument `clear` to wipe MariaDB data before transfer (prompts for confirmation)
|
|
2. Calls `POST http://localhost:8180/api/maintenance/transfer-sqlite-to-mariadb[?clear=true]` via `curl`
|
|
3. Displays JSON response (formatted via `jq` if available) or raw output
|
|
|
|
**Purpose:** One-time data migration from the SQLite test database (`testdata/prepared_test_data.db`) to the MariaDB development database. The actual transfer logic is implemented in the backend's `/maintenance` module.
|
|
|
|
---
|
|
|
|
### 4.7 `scripts/export-databases.sh`
|
|
|
|
**What it does:**
|
|
1. Loads environment variables from `./docker/.env.dev`
|
|
2. **SQLite export:**
|
|
- Dumps `backend/testdata/prepared_test_data.db` to `backend/testdata/exports/sqlite_dump.sql`
|
|
- Exports each table as a CSV file: `backend/testdata/exports/sqlite_<table>.csv`
|
|
3. **MariaDB export** (requires `bamort-mariadb-dev` container running):
|
|
- Runs `mariadb-dump` inside the container → `backend/testdata/exports/mysql_dump.sql`
|
|
- Exports each table as CSV: `backend/testdata/exports/mysql_<table>.csv`
|
|
4. Lists exported files
|
|
|
|
**Purpose:** Side-by-side export of SQLite (test data) and MariaDB (dev data) for comparison/debugging during data migration. The `TIMESTAMP` variable is intentionally empty (left blank in the script), so files are overwritten on each run.
|
|
|
|
---
|
|
|
|
### 4.8 `scripts/update-version.sh`
|
|
|
|
**Files updated by this script:**
|
|
|
|
| File | What changes |
|
|
|------|--------------|
|
|
| `backend/appsystem/version.go` | `const Version = "X.Y.Z"` |
|
|
| `backend/VERSION.md` | `## Current Version: X.Y.Z` |
|
|
| `frontend/src/version.js` | `export const VERSION = 'X.Y.Z'` |
|
|
| `frontend/package.json` | `"version": "X.Y.Z"` |
|
|
| `frontend/VERSION.md` | `## Current Version: X.Y.Z` |
|
|
|
|
**CLI flags:**
|
|
```
|
|
-b <version> Set backend version explicitly
|
|
-f <version> Set frontend version explicitly
|
|
-n Auto-bump patch version on both (reads current, increments Z in X.Y.Z)
|
|
-c Create a git commit after version update
|
|
-t Create a git tag after commit
|
|
```
|
|
|
|
**Tag strategy:**
|
|
- If both versions are equal: creates single tag `vX.Y.Z`
|
|
- If they differ: creates `backend-vX.Y.Z` and `frontend-vX.Y.Z` separately
|
|
|
|
**Example workflows:**
|
|
```bash
|
|
./scripts/update-version.sh -b 0.1.31 -f 0.2.0 # Set explicit versions
|
|
./scripts/update-version.sh -b 0.1.31 -c -t # Set + commit + tag
|
|
./scripts/update-version.sh -n -c # Auto-bump patch + commit
|
|
./scripts/update-version.sh -c -t # Commit + tag from current files
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Database Initialization
|
|
|
|
### Init directory: `docker/init-db/`
|
|
|
|
| File | Status | Description |
|
|
|------|--------|-------------|
|
|
| `00-init.sql` | **Active** | Full schema + seed data (~3,872 lines) |
|
|
| `01-init.sql.bak` | Backup | Earlier version (user data focus) |
|
|
| `02-init.sql.bak` | Backup | Skill tables variant |
|
|
| `09-init.sql.bak` | Backup | Indexes and auto-increments |
|
|
|
|
### `00-init.sql` — Active Initialization Script
|
|
|
|
Generated by phpMyAdmin from `mariadb-dev:3306` on 2025-12-30.
|
|
|
|
Contains **78 CREATE TABLE + INSERT INTO** statements covering the full database schema.
|
|
|
|
**Tables created (sampled):**
|
|
- `audit_log_entries` — character field change history
|
|
- `char_bennies` — character blessings/bennies
|
|
- `char_characteristics` — physical appearance (eye color, hair, size, etc.)
|
|
- Full game character schema (all character sub-tables)
|
|
|
|
**Settings applied on init:**
|
|
```sql
|
|
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
|
START TRANSACTION;
|
|
SET time_zone = "+00:00";
|
|
SET NAMES utf8mb4;
|
|
```
|
|
|
|
**Character set:** `utf8mb4` with `utf8mb4_unicode_ci` collation throughout.
|
|
|
|
**Execution timing:** Scripts in `init-db/` run only once — when the MariaDB container is created for the first time. If `./bamort-db-dev/` (dev) or `./bamort-db/` (prod) already contain data, init scripts are skipped.
|
|
|
|
---
|
|
|
|
## 6. Network Architecture
|
|
|
|
### Development Network Topology
|
|
|
|
```
|
|
Host Machine
|
|
├── :5173 ──→ bamort-frontend-dev (Vite HMR, Vue SPA)
|
|
├── :8180 ──→ bamort-backend-dev (Go/Gin REST API, Air live-reload)
|
|
├── :3306 ──→ bamort-mariadb-dev (MariaDB 11.4)
|
|
└── :8081 ──→ bamort-phpmyadmin-dev (phpMyAdmin 5.2)
|
|
|
|
Docker Internal Network (docker-compose default bridge)
|
|
├── bamort-frontend-dev → bamort-backend-dev:8180 (via VITE_API_URL on host)
|
|
├── bamort-backend-dev → mariadb-dev:3306 (internal DNS resolution)
|
|
└── bamort-phpmyadmin-dev → mariadb-dev:3306 (internal DNS resolution)
|
|
```
|
|
|
|
**Inter-container communication:**
|
|
- Backend connects to MariaDB using the Docker service name `mariadb-dev` as hostname (resolved via Docker internal DNS)
|
|
- Frontend in dev calls the backend via `VITE_API_URL` which is set to the **host's** IP (e.g., `http://localhost:8180`), not an internal Docker name — meaning the browser makes direct calls to the exposed host port, not through Docker networking
|
|
- phpMyAdmin connects to `mariadb-dev:3306` internally
|
|
|
|
### Production Network Topology
|
|
|
|
```
|
|
Host Machine
|
|
├── :8181 ──→ bamort-frontend (Nginx serving built Vue SPA)
|
|
├── :8182 ──→ bamort-backend (Go/Gin REST API, compiled binary)
|
|
│ bamort-mariadb (NOT exposed — internal only)
|
|
│ [phpmyadmin] (disabled by default)
|
|
|
|
Docker Internal Network
|
|
├── bamort-frontend → bamort-backend [via external VITE_API_URL baked into bundle]
|
|
├── bamort-backend → mariadb:3306 (internal DNS)
|
|
└── bamort-mariadb (no host port binding — not accessible from outside)
|
|
```
|
|
|
|
**External reverse proxy (implicit):** The production setup is designed to sit behind an external reverse proxy (e.g., Nginx, Traefik, or HAProxy) that terminates TLS and routes:
|
|
- `bamort.trokan.de` → container port `8181` (frontend)
|
|
- `bamort-api.trokan.de` → container port `8182` (backend API)
|
|
|
|
### CORS Configuration
|
|
The backend reads `FRONTEND_URL` from config (defaults to `http://localhost:5173`) and uses it for CORS origin allow-listing. Production backend receives `BASE_URL=https://bamort.trokan.de` for this purpose.
|
|
|
|
---
|
|
|
|
## 7. Data Persistence
|
|
|
|
### Volume Mounts
|
|
|
|
| Environment | Service | Host path | Container path | Purpose |
|
|
|-------------|---------|-----------|----------------|---------|
|
|
| Dev | MariaDB | `docker/bamort-db-dev/` | `/var/lib/mysql` | Database files |
|
|
| Dev | Backend | `../backend/` | `/app` | Source code (live-reload) |
|
|
| Dev | Frontend | `../frontend/` | `/app` | Source code (HMR) |
|
|
| Dev | Backend | `go-mod-cache` (named) | `/go/pkg/mod` | Go module cache |
|
|
| Prod | MariaDB | `docker/bamort-db/` | `/var/lib/mysql` | Database files |
|
|
| Prod | Backend | `docker/templates/` | `/app/templates` | PDF templates |
|
|
|
|
### Database Persistence Strategy
|
|
|
|
**Dev:** `docker/bamort-db-dev/` — a host directory mount. Survives container restarts and recreations. Wiped only if you remove the directory manually or run `docker-compose down -v` (though `-v` only removes named volumes, not bind mounts).
|
|
|
|
**Prod:** `docker/bamort-db/` — same strategy. MariaDB port is NOT exposed to the host, so direct external access requires `docker exec`.
|
|
|
|
### SQLite (Test Data)
|
|
|
|
The backend also has a SQLite database at `backend/testdata/prepared_test_data.db` used for automated tests. This is **not** a Docker volume — it's part of the source tree.
|
|
|
|
### Backup Strategy
|
|
|
|
**`scripts/export-databases.sh`:** Manual backup script that:
|
|
1. Dumps SQLite test DB to SQL + per-table CSV files
|
|
2. Dumps MariaDB dev DB to SQL + per-table CSV files
|
|
3. Output to `backend/testdata/exports/`
|
|
|
|
**No automated backup:** There is no cron job or automated backup mechanism in the Docker setup. Backups are manual only. The `docker/bamort_20260123_v0.1.37.sql` and `docker/bamort.sql_backup` files are manual one-off snapshots.
|
|
|
|
---
|
|
|
|
## 8. Development vs Production Differences
|
|
|
|
| Aspect | Development | Production |
|
|
|--------|-------------|------------|
|
|
| **Backend image** | `Dockerfile.backend.dev` — full Go toolchain + Air | `Dockerfile.backend` — multi-stage, Alpine runtime only |
|
|
| **Backend start** | `air -c .air.toml` (live reloads on `.go` file changes) | `./server` (pre-compiled binary) |
|
|
| **Backend source** | Volume-mounted from host (`../backend:/app`) | Compiled into image at build time |
|
|
| **Frontend image** | `Dockerfile.frontend.dev` — Node + Vite dev server | `Dockerfile.frontend` — multi-stage, Nginx serving `dist/` |
|
|
| **Frontend start** | `npm run dev -- --host 0.0.0.0` (Vite HMR) | `nginx -g 'daemon off;'` |
|
|
| **Frontend port** | `5173` | `8181` (maps to container `80`) |
|
|
| **Backend port** | `8180` | `8182` (maps to container `8180`) |
|
|
| **MariaDB port** | `3306` (exposed to host) | NOT exposed externally |
|
|
| **phpMyAdmin** | Enabled on port `8081` | Disabled (commented out) |
|
|
| **VITE_API_URL** | Runtime env var (set in compose) | Build-time ARG (baked into bundle) |
|
|
| **Template source** | Mounted from `../backend` volume (part of source) | Mounted from `docker/templates/` |
|
|
| **DB data dir** | `docker/bamort-db-dev/` | `docker/bamort-db/` |
|
|
| **Go env** | `GO_ENV=development` | `GO_ENV=production` |
|
|
| **MariaDB hostname** | `mariadb-dev` | `mariadb` |
|
|
| **Health check timing** | `start_period: 10s` | `start_period: 20s` |
|
|
| **Image size** | Large (Go toolchain + Chromium) | Smaller (Alpine + Chromium only) |
|
|
| **Hot reload** | Yes (Air for Go, Vite HMR for Vue) | No |
|
|
| **Startup script** | `start-dev.sh` (includes git commit export) | `start-prd.sh` (pre-builds before stopping) |
|
|
|
|
### Notable Configuration Quirks
|
|
|
|
1. **`TEMPLATES_DIR=./templatesx`** in dev compose — has a trailing `x` suffix, which means Air/server falls back to default `./templates`. The actual templates come from the bind-mounted source directory.
|
|
|
|
2. **Production DATABASE_URL** is constructed inline without `${}` fallback syntax, meaning if `MARIADB_USER` or `MARIADB_PASSWORD` are unset, the URL will contain empty strings rather than defaults.
|
|
|
|
3. **Frontend VITE_API_URL defaults** differ between dev (`192.168.0.1:8180`) and prod (`bamort-api.trokan.de`). The dev default is a LAN IP that only works in the author's local network — other developers must set their own `.env.dev`.
|
|
|
|
4. **No Docker network** is explicitly defined — both compose files rely on Docker Compose's default bridge network where all services in the same file can reach each other by service name.
|
|
|
|
5. **`startserver.sh`** is misleadingly named — it is actually a combined `.env` file + startup command for running outside Docker, not a proper shell script header (no `#!/bin/bash`).
|