docs: add server deployment guide and compose env wiring
This commit is contained in:
46
DEPLOY_docker_compose.env.example
Normal file
46
DEPLOY_docker_compose.env.example
Normal file
@ -0,0 +1,46 @@
|
||||
# Docker Compose environment (NO SECRETS)
|
||||
#
|
||||
# Copy to `.env` (it is gitignored):
|
||||
# cp DEPLOY_docker_compose.env.example .env
|
||||
#
|
||||
# Then set real values (password manager / vault).
|
||||
|
||||
# Core (required)
|
||||
DB_PASSWORD=change-me
|
||||
SECRET_KEY=GENERATE_A_LONG_RANDOM_SECRET
|
||||
ENVIRONMENT=production
|
||||
SITE_URL=https://your-domain.com
|
||||
|
||||
# CORS (only needed if frontend and backend are different origins)
|
||||
ALLOWED_ORIGINS=https://your-domain.com,https://www.your-domain.com
|
||||
|
||||
# Cookies (optional)
|
||||
COOKIE_SECURE=true
|
||||
# COOKIE_DOMAIN=.your-domain.com
|
||||
|
||||
# Email (optional but recommended for alerts)
|
||||
# SMTP_HOST=smtp.example.com
|
||||
# SMTP_PORT=587
|
||||
# SMTP_USER=
|
||||
# SMTP_PASSWORD=
|
||||
# SMTP_FROM_EMAIL=
|
||||
# SMTP_FROM_NAME=pounce
|
||||
# SMTP_USE_TLS=true
|
||||
# SMTP_USE_SSL=false
|
||||
# CONTACT_EMAIL=
|
||||
|
||||
# OAuth (optional)
|
||||
# GOOGLE_CLIENT_ID=
|
||||
# GOOGLE_CLIENT_SECRET=
|
||||
# GOOGLE_REDIRECT_URI=https://your-domain.com/api/v1/oauth/google/callback
|
||||
# GITHUB_CLIENT_ID=
|
||||
# GITHUB_CLIENT_SECRET=
|
||||
# GITHUB_REDIRECT_URI=https://your-domain.com/api/v1/oauth/github/callback
|
||||
|
||||
# Stripe (optional)
|
||||
# STRIPE_SECRET_KEY=
|
||||
# STRIPE_WEBHOOK_SECRET=
|
||||
# STRIPE_PRICE_TRADER=
|
||||
# STRIPE_PRICE_TYCOON=
|
||||
|
||||
|
||||
@ -3,5 +3,5 @@
|
||||
# Copy to a *local-only* file and keep it OUT of git:
|
||||
# cp DEPLOY_frontend.env.example DEPLOY_frontend.env
|
||||
#
|
||||
NEXT_PUBLIC_API_URL=https://your-domain.com/api
|
||||
NEXT_PUBLIC_API_URL=https://your-domain.com/api/v1
|
||||
|
||||
|
||||
@ -54,6 +54,10 @@ For the new cookie-auth to “just work”, the recommended setup is:
|
||||
- **Serve the frontend on your main domain**
|
||||
- **Route `/api/v1/*` to the backend via reverse proxy** (nginx/caddy/Next rewrite)
|
||||
|
||||
## Server deployment (recommended)
|
||||
|
||||
See `SERVER_DEPLOYMENT.md`.
|
||||
|
||||
### Env files (important)
|
||||
|
||||
- **Never commit** any of these:
|
||||
|
||||
170
SERVER_DEPLOYMENT.md
Normal file
170
SERVER_DEPLOYMENT.md
Normal file
@ -0,0 +1,170 @@
|
||||
# Server Deployment (Docker Compose)
|
||||
|
||||
## Ziel
|
||||
|
||||
Pounce auf einem Server starten mit:
|
||||
|
||||
- **Frontend** (Next.js)
|
||||
- **Backend API** (FastAPI)
|
||||
- **Postgres**
|
||||
- **Redis** (Rate-Limit Storage + Job Queue)
|
||||
- **Scheduler** (APScheduler) – **separater Prozess**
|
||||
- **Worker** (ARQ) – **separater Prozess**
|
||||
|
||||
Damit laufen Jobs nicht mehrfach bei mehreren API-Workern und die UI bleibt schnell.
|
||||
|
||||
---
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Linux Server (z.B. Ubuntu 22.04+)
|
||||
- Docker + Docker Compose Plugin
|
||||
- Domain + HTTPS Reverse Proxy (empfohlen), damit Cookie-Auth zuverlässig funktioniert
|
||||
|
||||
---
|
||||
|
||||
## 1) Repo auf den Server holen
|
||||
|
||||
```bash
|
||||
cd /opt
|
||||
git clone <your-repo-url> pounce
|
||||
cd pounce
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2) Server-Environment anlegen
|
||||
|
||||
In `/opt/pounce`:
|
||||
|
||||
```bash
|
||||
cp DEPLOY_docker_compose.env.example .env
|
||||
```
|
||||
|
||||
Dann `.env` öffnen und mindestens setzen:
|
||||
|
||||
- **DB_PASSWORD**
|
||||
- **SECRET_KEY**
|
||||
- **SITE_URL** (z.B. `https://pounce.example.com`)
|
||||
- **ALLOWED_ORIGINS** (z.B. `https://pounce.example.com`)
|
||||
|
||||
Optional (aber empfohlen):
|
||||
|
||||
- **SMTP_\*** (für Alerts/Emails)
|
||||
- **COOKIE_DOMAIN** (wenn du Cookies über Subdomains teilen willst)
|
||||
|
||||
---
|
||||
|
||||
## 3) Starten
|
||||
|
||||
```bash
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
Services:
|
||||
|
||||
- `frontend` (Port 3000)
|
||||
- `backend` (Port 8000)
|
||||
- `scheduler` (kein Port)
|
||||
- `worker` (kein Port)
|
||||
- `db` (kein Port)
|
||||
- `redis` (kein Port)
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 4) Initial Setup (1× nach erstem Start)
|
||||
|
||||
### DB Tabellen + Baseline Seed
|
||||
|
||||
```bash
|
||||
docker compose exec backend python scripts/init_db.py
|
||||
```
|
||||
|
||||
### TLD Price Seed (886+)
|
||||
|
||||
```bash
|
||||
docker compose exec backend python scripts/seed_tld_prices.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5) Reverse Proxy (empfohlen)
|
||||
|
||||
### Warum?
|
||||
|
||||
Das Frontend ruft im Browser standardmässig `https://<domain>/api/v1/...` auf (same-origin).
|
||||
Darum solltest du:
|
||||
|
||||
- **HTTPS** terminieren
|
||||
- `/api/v1/*` an das Backend routen
|
||||
- `/` an das Frontend routen
|
||||
|
||||
### Beispiel: Caddy (sehr simpel)
|
||||
|
||||
```caddy
|
||||
pounce.example.com {
|
||||
encode zstd gzip
|
||||
|
||||
# API
|
||||
handle_path /api/v1/* {
|
||||
reverse_proxy 127.0.0.1:8000
|
||||
}
|
||||
|
||||
# Frontend
|
||||
reverse_proxy 127.0.0.1:3000
|
||||
|
||||
# optional: metrics nur intern
|
||||
@metrics path /metrics
|
||||
handle @metrics {
|
||||
respond 403
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Wichtig:
|
||||
|
||||
- Setze `SITE_URL=https://pounce.example.com`
|
||||
- Setze `COOKIE_SECURE=true` (oder via `ENVIRONMENT=production`)
|
||||
|
||||
---
|
||||
|
||||
## 6) Checks (nach Deploy)
|
||||
|
||||
```bash
|
||||
curl -f http://127.0.0.1:8000/health
|
||||
curl -f http://127.0.0.1:8000/metrics
|
||||
```
|
||||
|
||||
Logs:
|
||||
|
||||
```bash
|
||||
docker compose logs -f backend
|
||||
docker compose logs -f scheduler
|
||||
docker compose logs -f worker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7) Updates
|
||||
|
||||
```bash
|
||||
cd /opt/pounce
|
||||
git pull
|
||||
docker compose up -d --build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting (häufig)
|
||||
|
||||
- **Cookies/Login klappt nicht**:
|
||||
- Prüfe `SITE_URL` und HTTPS (Secure Cookies)
|
||||
- Prüfe `ALLOWED_ORIGINS` (falls Frontend/Backend nicht same-origin sind)
|
||||
- **Scheduler läuft doppelt**:
|
||||
- Stelle sicher, dass nur **ein** `scheduler` Service läuft (keine zweite Instanz)
|
||||
- **Emails werden nicht gesendet**:
|
||||
- `docker compose exec scheduler env | grep SMTP_`
|
||||
- SMTP Vars müssen im Container vorhanden sein (kommen aus `.env`)
|
||||
|
||||
|
||||
@ -118,14 +118,15 @@ async def rate_limit_handler(request: Request, exc: RateLimitExceeded):
|
||||
},
|
||||
)
|
||||
|
||||
# Get allowed origins from environment
|
||||
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "").split(",")
|
||||
if not ALLOWED_ORIGINS or ALLOWED_ORIGINS == [""]:
|
||||
ALLOWED_ORIGINS = [
|
||||
"http://localhost:3000",
|
||||
"http://127.0.0.1:3000",
|
||||
"http://10.42.0.73:3000",
|
||||
]
|
||||
# Get allowed origins (env overrides settings)
|
||||
origins_raw = (
|
||||
os.getenv("ALLOWED_ORIGINS", "").strip()
|
||||
or os.getenv("CORS_ORIGINS", "").strip()
|
||||
or (settings.cors_origins or "").strip()
|
||||
)
|
||||
ALLOWED_ORIGINS = [o.strip() for o in origins_raw.split(",") if o.strip()]
|
||||
if not ALLOWED_ORIGINS:
|
||||
ALLOWED_ORIGINS = ["http://localhost:3000", "http://127.0.0.1:3000"]
|
||||
|
||||
# Add production origins
|
||||
SITE_URL = os.getenv("SITE_URL", "")
|
||||
|
||||
@ -1,5 +1,45 @@
|
||||
version: '3.8'
|
||||
|
||||
x-backend-env: &backend-env
|
||||
DATABASE_URL: postgresql+asyncpg://pounce:${DB_PASSWORD:-changeme}@db:5432/pounce
|
||||
SECRET_KEY: ${SECRET_KEY:-change-this-in-production}
|
||||
ENVIRONMENT: ${ENVIRONMENT:-production}
|
||||
SITE_URL: ${SITE_URL:-}
|
||||
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-}
|
||||
COOKIE_DOMAIN: ${COOKIE_DOMAIN:-}
|
||||
COOKIE_SECURE: ${COOKIE_SECURE:-true}
|
||||
# Optional: SMTP (email alerts)
|
||||
SMTP_HOST: ${SMTP_HOST:-}
|
||||
SMTP_PORT: ${SMTP_PORT:-587}
|
||||
SMTP_USER: ${SMTP_USER:-}
|
||||
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
|
||||
SMTP_FROM_EMAIL: ${SMTP_FROM_EMAIL:-}
|
||||
SMTP_FROM_NAME: ${SMTP_FROM_NAME:-pounce}
|
||||
SMTP_USE_TLS: ${SMTP_USE_TLS:-true}
|
||||
SMTP_USE_SSL: ${SMTP_USE_SSL:-false}
|
||||
CONTACT_EMAIL: ${CONTACT_EMAIL:-}
|
||||
# Optional: OAuth
|
||||
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
|
||||
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
|
||||
GOOGLE_REDIRECT_URI: ${GOOGLE_REDIRECT_URI:-}
|
||||
GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID:-}
|
||||
GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET:-}
|
||||
GITHUB_REDIRECT_URI: ${GITHUB_REDIRECT_URI:-}
|
||||
# Optional: Stripe
|
||||
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-}
|
||||
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-}
|
||||
STRIPE_PRICE_TRADER: ${STRIPE_PRICE_TRADER:-}
|
||||
STRIPE_PRICE_TYCOON: ${STRIPE_PRICE_TYCOON:-}
|
||||
# Optional integrations
|
||||
DROPCATCH_CLIENT_ID: ${DROPCATCH_CLIENT_ID:-}
|
||||
DROPCATCH_CLIENT_SECRET: ${DROPCATCH_CLIENT_SECRET:-}
|
||||
DROPCATCH_API_BASE: ${DROPCATCH_API_BASE:-https://api.dropcatch.com}
|
||||
SEDO_PARTNER_ID: ${SEDO_PARTNER_ID:-}
|
||||
SEDO_SIGN_KEY: ${SEDO_SIGN_KEY:-}
|
||||
SEDO_API_BASE: ${SEDO_API_BASE:-https://api.sedo.com/api/v1/}
|
||||
MOZ_ACCESS_ID: ${MOZ_ACCESS_ID:-}
|
||||
MOZ_SECRET_KEY: ${MOZ_SECRET_KEY:-}
|
||||
|
||||
services:
|
||||
# PostgreSQL Database
|
||||
db:
|
||||
@ -42,9 +82,7 @@ services:
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
DATABASE_URL: postgresql+asyncpg://pounce:${DB_PASSWORD:-changeme}@db:5432/pounce
|
||||
SECRET_KEY: ${SECRET_KEY:-change-this-in-production}
|
||||
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000}
|
||||
<<: *backend-env
|
||||
ENABLE_SCHEDULER: "false"
|
||||
ENABLE_JOB_QUEUE: "true"
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
@ -69,8 +107,7 @@ services:
|
||||
container_name: pounce-scheduler
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DATABASE_URL: postgresql+asyncpg://pounce:${DB_PASSWORD:-changeme}@db:5432/pounce
|
||||
SECRET_KEY: ${SECRET_KEY:-change-this-in-production}
|
||||
<<: *backend-env
|
||||
ENABLE_SCHEDULER: "true"
|
||||
depends_on:
|
||||
db:
|
||||
@ -85,8 +122,7 @@ services:
|
||||
container_name: pounce-worker
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DATABASE_URL: postgresql+asyncpg://pounce:${DB_PASSWORD:-changeme}@db:5432/pounce
|
||||
SECRET_KEY: ${SECRET_KEY:-change-this-in-production}
|
||||
<<: *backend-env
|
||||
ENABLE_SCHEDULER: "false"
|
||||
ENABLE_JOB_QUEUE: "true"
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
@ -107,7 +143,8 @@ services:
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
NEXT_PUBLIC_API_URL: ${API_URL:-http://localhost:8000}
|
||||
# Internal backend URL for server-side requests
|
||||
BACKEND_URL: http://backend:8000
|
||||
depends_on:
|
||||
- backend
|
||||
|
||||
|
||||
@ -10,8 +10,11 @@
|
||||
const getApiBase = (): string => {
|
||||
// Server-side rendering: use environment variable or localhost
|
||||
if (typeof window === 'undefined') {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api/v1'
|
||||
return baseUrl.replace(/\/$/, '')
|
||||
// Prefer internal backend URL (server-only) when running in Docker/SSR
|
||||
const backendUrl = process.env.BACKEND_URL?.replace(/\/$/, '')
|
||||
const configured = (backendUrl ? `${backendUrl}/api/v1` : process.env.NEXT_PUBLIC_API_URL) || 'http://localhost:8000/api/v1'
|
||||
const normalized = configured.replace(/\/$/, '')
|
||||
return normalized.endsWith('/api/v1') ? normalized : `${normalized}/api/v1`
|
||||
}
|
||||
|
||||
const { protocol, hostname, port } = window.location
|
||||
|
||||
Reference in New Issue
Block a user