ai documentation
This commit is contained in:
File diff suppressed because it is too large
Load Diff
+1362
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,703 @@
|
|||||||
|
# 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`).
|
||||||
Reference in New Issue
Block a user