Merge pull request 'bug fixes' (#6) from develop into main
Reviewed-on: #6
This commit is contained in:
commit
7e7adfab54
39
.env.example
39
.env.example
|
|
@ -1,49 +1,56 @@
|
|||
# === Deploy (.env auf dem Host) ============================================
|
||||
# Kopiere diese Datei nach `.env` im SELBEN Verzeichnis wie `docker-compose.yml` (Pi: ~/docker/shinkan).
|
||||
# Docker Compose liest `.env` beim Start — dadurch wird z. B. SMTP_HOST=${SMTP_HOST} gefüllt.
|
||||
# Ist die Datei weg/leer oder steht SMTP_PASS nicht darin → im Container keine SMTP-Daten ([SMTP] nicht konfiguriert).
|
||||
# === .env neben der Jeweiligen docker-compose.*.yml kopieren ====================
|
||||
# Docker Compose ersetzt ${VARIABLE} beim Start.
|
||||
#
|
||||
# Pro Umgebung eigene Datei (z. B. ~/docker/shinkan/.env für Prod,
|
||||
# ~/docker/shinkan-dev/.env für Dev) — dieselben SCHLÜSSEL, unterschiedliche Werte.
|
||||
# Kein separates DEV_APP_URL vs APP_URL: immer APP_URL, ALLOWED_ORIGINS, DB_*, …
|
||||
|
||||
# Database
|
||||
# ─── Typische Werte PROD (docker-compose.yml) ─────────────────────────────────
|
||||
# DB_NAME=shinkan
|
||||
# DB_USER=shinkan_user
|
||||
# DB_PASSWORD=…
|
||||
# APP_URL=https://shinkan.jinkendo.de
|
||||
# ALLOWED_ORIGINS=https://shinkan.jinkendo.de
|
||||
# ENVIRONMENT=production
|
||||
|
||||
# ─── Typische Werte DEV (docker-compose.dev-env.yml) ─────────────────────────
|
||||
# DB_NAME=shinkan_dev
|
||||
# DB_USER=shinkan_dev
|
||||
# DB_PASSWORD=dev_password
|
||||
# APP_URL=https://dev.shinkan.jinkendo.de
|
||||
# ALLOWED_ORIGINS=https://dev.shinkan.jinkendo.de,http://192.168.2.49:3098
|
||||
# ENVIRONMENT=development
|
||||
|
||||
# ─── Ab hier: eine ausfüllbare Vorlage (bei uns meist Prod-Defaults) ───────────
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_NAME=shinkan
|
||||
DB_USER=shinkan_user
|
||||
DB_PASSWORD=CHANGE_ME_SECURE_PASSWORD
|
||||
|
||||
# OpenRouter (KI - optional)
|
||||
OPENROUTER_API_KEY=your_api_key_here
|
||||
OPENROUTER_MODEL=anthropic/claude-sonnet-4
|
||||
|
||||
# SMTP (E-Mail)
|
||||
SMTP_HOST=smtp.example.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=noreply@jinkendo.de
|
||||
SMTP_PASS=your_smtp_password
|
||||
SMTP_FROM=noreply@jinkendo.de
|
||||
# Port 465 oft SSL; dann z. B. SMTP_SSL=true und SMTP_STARTTLS=false
|
||||
SMTP_SSL=
|
||||
SMTP_STARTTLS=
|
||||
|
||||
# Bootstrap: erste Registrierung (leere Nutzerliste) erhält Rolle admin; oder feste Admin-Mails:
|
||||
AUTO_ADMIN_FIRST_USER=true
|
||||
ADMIN_BOOTSTRAP_EMAILS=
|
||||
|
||||
# App
|
||||
APP_URL=https://shinkan.jinkendo.de
|
||||
# Kommasepariert (ohne Leerzeichen um die Kommas ist am sichersten). Für Dev mehrere Origins nötig (HTTPS + LAN).
|
||||
ALLOWED_ORIGINS=https://shinkan.jinkendo.de
|
||||
ENVIRONMENT=production
|
||||
|
||||
# Nur docker-compose.dev-env.yml (optional): DEV_APP_URL, DEV_ALLOWED_ORIGINS
|
||||
|
||||
# Media Storage
|
||||
MEDIA_DIR=/app/media
|
||||
|
||||
# MediaWiki Import (Semantic MediaWiki)
|
||||
MEDIAWIKI_API_URL=https://karatetrainer.net/api.php
|
||||
MEDIAWIKI_USER=Jinkendo
|
||||
MEDIAWIKI_PASSWORD=CHANGE_ME
|
||||
# Kategorienamen im Wiki (echte Namen von karatetrainer.net)
|
||||
MEDIAWIKI_CATEGORY_EXERCISES=Übungen
|
||||
MEDIAWIKI_CATEGORY_SKILLS=Fähigkeitsbeschreibung
|
||||
MEDIAWIKI_CATEGORY_METHODS=Methodenbeschreibung
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ jobs:
|
|||
playwright-tests:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
# Kein zusätzlicher docker-compose im Job: keine zweiten Container/Host-Ports.
|
||||
# Dev: Tests gegen bereits deployte URL (HTTPS, Reverse-Proxy). Prod: gleicher Ablauf.
|
||||
env:
|
||||
# Öffentliche Dev-Basis — muss ALLOWED_ORIGINS / Nginx entsprechen; bei anderer Domain Workflow anpassen.
|
||||
E2E_TARGET_URL: https://dev.shinkan.jinkendo.de
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
|
@ -66,33 +71,69 @@ jobs:
|
|||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Start dev stack for E2E
|
||||
env:
|
||||
DEV_ALLOWED_ORIGINS: http://127.0.0.1:3098,http://localhost:3098,http://host.docker.internal:3098,https://dev.shinkan.jinkendo.de
|
||||
- name: E2E-Ziel wählen (Dev über Proxy vs. Production)
|
||||
id: e2e
|
||||
run: |
|
||||
docker compose -f docker-compose.dev-env.yml up -d --build
|
||||
echo "Warte auf Frontend + API (/health) …"
|
||||
EVENT="${{ github.event_name }}"
|
||||
WF_NAME="${{ github.event.workflow_run.name }}"
|
||||
DEV_BASE="${{ env.E2E_TARGET_URL }}"
|
||||
if [ "$EVENT" = "workflow_run" ] && [ "$WF_NAME" = "Deploy Production" ]; then
|
||||
echo "mode=prod" >> $GITHUB_OUTPUT
|
||||
echo "base_url=https://shinkan.jinkendo.de" >> $GITHUB_OUTPUT
|
||||
echo "→ Prod. Secrets: E2E_PROD_TEST_EMAIL / E2E_PROD_TEST_PASSWORD."
|
||||
else
|
||||
echo "mode=dev" >> $GITHUB_OUTPUT
|
||||
echo "base_url=${DEV_BASE}" >> $GITHUB_OUTPUT
|
||||
echo "→ Deployte Dev-Umgebung (${DEV_BASE}). Secrets: E2E_DEV_TEST_EMAIL / E2E_DEV_TEST_PASSWORD."
|
||||
fi
|
||||
|
||||
- name: Dev /health abwarten
|
||||
if: ${{ steps.e2e.outputs.mode == 'dev' }}
|
||||
run: |
|
||||
BASE="${{ steps.e2e.outputs.base_url }}"
|
||||
echo "Warte auf $BASE/health …"
|
||||
for i in $(seq 1 90); do
|
||||
if curl -sf http://127.0.0.1:3098/health >/dev/null 2>&1; then
|
||||
if curl -sf "$BASE/health" >/dev/null 2>&1; then
|
||||
echo "Health OK (Versuch $i)"
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo "Timeout: /health nicht erreichbar"
|
||||
curl -v http://127.0.0.1:3098/health || true
|
||||
docker compose -f docker-compose.dev-env.yml logs --tail=120
|
||||
echo "Timeout: Dev /health nicht erreichbar — Deploy / DNS / Firewall prüfen."
|
||||
curl -v "$BASE/health" || true
|
||||
exit 1
|
||||
|
||||
- name: Seed E2E test user (erste Registrierung in frischer DB)
|
||||
env:
|
||||
TEST_EMAIL: lars@stommer.com
|
||||
TEST_PASSWORD: 12345678
|
||||
- name: Prod /health abwarten
|
||||
if: ${{ steps.e2e.outputs.mode == 'prod' }}
|
||||
run: |
|
||||
curl -sf -X POST "http://127.0.0.1:3098/api/auth/register" \
|
||||
BASE="${{ steps.e2e.outputs.base_url }}"
|
||||
echo "Warte auf $BASE/health …"
|
||||
for i in $(seq 1 60); do
|
||||
if curl -sf "$BASE/health" >/dev/null 2>&1; then
|
||||
echo "Health OK (Versuch $i)"
|
||||
exit 0
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
echo "Timeout: Prod /health nicht erreichbar"
|
||||
curl -v "$BASE/health" || true
|
||||
exit 1
|
||||
|
||||
- name: Testnutzer registrieren (Dev, nur wenn möglich)
|
||||
if: ${{ steps.e2e.outputs.mode == 'dev' }}
|
||||
env:
|
||||
E2E_DEV_TEST_EMAIL: ${{ secrets.E2E_DEV_TEST_EMAIL }}
|
||||
E2E_DEV_TEST_PASSWORD: ${{ secrets.E2E_DEV_TEST_PASSWORD }}
|
||||
run: |
|
||||
BASE="${{ steps.e2e.outputs.base_url }}"
|
||||
if [ -z "$E2E_DEV_TEST_EMAIL" ] || [ -z "$E2E_DEV_TEST_PASSWORD" ]; then
|
||||
echo "(Registrierung übersprungen — Secrets E2E_DEV_* nicht gesetzt.)"
|
||||
exit 0
|
||||
fi
|
||||
curl -sf -X POST "$BASE/api/auth/register" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"email\":\"${TEST_EMAIL}\",\"password\":\"${TEST_PASSWORD}\",\"name\":\"Playwright CI\"}" \
|
||||
|| echo "(Registrierung übersprungen — Nutzer existiert evtl. schon)"
|
||||
-d "{\"email\":\"${E2E_DEV_TEST_EMAIL}\",\"password\":\"${E2E_DEV_TEST_PASSWORD}\",\"name\":\"Playwright CI\"}" \
|
||||
|| echo "(Register evtl. schon erfolgt oder Limits — Login-Test gilt trotzdem.)"
|
||||
|
||||
- name: Install Playwright
|
||||
run: |
|
||||
|
|
@ -101,18 +142,34 @@ jobs:
|
|||
|
||||
- name: Run Playwright tests
|
||||
env:
|
||||
PLAYWRIGHT_BASE_URL: http://127.0.0.1:3098
|
||||
TEST_EMAIL: lars@stommer.com
|
||||
TEST_PASSWORD: 12345678
|
||||
E2E_DEV_TEST_EMAIL: ${{ secrets.E2E_DEV_TEST_EMAIL }}
|
||||
E2E_DEV_TEST_PASSWORD: ${{ secrets.E2E_DEV_TEST_PASSWORD }}
|
||||
run: |
|
||||
set -e
|
||||
MODE="${{ steps.e2e.outputs.mode }}"
|
||||
BASE_URL="${{ steps.e2e.outputs.base_url }}"
|
||||
export PLAYWRIGHT_BASE_URL="$BASE_URL"
|
||||
|
||||
if [ "$MODE" = "prod" ]; then
|
||||
export TEST_EMAIL="${{ secrets.E2E_PROD_TEST_EMAIL }}"
|
||||
export TEST_PASSWORD="${{ secrets.E2E_PROD_TEST_PASSWORD }}"
|
||||
if [ -z "$TEST_EMAIL" ] || [ -z "$TEST_PASSWORD" ]; then
|
||||
echo "Fehler: E2E_PROD_TEST_EMAIL und E2E_PROD_TEST_PASSWORD setzen."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
export TEST_EMAIL="$E2E_DEV_TEST_EMAIL"
|
||||
export TEST_PASSWORD="$E2E_DEV_TEST_PASSWORD"
|
||||
if [ -z "$TEST_EMAIL" ] || [ -z "$TEST_PASSWORD" ]; then
|
||||
echo "Fehler: E2E_DEV_TEST_EMAIL und E2E_DEV_TEST_PASSWORD setzen (Playwright soll gegen Dev einloggen)."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p screenshots
|
||||
npx playwright test
|
||||
echo "✓ Playwright tests passed"
|
||||
|
||||
- name: Stop dev stack
|
||||
if: always()
|
||||
run: docker compose -f docker-compose.dev-env.yml down
|
||||
|
||||
- name: Upload test screenshots
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
version: '3.8'
|
||||
|
||||
# Keine festen container_name — Compose-Namen haben Projektprefix (<projekt>-postgres-1).
|
||||
# Gleiche Variablennamen wie docker-compose.yml; andere Werte in einer eigenen .env neben dieser Datei.
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: dev-shinkan-postgres
|
||||
environment:
|
||||
POSTGRES_DB: shinkan_dev
|
||||
POSTGRES_USER: shinkan_dev
|
||||
POSTGRES_PASSWORD: dev_password
|
||||
POSTGRES_DB: "${DB_NAME:-shinkan_dev}"
|
||||
POSTGRES_USER: "${DB_USER:-shinkan_dev}"
|
||||
POSTGRES_PASSWORD: "${DB_PASSWORD:-dev_password}"
|
||||
volumes:
|
||||
- dev-shinkan-db-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
|
|
@ -20,13 +22,12 @@ services:
|
|||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: dev-shinkan-api
|
||||
environment:
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_NAME: shinkan_dev
|
||||
DB_USER: shinkan_dev
|
||||
DB_PASSWORD: dev_password
|
||||
DB_NAME: "${DB_NAME:-shinkan_dev}"
|
||||
DB_USER: "${DB_USER:-shinkan_dev}"
|
||||
DB_PASSWORD: "${DB_PASSWORD:-dev_password}"
|
||||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
|
||||
OPENROUTER_MODEL: ${OPENROUTER_MODEL}
|
||||
SMTP_HOST: ${SMTP_HOST}
|
||||
|
|
@ -34,18 +35,16 @@ services:
|
|||
SMTP_USER: ${SMTP_USER}
|
||||
SMTP_PASS: ${SMTP_PASS}
|
||||
SMTP_FROM: ${SMTP_FROM}
|
||||
# Öffentliche Dev-URL (E-Mail-Links); lokaler Zugriff per IP bleibt über ALLOWED_ORIGINS möglich
|
||||
APP_URL: "${DEV_APP_URL:-https://dev.shinkan.jinkendo.de}"
|
||||
# Login/Register vom Browser: HTTPS-Subdomain und optional LAN-IP (Compose überschreibbar per .env)
|
||||
ALLOWED_ORIGINS: "${DEV_ALLOWED_ORIGINS:-https://dev.shinkan.jinkendo.de,http://192.168.2.49:3098}"
|
||||
ENVIRONMENT: development
|
||||
MEDIAWIKI_API_URL: https://karatetrainer.net/api.php
|
||||
MEDIAWIKI_USER: Jinkendo
|
||||
MEDIAWIKI_PASSWORD: Jinkendo6970
|
||||
MEDIAWIKI_CATEGORY_EXERCISES: Übungen
|
||||
MEDIAWIKI_CATEGORY_SKILLS: Fähigkeitsbeschreibung
|
||||
MEDIAWIKI_CATEGORY_METHODS: Methodenbeschreibung
|
||||
MEDIAWIKI_CATEGORY_MODELS: Reifegradmodelle
|
||||
APP_URL: "${APP_URL:-https://dev.shinkan.jinkendo.de}"
|
||||
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://dev.shinkan.jinkendo.de,http://192.168.2.49:3098}"
|
||||
ENVIRONMENT: "${ENVIRONMENT:-development}"
|
||||
MEDIAWIKI_API_URL: "${MEDIAWIKI_API_URL:-https://karatetrainer.net/api.php}"
|
||||
MEDIAWIKI_USER: "${MEDIAWIKI_USER:-Jinkendo}"
|
||||
MEDIAWIKI_PASSWORD: "${MEDIAWIKI_PASSWORD:-CHANGE_ME}"
|
||||
MEDIAWIKI_CATEGORY_EXERCISES: "${MEDIAWIKI_CATEGORY_EXERCISES:-Übungen}"
|
||||
MEDIAWIKI_CATEGORY_SKILLS: "${MEDIAWIKI_CATEGORY_SKILLS:-Fähigkeitsbeschreibung}"
|
||||
MEDIAWIKI_CATEGORY_METHODS: "${MEDIAWIKI_CATEGORY_METHODS:-Methodenbeschreibung}"
|
||||
MEDIAWIKI_CATEGORY_MODELS: "${MEDIAWIKI_CATEGORY_MODELS:-Reifegradmodelle}"
|
||||
volumes:
|
||||
- dev-shinkan-media:/app/media
|
||||
ports:
|
||||
|
|
@ -63,7 +62,6 @@ services:
|
|||
# Leer = relative /api/*-URLs → gleicher Host wie die SPA (vermeidet Mixed Content HTTPS→HTTP)
|
||||
args:
|
||||
VITE_API_URL: ""
|
||||
container_name: dev-shinkan-ui
|
||||
ports:
|
||||
- "3098:80"
|
||||
depends_on:
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ services:
|
|||
image: postgres:16-alpine
|
||||
container_name: shinkan-db-prod
|
||||
environment:
|
||||
POSTGRES_DB: shinkan
|
||||
POSTGRES_USER: shinkan_user
|
||||
POSTGRES_DB: "${DB_NAME:-shinkan}"
|
||||
POSTGRES_USER: "${DB_USER:-shinkan_user}"
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- shinkan-db-data:/var/lib/postgresql/data
|
||||
|
|
@ -40,9 +40,10 @@ services:
|
|||
# Erste Self-Registration → Admin; oder ADMIN_BOOTSTRAP_EMAILS=mail@…,weitere@…
|
||||
AUTO_ADMIN_FIRST_USER: "${AUTO_ADMIN_FIRST_USER:-true}"
|
||||
ADMIN_BOOTSTRAP_EMAILS: "${ADMIN_BOOTSTRAP_EMAILS:-}"
|
||||
APP_URL: https://shinkan.jinkendo.de
|
||||
ALLOWED_ORIGINS: https://shinkan.jinkendo.de
|
||||
ENVIRONMENT: production
|
||||
# Werte wie in .env (APP_URL, ALLOWED_ORIGINS, ENVIRONMENT) — keine zweite „Wahrheit“ in YAML
|
||||
APP_URL: "${APP_URL:-https://shinkan.jinkendo.de}"
|
||||
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://shinkan.jinkendo.de}"
|
||||
ENVIRONMENT: "${ENVIRONMENT:-production}"
|
||||
volumes:
|
||||
- shinkan-media:/app/media
|
||||
ports:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// CI: PLAYWRIGHT_BASE_URL=http://127.0.0.1:3098 nach docker compose dev-env
|
||||
// Lokal gegen LAN/Dev: export PLAYWRIGHT_BASE_URL=http://192.168.x.x:3098
|
||||
// CI: PLAYWRIGHT_BASE_URL = öffentliche Dev-/Prod-URL (HTTPS über Reverse Proxy), nicht localhost
|
||||
// Lokal: export PLAYWRIGHT_BASE_URL=http://192.168.x.x:3098
|
||||
|
||||
const rawBase =
|
||||
process.env.PLAYWRIGHT_BASE_URL ||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user