Infrastruktur-Grundgeruest fuer Kairo Jinkendo: Docker, Gitea Actions, Deployment-Doku (ohne App-Code).
Some checks failed
Test Suite / lint-backend (push) Waiting to run
Test Suite / build-frontend (push) Waiting to run
Test Suite / k6 /health Baseline (push) Waiting to run
Test Suite / playwright-tests (push) Waiting to run
Deploy Development / deploy (push) Failing after 1s
Test Suite / pytest-backend (push) Has been cancelled
Some checks failed
Test Suite / lint-backend (push) Waiting to run
Test Suite / build-frontend (push) Waiting to run
Test Suite / k6 /health Baseline (push) Waiting to run
Test Suite / playwright-tests (push) Waiting to run
Deploy Development / deploy (push) Failing after 1s
Test Suite / pytest-backend (push) Has been cancelled
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
commit
848452f837
70
.env.example
Normal file
70
.env.example
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# === .env neben der jeweiligen docker-compose.*.yml kopieren ====================
|
||||
# Docker Compose ersetzt ${VARIABLE} beim Start.
|
||||
#
|
||||
# Pro Umgebung eigene Datei (z. B. ~/docker/kairo/.env für Prod,
|
||||
# ~/docker/kairo-dev/.env für Dev) — dieselben SCHLÜSSEL, unterschiedliche Werte.
|
||||
# Kein separates DEV_APP_URL vs APP_URL: immer APP_URL, ALLOWED_ORIGINS, DB_*, …
|
||||
|
||||
# ─── Typische Werte PROD (docker-compose.yml) ─────────────────────────────────
|
||||
# DB_NAME=kairo
|
||||
# DB_USER=kairo_user
|
||||
# DB_PASSWORD=…
|
||||
# APP_URL=https://kairo.jinkendo.de
|
||||
# ALLOWED_ORIGINS=https://kairo.jinkendo.de
|
||||
# ENVIRONMENT=production
|
||||
# KAIRO_MEDIA_HOST=/kairo-media
|
||||
# MEDIA_ROOT=/app/media
|
||||
|
||||
# ─── Typische Werte DEV (docker-compose.dev-env.yml) ─────────────────────────
|
||||
# DB_NAME=kairo_dev
|
||||
# DB_USER=kairo_dev
|
||||
# DB_PASSWORD=dev_password
|
||||
# APP_URL=https://dev.kairo.jinkendo.de
|
||||
# ALLOWED_ORIGINS=https://dev.kairo.jinkendo.de,http://192.168.2.49:3097
|
||||
# ENVIRONMENT=development
|
||||
# KAIRO_MEDIA_HOST=/kairo-media/dev
|
||||
# MEDIA_ROOT=/app/media
|
||||
|
||||
# ─── Ab hier: eine ausfüllbare Vorlage (bei uns meist Prod-Defaults) ───────────
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_NAME=kairo
|
||||
DB_USER=kairo_user
|
||||
DB_PASSWORD=CHANGE_ME_SECURE_PASSWORD
|
||||
|
||||
OPENROUTER_API_KEY=your_api_key_here
|
||||
OPENROUTER_MODEL=anthropic/claude-sonnet-4
|
||||
|
||||
# Vereins-Kontingente hart blockieren (KI-Kosten!). Nur 1, true oder yes aktivieren.
|
||||
CLUB_FEATURE_ENFORCE=1
|
||||
|
||||
# KI-Debug (Docker): KAIRO_AI_DEBUG in docker-compose*.yml angebunden — 1 = ausführliche WARN-Logs.
|
||||
# KAIRO_AI_DEBUG=1
|
||||
|
||||
SMTP_HOST=smtp.example.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=noreply@jinkendo.de
|
||||
SMTP_PASS=your_smtp_password
|
||||
SMTP_FROM=noreply@jinkendo.de
|
||||
SMTP_SSL=
|
||||
SMTP_STARTTLS=
|
||||
|
||||
AUTO_ADMIN_FIRST_USER=true
|
||||
ADMIN_BOOTSTRAP_EMAILS=
|
||||
|
||||
APP_URL=https://kairo.jinkendo.de
|
||||
ALLOWED_ORIGINS=https://kairo.jinkendo.de
|
||||
ENVIRONMENT=production
|
||||
|
||||
# Medien (Docker Compose): KAIRO_MEDIA_HOST = Verzeichnis auf dem Host (Bind-Mount),
|
||||
# MEDIA_ROOT = gleicher Pfad im Container (muss mit dem Mount-Ziel übereinstimmen).
|
||||
KAIRO_MEDIA_HOST=/kairo-media
|
||||
MEDIA_ROOT=/app/media
|
||||
|
||||
MEDIAWIKI_API_URL=https://karatetrainer.net/api.php
|
||||
MEDIAWIKI_USER=Jinkendo
|
||||
MEDIAWIKI_PASSWORD=CHANGE_ME
|
||||
MEDIAWIKI_CATEGORY_EXERCISES=Übungen
|
||||
MEDIAWIKI_CATEGORY_SKILLS=Fähigkeitsbeschreibung
|
||||
MEDIAWIKI_CATEGORY_METHODS=Methodenbeschreibung
|
||||
MEDIAWIKI_CATEGORY_MODELS=Reifegradmodelle
|
||||
28
.gitea/workflows/deploy-dev.yml
Normal file
28
.gitea/workflows/deploy-dev.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
name: Deploy Development
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [develop]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Deploy to Development
|
||||
run: |
|
||||
set -e
|
||||
echo "=== Deploying Kairo to DEVELOPMENT ==="
|
||||
cd /home/lars/docker/kairo-dev
|
||||
git fetch origin develop || git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
|
||||
git reset --hard origin/develop
|
||||
docker compose -f docker-compose.dev-env.yml build --no-cache
|
||||
docker compose -f docker-compose.dev-env.yml up -d
|
||||
sleep 5
|
||||
if ! curl -sf http://localhost:8097/api/version; then
|
||||
echo "✗ DEV API nicht erreichbar — Backend-Logs (Migration/Startup):"
|
||||
docker compose -f docker-compose.dev-env.yml logs backend --tail 120 || true
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ DEV API healthy"
|
||||
curl -sf http://localhost:3097/api/version && echo "✓ DEV über Frontend-Nginx (wie Browser) healthy"
|
||||
echo "=== Kairo DEV Deploy complete ==="
|
||||
23
.gitea/workflows/deploy-prod.yml
Normal file
23
.gitea/workflows/deploy-prod.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
name: Deploy Production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Deploy to Production
|
||||
run: |
|
||||
set -e
|
||||
echo "=== Deploying Kairo to PRODUCTION ==="
|
||||
cd /home/lars/docker/kairo
|
||||
git fetch origin main || git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
|
||||
git reset --hard origin/main
|
||||
docker compose build --no-cache
|
||||
docker compose up -d
|
||||
sleep 5
|
||||
curl -sf http://localhost:8004/api/version && echo "✓ PROD API (direkt) healthy"
|
||||
curl -sf http://localhost:3004/api/version && echo "✓ PROD API über Frontend-Nginx (wie Browser) healthy"
|
||||
echo "=== Kairo PROD Deploy complete ==="
|
||||
312
.gitea/workflows/test.yml
Normal file
312
.gitea/workflows/test.yml
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
name: Test Suite
|
||||
|
||||
# develop: push/PR → Tests gegen Dev (parallel oder vor Deploy Development).
|
||||
# main: kein push/PR-Trigger — vermeidet doppelten Dev-Lauf beim Merge develop→main;
|
||||
# Prod-Tests nur via workflow_run nach erfolgreichem Deploy Production.
|
||||
on:
|
||||
push:
|
||||
branches: [develop]
|
||||
pull_request:
|
||||
branches: [develop]
|
||||
workflow_run:
|
||||
workflows: ["Deploy Development", "Deploy Production"]
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
pytest-backend:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Backend pytest im deployten Container
|
||||
run: |
|
||||
set -e
|
||||
EVENT_NAME="${{ github.event_name }}"
|
||||
REF_NAME="${{ github.ref_name }}"
|
||||
BASE_REF="${{ github.base_ref }}"
|
||||
RUN_WORKFLOW="${{ github.event.workflow_run.name }}"
|
||||
APP_DIR="/home/lars/docker/kairo"
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
|
||||
if [ "$EVENT_NAME" = "workflow_run" ]; then
|
||||
if [ "$RUN_WORKFLOW" = "Deploy Development" ]; then
|
||||
APP_DIR="/home/lars/docker/kairo-dev"
|
||||
COMPOSE_FILE="docker-compose.dev-env.yml"
|
||||
fi
|
||||
elif [ "$REF_NAME" = "develop" ] || [ "$BASE_REF" = "develop" ]; then
|
||||
APP_DIR="/home/lars/docker/kairo-dev"
|
||||
COMPOSE_FILE="docker-compose.dev-env.yml"
|
||||
fi
|
||||
|
||||
cd "$APP_DIR"
|
||||
echo "Warte auf stabilen backend-Container …"
|
||||
for i in $(seq 1 60); do
|
||||
if docker compose -f "$COMPOSE_FILE" exec -T backend true 2>/dev/null; then
|
||||
echo "Backend bereit (Versuch $i)"
|
||||
break
|
||||
fi
|
||||
if [ "$i" -eq 60 ]; then
|
||||
echo "Timeout: backend-Container nicht bereit"
|
||||
docker compose -f "$COMPOSE_FILE" ps || true
|
||||
docker compose -f "$COMPOSE_FILE" logs backend --tail 80 || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
docker compose -f "$COMPOSE_FILE" exec -T backend sh -lc "
|
||||
pip install -r /app/requirements-dev.txt &&
|
||||
cd /app &&
|
||||
ACCESS_LAYER_STRICT=1 python scripts/check_access_layer_hints.py &&
|
||||
python scripts/security_release_checks.py &&
|
||||
ACCESS_LAYER_INTEGRATION=1 SKIP_DB_MIGRATE=1 python -m pytest tests -m 'not slow' -ra -vv --tb=short
|
||||
"
|
||||
|
||||
lint-backend:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check backend syntax
|
||||
run: |
|
||||
EVENT_NAME="${{ github.event_name }}"
|
||||
REF_NAME="${{ github.ref_name }}"
|
||||
RUN_WORKFLOW="${{ github.event.workflow_run.name }}"
|
||||
APP_DIR="/home/lars/docker/kairo"
|
||||
|
||||
if [ "$EVENT_NAME" = "workflow_run" ]; then
|
||||
if [ "$RUN_WORKFLOW" = "Deploy Development" ]; then
|
||||
APP_DIR="/home/lars/docker/kairo-dev"
|
||||
fi
|
||||
elif [ "$REF_NAME" = "develop" ]; then
|
||||
APP_DIR="/home/lars/docker/kairo-dev"
|
||||
fi
|
||||
|
||||
python3 -m py_compile "$APP_DIR/backend/main.py"
|
||||
echo "✓ Backend syntax OK"
|
||||
|
||||
build-frontend:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build frontend
|
||||
run: |
|
||||
EVENT_NAME="${{ github.event_name }}"
|
||||
REF_NAME="${{ github.ref_name }}"
|
||||
RUN_WORKFLOW="${{ github.event.workflow_run.name }}"
|
||||
APP_DIR="/home/lars/docker/kairo"
|
||||
|
||||
if [ "$EVENT_NAME" = "workflow_run" ]; then
|
||||
if [ "$RUN_WORKFLOW" = "Deploy Development" ]; then
|
||||
APP_DIR="/home/lars/docker/kairo-dev"
|
||||
fi
|
||||
elif [ "$REF_NAME" = "develop" ]; then
|
||||
APP_DIR="/home/lars/docker/kairo-dev"
|
||||
fi
|
||||
|
||||
cd "$APP_DIR/frontend"
|
||||
npm install
|
||||
npm run build
|
||||
echo "✓ Frontend build OK"
|
||||
|
||||
k6-health-baseline:
|
||||
name: k6 /health Baseline
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
E2E_TARGET_URL: https://dev.kairo.jinkendo.de
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: E2E-Ziel wählen (Dev über Proxy vs. Production)
|
||||
id: e2e
|
||||
run: |
|
||||
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://kairo.jinkendo.de" >> $GITHUB_OUTPUT
|
||||
echo "→ k6 gegen Prod-Basis."
|
||||
else
|
||||
echo "mode=dev" >> $GITHUB_OUTPUT
|
||||
echo "base_url=${DEV_BASE}" >> $GITHUB_OUTPUT
|
||||
echo "→ k6 gegen Dev (${DEV_BASE})."
|
||||
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 "$BASE/health" >/dev/null 2>&1; then
|
||||
echo "Health OK (Versuch $i)"
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo "Timeout: Dev /health nicht erreichbar — Deploy / DNS / Firewall prüfen."
|
||||
curl -v "$BASE/health" || true
|
||||
exit 1
|
||||
|
||||
- name: Prod /health abwarten
|
||||
if: ${{ steps.e2e.outputs.mode == 'prod' }}
|
||||
run: |
|
||||
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: Install k6
|
||||
run: |
|
||||
set -e
|
||||
K6_VER="v0.55.0"
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
x86_64) K6_ARCH=amd64 ;;
|
||||
aarch64|arm64) K6_ARCH=arm64 ;;
|
||||
*) echo "k6: unbekannte Architektur: $ARCH"; exit 1 ;;
|
||||
esac
|
||||
echo "Installing k6 ${K6_VER} linux-${K6_ARCH}"
|
||||
curl -sSL "https://github.com/grafana/k6/releases/download/${K6_VER}/k6-${K6_VER}-linux-${K6_ARCH}.tar.gz" -o /tmp/k6.tgz
|
||||
tar -xzf /tmp/k6.tgz -C /tmp
|
||||
sudo mv "/tmp/k6-${K6_VER}-linux-${K6_ARCH}/k6" /usr/local/bin/k6
|
||||
k6 version
|
||||
|
||||
- name: k6 Health-Baseline (parallele /health)
|
||||
env:
|
||||
BASE_URL: ${{ steps.e2e.outputs.base_url }}
|
||||
run: |
|
||||
set -e
|
||||
echo "k6 gegen BASE_URL=$BASE_URL"
|
||||
k6 run scripts/load/k6-health-baseline.js
|
||||
echo "✓ k6 Health-Baseline passed"
|
||||
|
||||
playwright-tests:
|
||||
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
E2E_TARGET_URL: https://dev.kairo.jinkendo.de
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: E2E-Ziel wählen (Dev über Proxy vs. Production)
|
||||
id: e2e
|
||||
run: |
|
||||
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://kairo.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 "$BASE/health" >/dev/null 2>&1; then
|
||||
echo "Health OK (Versuch $i)"
|
||||
exit 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
echo "Timeout: Dev /health nicht erreichbar — Deploy / DNS / Firewall prüfen."
|
||||
curl -v "$BASE/health" || true
|
||||
exit 1
|
||||
|
||||
- name: Prod /health abwarten
|
||||
if: ${{ steps.e2e.outputs.mode == 'prod' }}
|
||||
run: |
|
||||
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\":\"${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: |
|
||||
npm ci || npm install
|
||||
npx playwright install --with-deps chromium
|
||||
|
||||
- name: Run Playwright tests
|
||||
env:
|
||||
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: Upload test screenshots
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: playwright-screenshots
|
||||
path: screenshots/
|
||||
retention-days: 7
|
||||
76
.gitignore
vendored
Normal file
76
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
.env.production
|
||||
|
||||
# Dependencies
|
||||
node_modules/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
.Python
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
# Build output
|
||||
frontend/dist/
|
||||
|
||||
# Data (NEVER commit database or user data)
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
data/
|
||||
photos/
|
||||
uploads/
|
||||
media/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# IDE
|
||||
.vscode/settings.json
|
||||
.idea/
|
||||
*.swp
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Docker overrides
|
||||
docker-compose.override.yml
|
||||
|
||||
# SSL certificates (never commit)
|
||||
nginx/ssl/
|
||||
nginx/certbot/
|
||||
*.pem
|
||||
*.key
|
||||
*.crt
|
||||
*.csr
|
||||
|
||||
# Pytest
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
coverage/
|
||||
|
||||
# Temp
|
||||
tmp/
|
||||
*.tmp
|
||||
|
||||
# Claude: nur ausgewählte Bereiche versionieren
|
||||
.claude/**
|
||||
!.claude/README.md
|
||||
!.claude/docs/
|
||||
!.claude/docs/**/*
|
||||
!.claude/rules/
|
||||
!.claude/rules/**/*
|
||||
!.claude/commands/
|
||||
!.claude/commands/**/*
|
||||
.claude/settings.local.json
|
||||
|
||||
# Cursor MCP
|
||||
.cursor/mcp.json
|
||||
frontend/package-lock.json
|
||||
59
CLAUDE.md
Normal file
59
CLAUDE.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Kairo Jinkendo – Entwickler-Kontext
|
||||
|
||||
## Projekt-Übersicht
|
||||
|
||||
**Kairo Jinkendo** (回廊 Jinkendo) — Schwesterprodukt in der **Jinkendo**-App-Familie (人拳道).
|
||||
Domains: kairo.jinkendo.de · dev.kairo.jinkendo.de
|
||||
|
||||
**Status:** Infrastruktur (Docker, Gitea Actions, Deployment-Doku) ist eingerichtet. Anwendungscode folgt.
|
||||
|
||||
## Tech-Stack (geplant)
|
||||
|
||||
| Komponente | Technologie |
|
||||
|-----------|-------------|
|
||||
| Frontend | React 18 + Vite + PWA (Node 20) |
|
||||
| Backend | FastAPI Python 3.12 |
|
||||
| Datenbank | PostgreSQL 16 Alpine |
|
||||
| Container | Docker + Docker Compose |
|
||||
| Auth | Token-basiert + bcrypt (Familien-Standard) |
|
||||
|
||||
**Ports:** Prod 3004/8004 · Dev 3097/8097 — nicht ändern ohne explizite Freigabe (Reverse Proxy/Fritz!Box).
|
||||
|
||||
## Deployment
|
||||
|
||||
```
|
||||
Internet → Fritz!Box (privat.stommer.com) → Synology NAS → Raspberry Pi 5 (192.168.2.49)
|
||||
|
||||
Git Workflow:
|
||||
develop → Auto-Deploy → dev.kairo.jinkendo.de (kairo-dev/, Port 3097/8097)
|
||||
main → Auto-Deploy → kairo.jinkendo.de (kairo/, Port 3004/8004)
|
||||
|
||||
Gitea: http://192.168.2.144:3000/Lars/Kairo-Jinkendo
|
||||
Runner: Raspberry Pi (/home/lars/gitea-runner/) — gemeinsam mit Shinkan/Mitai
|
||||
|
||||
Manuell:
|
||||
cd /home/lars/docker/kairo[-dev]
|
||||
docker compose -f docker-compose[.dev-env].yml build --no-cache && up -d
|
||||
```
|
||||
|
||||
## Verzeichnisstruktur (Zielbild)
|
||||
|
||||
```
|
||||
backend/ # FastAPI — noch anzulegen
|
||||
frontend/ # React + Vite — noch anzulegen
|
||||
.gitea/workflows/ # CI/CD (deploy + test)
|
||||
docs/ # Deployment-Doku
|
||||
scripts/load/ # k6 Health-Baseline
|
||||
```
|
||||
|
||||
## Referenz
|
||||
|
||||
Deployment-Muster und Familien-Standards: Schwesterprojekt **shinkan-jinkendo** (`c:\Dev\shinkan-jinkendo`).
|
||||
|
||||
## Jinkendo-Familie
|
||||
|
||||
```
|
||||
mitai.jinkendo.de → Körper-Tracker (身体)
|
||||
shinkan.jinkendo.de → Trainingsplanung (真観)
|
||||
kairo.jinkendo.de → (回廊 — Produktdefinition folgt)
|
||||
```
|
||||
63
README.md
Normal file
63
README.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Kairo Jinkendo (回廊)
|
||||
|
||||
**Produktfamilie Jinkendo (人拳道) — Schwesterprodukt zu Shinkan und Mitai**
|
||||
|
||||
> Infrastruktur-Setup: Deployment, Docker und Gitea Actions sind vorbereitet.
|
||||
> Anwendungscode (Backend/Frontend) folgt in einem separaten Schritt.
|
||||
|
||||
## Deployment-Übersicht
|
||||
|
||||
| Umgebung | Branch | Server-Pfad | Host-Ports (UI/API) | Domain |
|
||||
|----------|--------|-------------|---------------------|--------|
|
||||
| **Development** | `develop` | `/home/lars/docker/kairo-dev` | 3097 / 8097 | https://dev.kairo.jinkendo.de |
|
||||
| **Production** | `main` | `/home/lars/docker/kairo` | 3004 / 8004 | https://kairo.jinkendo.de |
|
||||
|
||||
Auto-Deploy via Gitea Actions auf dem Raspberry Pi (gleicher Runner wie Shinkan/Mitai).
|
||||
|
||||
**Gitea:** http://192.168.2.144:3000/Lars/Kairo-Jinkendo
|
||||
|
||||
## Tech-Stack (geplant)
|
||||
|
||||
- **Frontend:** React 18 + Vite + PWA
|
||||
- **Backend:** FastAPI (Python 3.12)
|
||||
- **Datenbank:** PostgreSQL 16
|
||||
- **Container:** Docker + Docker Compose
|
||||
|
||||
## Lokales Setup (wenn App-Code vorhanden)
|
||||
|
||||
```bash
|
||||
git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git
|
||||
cd Kairo-Jinkendo
|
||||
cp .env.example .env
|
||||
# .env anpassen
|
||||
|
||||
# Development
|
||||
docker compose -f docker-compose.dev-env.yml up --build
|
||||
|
||||
# Production (lokal)
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Frontend (Dev): http://localhost:3097
|
||||
Backend (Dev): http://localhost:8097
|
||||
|
||||
## Server-Einrichtung
|
||||
|
||||
Siehe [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) für Verzeichnisse, `.env`-Dateien und Reverse-Proxy-Hinweise auf dem Pi.
|
||||
|
||||
## Git-Workflow
|
||||
|
||||
```
|
||||
develop → Auto-Deploy → kairo-dev (Dev)
|
||||
main → Auto-Deploy → kairo (Prod)
|
||||
```
|
||||
|
||||
## Dokumentation
|
||||
|
||||
- **Deployment:** [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)
|
||||
- **Entwickler-Kontext:** [CLAUDE.md](CLAUDE.md)
|
||||
- **Schwesterprojekt (Referenz):** [shinkan-jinkendo](http://192.168.2.144:3000/Lars/shinkan-jinkendo)
|
||||
|
||||
## Lizenz
|
||||
|
||||
Proprietary – Lars Stommer
|
||||
14
backend/README.md
Normal file
14
backend/README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Backend
|
||||
|
||||
FastAPI-Anwendung — noch anzulegen.
|
||||
|
||||
Erwartete Struktur (analog shinkan-jinkendo):
|
||||
|
||||
```
|
||||
backend/
|
||||
├── Dockerfile
|
||||
├── main.py
|
||||
├── requirements.txt
|
||||
├── migrations/
|
||||
└── routers/
|
||||
```
|
||||
79
docker-compose.dev-env.yml
Normal file
79
docker-compose.dev-env.yml
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Keine festen container_name — Compose-Namen haben Projektprefix (<projekt>-postgres-1).
|
||||
# Medien: In .env KAIRO_MEDIA_HOST (Host-Pfad) und optional MEDIA_ROOT (Container-Pfad) setzen.
|
||||
# Default Host /kairo-media/dev — Verzeichnis ggf. anlegen oder Compose legt es an.
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_DB: "${DB_NAME:-kairo_dev}"
|
||||
POSTGRES_USER: "${DB_USER:-kairo_dev}"
|
||||
POSTGRES_PASSWORD: "${DB_PASSWORD:-dev_password}"
|
||||
volumes:
|
||||
- dev-kairo-db-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5437:5432"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- dev-kairo-network
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
environment:
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_NAME: "${DB_NAME:-kairo_dev}"
|
||||
DB_USER: "${DB_USER:-kairo_dev}"
|
||||
DB_PASSWORD: "${DB_PASSWORD:-dev_password}"
|
||||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
|
||||
OPENROUTER_MODEL: ${OPENROUTER_MODEL}
|
||||
KAIRO_AI_DEBUG: "${KAIRO_AI_DEBUG:-}"
|
||||
SMTP_HOST: ${SMTP_HOST}
|
||||
SMTP_PORT: ${SMTP_PORT}
|
||||
SMTP_USER: ${SMTP_USER}
|
||||
SMTP_PASS: ${SMTP_PASS}
|
||||
SMTP_FROM: ${SMTP_FROM}
|
||||
APP_URL: "${APP_URL:-https://dev.kairo.jinkendo.de}"
|
||||
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://dev.kairo.jinkendo.de,http://192.168.2.49:3097}"
|
||||
ENVIRONMENT: "${ENVIRONMENT:-development}"
|
||||
CLUB_FEATURE_ENFORCE: "${CLUB_FEATURE_ENFORCE:-1}"
|
||||
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}"
|
||||
MEDIA_ROOT: "${MEDIA_ROOT:-/app/media}"
|
||||
volumes:
|
||||
- ${KAIRO_MEDIA_HOST:-/kairo-media/dev}:${MEDIA_ROOT:-/app/media}
|
||||
ports:
|
||||
- "8097:8000"
|
||||
depends_on:
|
||||
- postgres
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- dev-kairo-network
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
VITE_API_URL: ""
|
||||
ports:
|
||||
- "3097:80"
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- dev-kairo-network
|
||||
|
||||
volumes:
|
||||
dev-kairo-db-data:
|
||||
|
||||
networks:
|
||||
dev-kairo-network:
|
||||
driver: bridge
|
||||
82
docker-compose.yml
Normal file
82
docker-compose.yml
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: kairo-db-prod
|
||||
environment:
|
||||
POSTGRES_DB: "${DB_NAME:-kairo}"
|
||||
POSTGRES_USER: "${DB_USER:-kairo_user}"
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- kairo-db-data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "127.0.0.1:5436:5432"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- kairo-network
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: kairo-api
|
||||
environment:
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_NAME: kairo
|
||||
DB_USER: kairo_user
|
||||
DB_PASSWORD: ${DB_PASSWORD}
|
||||
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
|
||||
OPENROUTER_MODEL: ${OPENROUTER_MODEL}
|
||||
KAIRO_AI_DEBUG: "${KAIRO_AI_DEBUG:-}"
|
||||
SMTP_HOST: ${SMTP_HOST}
|
||||
SMTP_PORT: ${SMTP_PORT}
|
||||
SMTP_USER: ${SMTP_USER}
|
||||
SMTP_PASS: ${SMTP_PASS}
|
||||
SMTP_FROM: ${SMTP_FROM}
|
||||
SMTP_SSL: ${SMTP_SSL:-}
|
||||
SMTP_STARTTLS: ${SMTP_STARTTLS:-}
|
||||
AUTO_ADMIN_FIRST_USER: "${AUTO_ADMIN_FIRST_USER:-true}"
|
||||
ADMIN_BOOTSTRAP_EMAILS: "${ADMIN_BOOTSTRAP_EMAILS:-}"
|
||||
APP_URL: "${APP_URL:-https://kairo.jinkendo.de}"
|
||||
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://kairo.jinkendo.de}"
|
||||
ENVIRONMENT: "${ENVIRONMENT:-production}"
|
||||
CLUB_FEATURE_ENFORCE: "${CLUB_FEATURE_ENFORCE:-1}"
|
||||
MEDIAWIKI_API_URL: "${MEDIAWIKI_API_URL:-https://karatetrainer.net/api.php}"
|
||||
MEDIAWIKI_USER: "${MEDIAWIKI_USER:-}"
|
||||
MEDIAWIKI_PASSWORD: "${MEDIAWIKI_PASSWORD:-}"
|
||||
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}"
|
||||
MEDIA_ROOT: "${MEDIA_ROOT:-/app/media}"
|
||||
volumes:
|
||||
- ${KAIRO_MEDIA_HOST:-/kairo-media}:${MEDIA_ROOT:-/app/media}
|
||||
ports:
|
||||
- "8004:8000"
|
||||
depends_on:
|
||||
- postgres
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- kairo-network
|
||||
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
VITE_API_URL: ""
|
||||
container_name: kairo-ui
|
||||
ports:
|
||||
- "3004:80"
|
||||
depends_on:
|
||||
- backend
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- kairo-network
|
||||
|
||||
volumes:
|
||||
kairo-db-data:
|
||||
|
||||
networks:
|
||||
kairo-network:
|
||||
driver: bridge
|
||||
127
docs/DEPLOYMENT.md
Normal file
127
docs/DEPLOYMENT.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Deployment – Kairo Jinkendo
|
||||
|
||||
**Stand:** 2026-07-04
|
||||
**Server:** Raspberry Pi 5 (`192.168.2.49`) — gleicher Host wie Shinkan/Mitai
|
||||
**Runner:** Gitea Actions (`/home/lars/gitea-runner/`)
|
||||
|
||||
---
|
||||
|
||||
## Port- und Pfad-Übersicht
|
||||
|
||||
| | Production | Development |
|
||||
|---|------------|-------------|
|
||||
| **Git-Branch** | `main` | `develop` |
|
||||
| **Server-Verzeichnis** | `/home/lars/docker/kairo` | `/home/lars/docker/kairo-dev` |
|
||||
| **Frontend-Port** | 3004 | 3097 |
|
||||
| **Backend-Port** | 8004 | 8097 |
|
||||
| **PostgreSQL (localhost)** | 5436 | 5437 |
|
||||
| **Domain** | kairo.jinkendo.de | dev.kairo.jinkendo.de |
|
||||
| **Medien-Host-Pfad** | `/kairo-media` | `/kairo-media/dev` |
|
||||
|
||||
**Familien-Referenz (bereits belegt):**
|
||||
|
||||
| App | Prod UI/API | Dev UI/API |
|
||||
|-----|-------------|------------|
|
||||
| Mitai | 3002 / 8002 | 3099 / 8099 |
|
||||
| Shinkan | 3003 / 8003 | 3098 / 8098 |
|
||||
| **Kairo** | **3004 / 8004** | **3097 / 8097** |
|
||||
|
||||
---
|
||||
|
||||
## Einmalige Server-Einrichtung
|
||||
|
||||
Auf dem Pi als User `lars` ausführen:
|
||||
|
||||
```bash
|
||||
# Verzeichnisse anlegen
|
||||
mkdir -p /home/lars/docker/kairo
|
||||
mkdir -p /home/lars/docker/kairo-dev
|
||||
mkdir -p /kairo-media /kairo-media/dev
|
||||
|
||||
# Production
|
||||
cd /home/lars/docker/kairo
|
||||
git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
|
||||
git checkout main
|
||||
cp .env.example .env
|
||||
# .env bearbeiten (DB_PASSWORD, SMTP, OPENROUTER, …)
|
||||
|
||||
# Development
|
||||
cd /home/lars/docker/kairo-dev
|
||||
git clone http://192.168.2.144:3000/Lars/Kairo-Jinkendo.git .
|
||||
git checkout develop
|
||||
cp .env.example .env
|
||||
# .env mit Dev-Werten bearbeiten (siehe Kommentare in .env.example)
|
||||
```
|
||||
|
||||
> **Hinweis:** Erstes `docker compose up` funktioniert erst, wenn `backend/` und `frontend/` mit Dockerfiles vorhanden sind.
|
||||
|
||||
---
|
||||
|
||||
## Reverse Proxy (Synology / Fritz!Box)
|
||||
|
||||
Analog zu Shinkan — neue Hostnamen im Proxy eintragen:
|
||||
|
||||
| Hostname | Ziel (Pi) |
|
||||
|----------|-----------|
|
||||
| `kairo.jinkendo.de` | `http://192.168.2.49:3004` |
|
||||
| `dev.kairo.jinkendo.de` | `http://192.168.2.49:3097` |
|
||||
|
||||
SSL/TLS wie bei den Schwester-Apps (Let's Encrypt über Synology).
|
||||
|
||||
---
|
||||
|
||||
## Gitea Actions
|
||||
|
||||
Workflows unter `.gitea/workflows/`:
|
||||
|
||||
| Workflow | Trigger | Aktion |
|
||||
|----------|---------|--------|
|
||||
| `deploy-dev.yml` | Push auf `develop` | Build + Deploy nach `kairo-dev/` |
|
||||
| `deploy-prod.yml` | Push auf `main` | Build + Deploy nach `kairo/` |
|
||||
| `test.yml` | PR/Push `develop`, nach Deploy | pytest, Lint, Frontend-Build, k6, Playwright |
|
||||
|
||||
Health-Checks nach Deploy:
|
||||
|
||||
- Dev: `curl http://localhost:8097/api/version` und `http://localhost:3097/api/version`
|
||||
- Prod: `curl http://localhost:8004/api/version` und `http://localhost:3004/api/version`
|
||||
|
||||
---
|
||||
|
||||
## Gitea Secrets (für E2E-Tests)
|
||||
|
||||
In Gitea unter Repository → Settings → Actions → Secrets (analog Shinkan):
|
||||
|
||||
| Secret | Verwendung |
|
||||
|--------|------------|
|
||||
| `E2E_DEV_TEST_EMAIL` | Playwright Dev-Login |
|
||||
| `E2E_DEV_TEST_PASSWORD` | Playwright Dev-Login |
|
||||
| `E2E_PROD_TEST_EMAIL` | Playwright Prod-Login |
|
||||
| `E2E_PROD_TEST_PASSWORD` | Playwright Prod-Login |
|
||||
|
||||
---
|
||||
|
||||
## Manuelles Deploy
|
||||
|
||||
```bash
|
||||
# Development
|
||||
cd /home/lars/docker/kairo-dev
|
||||
git fetch origin develop && git reset --hard origin/develop
|
||||
docker compose -f docker-compose.dev-env.yml build --no-cache
|
||||
docker compose -f docker-compose.dev-env.yml up -d
|
||||
|
||||
# Production
|
||||
cd /home/lars/docker/kairo
|
||||
git fetch origin main && git reset --hard origin/main
|
||||
docker compose build --no-cache
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nächster Schritt (App-Code)
|
||||
|
||||
1. `backend/` mit FastAPI-Skeleton (main.py, Dockerfile, migrations/)
|
||||
2. `frontend/` mit React/Vite-Skeleton (Dockerfile, nginx.conf)
|
||||
3. Erster Push auf `develop` → Auto-Deploy Dev
|
||||
4. Reverse-Proxy-Einträge für Domains aktivieren
|
||||
5. Gitea E2E-Secrets setzen
|
||||
13
frontend/README.md
Normal file
13
frontend/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Frontend
|
||||
|
||||
React + Vite SPA — noch anzulegen.
|
||||
|
||||
Erwartete Struktur (analog shinkan-jinkendo):
|
||||
|
||||
```
|
||||
frontend/
|
||||
├── Dockerfile
|
||||
├── nginx.conf
|
||||
├── package.json
|
||||
└── src/
|
||||
```
|
||||
11
package.json
Normal file
11
package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "kairo-jinkendo",
|
||||
"private": true,
|
||||
"description": "Workspace-Metadaten für E2E (Playwright). Frontend: frontend/",
|
||||
"scripts": {
|
||||
"test:e2e": "playwright test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.49.0"
|
||||
}
|
||||
}
|
||||
19
playwright.config.js
Normal file
19
playwright.config.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// CI: PLAYWRIGHT_BASE_URL = öffentliche Dev-/Prod-URL (HTTPS über Reverse Proxy), nicht localhost
|
||||
// Lokal: export PLAYWRIGHT_BASE_URL=http://192.168.x.x:3097
|
||||
|
||||
const rawBase =
|
||||
process.env.PLAYWRIGHT_BASE_URL ||
|
||||
process.env.BASE_URL ||
|
||||
'http://127.0.0.1:3097';
|
||||
|
||||
module.exports = {
|
||||
testDir: './tests',
|
||||
timeout: 30000,
|
||||
use: {
|
||||
headless: true,
|
||||
viewport: { width: 390, height: 844 },
|
||||
screenshot: 'only-on-failure',
|
||||
baseURL: rawBase.replace(/\/$/, ''),
|
||||
},
|
||||
reporter: 'list',
|
||||
};
|
||||
16
scripts/load/README.md
Normal file
16
scripts/load/README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# k6 Load-Tests (Phase 0)
|
||||
|
||||
Parallele `/health`-Requests gegen Dev oder Prod.
|
||||
|
||||
## Lokal / CI
|
||||
|
||||
```powershell
|
||||
$env:BASE_URL="https://dev.kairo.jinkendo.de"
|
||||
k6 run scripts/load/k6-health-baseline.js
|
||||
```
|
||||
|
||||
```bash
|
||||
BASE_URL=https://dev.kairo.jinkendo.de k6 run scripts/load/k6-health-baseline.js
|
||||
```
|
||||
|
||||
Im Gitea-Workflow `test.yml` (Job `k6-health-baseline`) wird k6 automatisch installiert und ausgeführt.
|
||||
32
scripts/load/k6-health-baseline.js
Normal file
32
scripts/load/k6-health-baseline.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Phase-0-Baseline: parallele GET /health (kein Auth).
|
||||
* BASE_URL optional, z. B. https://dev.kairo.jinkendo.de
|
||||
*/
|
||||
import http from 'k6/http'
|
||||
import { check } from 'k6'
|
||||
|
||||
export const options = {
|
||||
scenarios: {
|
||||
health: {
|
||||
executor: 'constant-vus',
|
||||
vus: 10,
|
||||
duration: '30s',
|
||||
gracefulStop: '5s',
|
||||
tags: { scenario: 'health' },
|
||||
exec: 'health',
|
||||
},
|
||||
},
|
||||
thresholds: {
|
||||
http_req_failed: ['rate<0.05'],
|
||||
'http_req_duration{scenario:health}': ['p(95)<3000'],
|
||||
},
|
||||
}
|
||||
|
||||
const BASE = (__ENV.BASE_URL || 'https://dev.kairo.jinkendo.de').replace(/\/$/, '')
|
||||
|
||||
export function health() {
|
||||
const res = http.get(`${BASE}/health`, { tags: { scenario: 'health' } })
|
||||
check(res, {
|
||||
'health 2xx': (r) => r.status >= 200 && r.status < 300,
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user