Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 38 additions & 20 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,25 @@ POSTGRES_PASSWORD=changeme
POSTGRES_DB=trainiq

# === Redis ===
REDIS_URL=redis://localhost:6379
REDIS_URL=redis://redis:6379/0
# Production mit Passwort:
# REDIS_PASSWORD=CHANGE_ME_REDIS_PASSWORD
# REDIS_URL=redis://:CHANGE_ME_REDIS_PASSWORD@redis:6379/0

# === Production Tuning ===
# Gunicorn worker count (empfohlen: 2×CPUs+1)
WORKERS=4

# === Security (WICHTIG: vor Deployment ändern!) ===
# Generiere mit: python -c "import secrets; print(secrets.token_hex(32))"
JWT_SECRET=AENDERN_VOR_DEPLOYMENT
# Generiere mit: openssl rand -hex 32
JWT_SECRET=CHANGE_ME_BEFORE_DEPLOYMENT_USE_openssl_rand_hex_32

# === LLM (OpenAI-kompatible API) ===
LLM_API_KEY=dein_llm_api_key_hier
LLM_BASE_URL=https://api.openai.com/v1
LLM_MODEL=gpt-4o-mini
# Foto-Analyse: Vision-fähiges Modell (leer = LLM_MODEL wird verwendet, muss Vision unterstützen)
LLM_VISION_MODEL=gpt-4o-mini

# === Bildupload (Cloudinary — optional) ===
CLOUDINARY_CLOUD_NAME=
Expand All @@ -29,12 +38,6 @@ AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=eu-central-1
BACKUP_RETENTION_DAYS=7

# === Strava OAuth (optional) ===
STRAVA_CLIENT_ID=
STRAVA_CLIENT_SECRET=
STRAVA_REDIRECT_URI=http://localhost/api/watch/strava/callback
STRAVA_WEBHOOK_VERIFY_TOKEN=trainiq_webhook

# === SMTP E-Mail (optional) ===
SMTP_HOST=localhost
SMTP_PORT=587
Expand All @@ -44,13 +47,14 @@ SMTP_USE_TLS=true
FROM_EMAIL=noreply@trainiq.app
FROM_NAME=TrainIQ

# === App-Konfiguration ===
# Domain für HTTPS / Let's Encrypt (z.B. trainiq.example.com)
DOMAIN=localhost
# === App-Konfiguration (Oracle Cloud — Backend only) ===
# API-Subdomain: Backend + nginx laufen hier
DOMAIN=api.trainiq.com
ADMIN_EMAIL=admin@example.com
FRONTEND_URL=http://localhost
# FRONTEND_URL = eigene Domain auf Vercel (steuert CORS + OAuth-Redirects)
FRONTEND_URL=https://trainiq.com
# DEV_MODE=true → kein Login, Demo-User ID wird verwendet
DEV_MODE=true
DEV_MODE=false
DEMO_USER_ID=00000000-0000-0000-0000-000000000001

# === Keycloak ===
Expand All @@ -60,19 +64,31 @@ KEYCLOAK_REALM=trainiq
KEYCLOAK_CLIENT_ID=trainiq-frontend
KEYCLOAK_CLIENT_SECRET=
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_ADMIN_PASSWORD=CHANGE_ME_STRONG_KEYCLOAK_PASSWORD
KEYCLOAK_DB_USER=keycloak
KEYCLOAK_DB_PASSWORD=keycloak
KEYCLOAK_DB_PASSWORD=CHANGE_ME_KEYCLOAK_DB_PASSWORD
KEYCLOAK_DB_NAME=keycloak
KEYCLOAK_HOSTNAME=localhost

# === CORS Origins (optional, für Produktion) ===
# Komma-getrennte Liste zusätzlicher Origins
ADDITIONAL_CORS_ORIGINS=https://trainiq.app,https://www.trainiq.app
# Frontend-Domain (Vercel) wird automatisch via FRONTEND_URL erlaubt.
# Hier bei Bedarf weitere Origins (www, etc.) ergänzen:
ADDITIONAL_CORS_ORIGINS=https://www.trainiq.com

# === Frontend ===
# === Frontend (Vercel — wird in Vercel Dashboard gesetzt, NICHT in dieser Datei) ===
# NEXT_PUBLIC_API_URL = https://api.trainiq.com/api (Vercel env var)
# BACKEND_URL = https://api.trainiq.com (Vercel env var, server-side)
# NEXT_PUBLIC_VAPID_KEY = ... (Vercel env var)
# NEXT_PUBLIC_SENTRY_DSN = ... (Vercel env var)
#
# Lokal (frontend/.env.local):
NEXT_PUBLIC_API_URL=http://localhost/api
BACKEND_URL=http://backend:8000
BACKEND_URL=http://localhost:8000

# === VAPID Push Notifications ===
# Generiere mit: npx web-push generate-vapid-keys
VAPID_PUBLIC_KEY=
VAPID_PRIVATE_KEY=

# === Sentry Error Tracking (optional) ===
# Backend DSN
Expand All @@ -93,6 +109,8 @@ STRIPE_PRICE_PRO_YEARLY=
# === Web Push Notifications (optional) ===
VAPID_PRIVATE_KEY=
VAPID_PUBLIC_KEY=
# Frontend-side VAPID key (same as VAPID_PUBLIC_KEY, exposed to browser)
NEXT_PUBLIC_VAPID_KEY=

# === Garmin Connect (optional) ===
GARMIN_CLIENT_ID=
Expand Down
18 changes: 15 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,14 @@ jobs:
JWT_SECRET: test-secret-ci-only
DEV_MODE: "true"
DEMO_USER_ID: "00000000-0000-0000-0000-000000000001"
GEMINI_API_KEY: ""
LLM_API_KEY: ""
LLM_BASE_URL: "https://api.openai.com/v1"
CLOUDINARY_CLOUD_NAME: ""
CLOUDINARY_API_KEY: ""
CLOUDINARY_API_SECRET: ""
STRAVA_CLIENT_ID: ""
STRAVA_CLIENT_SECRET: ""
FRONTEND_URL: "http://localhost:3000"
run: python -m pytest tests/ -v --tb=short

frontend-build:
Expand All @@ -72,11 +76,19 @@ jobs:

- name: Type check
working-directory: frontend
run: npx tsc --noEmit
run: node_modules/.bin/tsc --noEmit

- name: Unit tests
working-directory: frontend
run: node_modules/.bin/vitest run

- name: Build
working-directory: frontend
env:
NEXT_TELEMETRY_DISABLED: 1
BACKEND_URL: http://backend:8000
# Simulate Vercel production environment
NEXT_PUBLIC_API_URL: https://api.trainiq.com/api
BACKEND_URL: https://api.trainiq.com
NEXT_PUBLIC_VAPID_KEY: ""
NEXT_PUBLIC_SENTRY_DSN: ""
run: npm run build
47 changes: 47 additions & 0 deletions .github/workflows/deploy-oracle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Deploy Backend → Oracle Cloud

on:
push:
branches: [main]
paths:
- "backend/**"
- "docker-compose.backend.yml"
- "nginx/**"
- "keycloak/**"
- "postgres/**"
- ".github/workflows/deploy-oracle.yml"
workflow_dispatch:

jobs:
deploy:
name: Oracle Cloud Production Deploy
runs-on: ubuntu-latest

steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.ORACLE_HOST }}
username: ${{ secrets.ORACLE_USER }}
key: ${{ secrets.ORACLE_SSH_KEY }}
port: ${{ secrets.ORACLE_SSH_PORT || 22 }}
script: |
set -e
cd ~/trainiq

# Pull latest code
git pull origin main

# Rebuild changed images and restart backend services
docker compose -f docker-compose.backend.yml build --pull backend scheduler worker

docker compose -f docker-compose.backend.yml up -d \
--remove-orphans \
migrate backend scheduler worker nginx certbot

# Wait for backend health check
sleep 15
docker compose -f docker-compose.backend.yml ps

# Remove dangling images older than 24 h to free disk space
docker image prune -f --filter "until=24h"
45 changes: 45 additions & 0 deletions .github/workflows/deploy-vercel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Deploy Frontend → Vercel

on:
push:
branches: [main]
paths:
- "frontend/**"
- ".github/workflows/deploy-vercel.yml"
workflow_dispatch:

jobs:
deploy:
name: Vercel Production Deploy
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install Vercel CLI
run: npm install --global vercel@latest

- name: Pull Vercel environment
working-directory: frontend
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

- name: Build
working-directory: frontend
run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

- name: Deploy to Vercel
working-directory: frontend
run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
Loading
Loading