feat: production deployment - full update with providers, admin, glossaries, pricing, tests
Major changes across backend, frontend, infrastructure: - Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud) - Admin panel: user management, pricing, settings - Glossary system with CSV import/export - Subscription and tier quota management - Security hardening (rate limiting, API key auth, path traversal fixes) - Docker compose for dev, prod, and IONOS deployment - Alembic migrations for new tables - Frontend: dashboard, pricing page, landing page, i18n (en/fr) - Test suite and verification scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
117
.env.example
117
.env.example
@@ -1,27 +1,83 @@
|
||||
# Document Translation API - Environment Configuration
|
||||
# Copy this file to .env and configure your settings
|
||||
# ⚠️ NEVER commit .env to version control!
|
||||
#
|
||||
# LEGEND (Story 6.6 - NFR10):
|
||||
# - Required: App fails at startup with a clear message listing missing variables if not set (see .env.example or README).
|
||||
# - Optional: Default value used when absent (documented below or in code). No fail-fast.
|
||||
# - No real secrets in this file: use placeholders only (e.g. your_secret_here, pk_..., sk_..., whsec_...).
|
||||
#
|
||||
# Production (ENV=production or docker compose prod):
|
||||
# Required: JWT_SECRET_KEY, ADMIN_USERNAME, (ADMIN_PASSWORD or ADMIN_PASSWORD_HASH), ADMIN_TOKEN_SECRET,
|
||||
# DATABASE_URL (or POSTGRES_*), REDIS_URL if RATE_LIMIT_ENABLED=true.
|
||||
# Optional: Provider keys, Stripe, CORS_ORIGINS, LOG_LEVEL, etc. (defaults or feature disabled).
|
||||
# Development: Some required vars may get a default or warning; REDIS_URL optional if rate limiting disabled.
|
||||
# See sections below for "Required" vs "Optional" and "If absent" behavior.
|
||||
|
||||
# ============== Translation Services ==============
|
||||
# Default provider: google, ollama, deepl, libre, openai, openrouter
|
||||
# Optional. Default: google. If absent: default used.
|
||||
TRANSLATION_SERVICE=google
|
||||
# Optional. Provider enable/disable flags. If absent: default true/false per line below.
|
||||
GOOGLE_TRANSLATE_ENABLED=true
|
||||
GOOGLE_CLOUD_ENABLED=false
|
||||
DEEPL_ENABLED=false
|
||||
OPENAI_ENABLED=false
|
||||
OLLAMA_ENABLED=false
|
||||
OPENROUTER_ENABLED=false
|
||||
|
||||
# DeepL API Key (required for DeepL provider)
|
||||
# Get from: https://www.deepl.com/pro-api
|
||||
# Provider fallback chain (comma-separated, in priority order)
|
||||
PROVIDER_FALLBACK_CHAIN=google,deepl,openai,ollama,openrouter
|
||||
|
||||
# Mode-specific fallback chains
|
||||
# Classic mode: Google Translate -> DeepL
|
||||
FALLBACK_CHAIN_CLASSIC=google,deepl
|
||||
|
||||
# LLM mode: Ollama (local) -> OpenAI (cloud)
|
||||
FALLBACK_CHAIN_LLM=ollama,openai
|
||||
|
||||
# Google Translate Configuration (GRATUIT via deep_translator — accès web non officiel)
|
||||
# Pas de clé API requise. Limites non garanties (usage raisonnable recommandé).
|
||||
GOOGLE_TRANSLATE_TIMEOUT=30
|
||||
GOOGLE_TRANSLATE_MAX_RETRIES=3
|
||||
GOOGLE_TRANSLATE_RETRY_DELAY=1
|
||||
|
||||
# Google Cloud Translation API v2 — version officielle et facturable
|
||||
# Activer : console.cloud.google.com → APIs & Services → Cloud Translation API
|
||||
# 500 000 caractères/mois GRATUITS, puis ~$20/million de caractères
|
||||
GOOGLE_CLOUD_ENABLED=false
|
||||
GOOGLE_CLOUD_API_KEY=
|
||||
GOOGLE_CLOUD_TIMEOUT=30
|
||||
GOOGLE_CLOUD_MAX_RETRIES=3
|
||||
GOOGLE_CLOUD_RETRY_DELAY=1
|
||||
|
||||
# DeepL Configuration. Optional. If absent: provider disabled.
|
||||
# Get API key from: https://www.deepl.com/pro-api. Free tier keys end with :fx.
|
||||
DEEPL_API_KEY=
|
||||
DEEPL_TIMEOUT=30
|
||||
DEEPL_MAX_RETRIES=3
|
||||
DEEPL_RETRY_DELAY=1
|
||||
|
||||
# OpenAI API Key (required for OpenAI provider)
|
||||
# Get from: https://platform.openai.com/api-keys
|
||||
# OpenAI Configuration. Optional. If absent: provider disabled.
|
||||
# Get API key from: https://platform.openai.com/api-keys
|
||||
OPENAI_API_KEY=
|
||||
OPENAI_MODEL=gpt-4o-mini
|
||||
OPENAI_TIMEOUT=60
|
||||
OPENAI_MAX_RETRIES=3
|
||||
OPENAI_RETRY_DELAY=1.0
|
||||
OPENAI_HEALTH_CHECK_TIMEOUT=5
|
||||
# OPENAI_BASE_URL=https://api.openai.com/v1 # Optional: for Azure OpenAI or proxies
|
||||
|
||||
# OpenRouter API Key (required for OpenRouter provider)
|
||||
# Get from: https://openrouter.ai/keys
|
||||
# OpenRouter Configuration. Optional. If absent: provider disabled.
|
||||
OPENROUTER_API_KEY=
|
||||
OPENROUTER_MODEL=deepseek/deepseek-chat
|
||||
|
||||
# Ollama Configuration (for local LLM-based translation)
|
||||
# Ollama Configuration. Optional. If absent: default http://localhost:11434 (provider may be disabled).
|
||||
OLLAMA_BASE_URL=http://localhost:11434
|
||||
OLLAMA_MODEL=llama3
|
||||
OLLAMA_VISION_MODEL=llava
|
||||
OLLAMA_TIMEOUT=120
|
||||
OLLAMA_MAX_RETRIES=2
|
||||
OLLAMA_RETRY_DELAY=2
|
||||
|
||||
# ============== File Limits ==============
|
||||
# Maximum file size in MB
|
||||
@@ -44,7 +100,7 @@ MAX_CONCURRENT_TRANSLATIONS=5
|
||||
# Enable automatic file cleanup
|
||||
CLEANUP_ENABLED=true
|
||||
|
||||
# Cleanup interval in minutes
|
||||
# Cleanup interval in minutes. Optional. Default: 5.
|
||||
CLEANUP_INTERVAL_MINUTES=15
|
||||
|
||||
# File time-to-live in minutes
|
||||
@@ -64,7 +120,8 @@ ENABLE_HSTS=false
|
||||
# ⚠️ IMPORTANT: Set to your actual frontend domain(s) in production!
|
||||
# Example: https://yourdomain.com,https://www.yourdomain.com
|
||||
# Use "*" ONLY for local development
|
||||
CORS_ORIGINS=http://localhost:3000
|
||||
# Dev: include every origin your Next.js dev server uses (e.g. 3000 and 3001). Non-production also auto-merges common localhost ports.
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:3001
|
||||
|
||||
# Maximum request size in MB
|
||||
MAX_REQUEST_SIZE_MB=100
|
||||
@@ -73,44 +130,45 @@ MAX_REQUEST_SIZE_MB=100
|
||||
REQUEST_TIMEOUT_SECONDS=300
|
||||
|
||||
# ============== Database ==============
|
||||
# PostgreSQL connection string (recommended for production)
|
||||
# Format: postgresql://user:password@host:port/database
|
||||
# DATABASE_URL=postgresql://translate_user:secure_password@localhost:5432/translate_db
|
||||
# Required in production: set either DATABASE_URL or POSTGRES_* (POSTGRES_HOST, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, optional POSTGRES_PORT). If absent in dev: SQLite used (SQLITE_PATH).
|
||||
# Format: postgresql://user:password@host:port/database. Example (no real secret):
|
||||
# DATABASE_URL=postgresql://user:your_password@localhost:5432/translate_db
|
||||
# DATABASE_URL=
|
||||
|
||||
# SQLite path (used when DATABASE_URL is not set - for development)
|
||||
# Optional. Default: data/translate.db. Used when DATABASE_URL is not set.
|
||||
SQLITE_PATH=data/translate.db
|
||||
|
||||
# Enable SQL query logging (for debugging)
|
||||
DATABASE_ECHO=false
|
||||
|
||||
# Redis for sessions and caching (recommended for production)
|
||||
# Required when RATE_LIMIT_ENABLED=true (production). Optional in dev (in-memory fallback if supported).
|
||||
# If absent and rate limiting enabled: app fails at startup with clear message (Story 6.6).
|
||||
# Example (no real secret): redis://localhost:6379/0
|
||||
# REDIS_URL=redis://localhost:6379/0
|
||||
|
||||
# ============== Admin Authentication ==============
|
||||
# ⚠️ REQUIRED: These must be set for admin endpoints to work!
|
||||
# Required for admin endpoints. If absent: app fails at startup (fail-fast) in production; see README.
|
||||
ADMIN_USERNAME=admin
|
||||
|
||||
# Use SHA256 hash of password (recommended for production)
|
||||
# Generate with: python -c "import hashlib; print(hashlib.sha256(b'your_password').hexdigest())"
|
||||
# Required (one of): ADMIN_PASSWORD_HASH (recommended prod) or ADMIN_PASSWORD. If both empty: fail-fast in production.
|
||||
# Generate hash: python -c "import hashlib; print(hashlib.sha256(b'your_password').hexdigest())"
|
||||
ADMIN_PASSWORD_HASH=
|
||||
|
||||
# Or use plain password (NOT recommended for production)
|
||||
# ADMIN_PASSWORD=
|
||||
# Plain password for dev only. Use placeholder or leave empty; never put real secret in .env.example.
|
||||
ADMIN_PASSWORD=your_admin_password_here
|
||||
|
||||
# Token secret for session management
|
||||
# Generate with: python -c "import secrets; print(secrets.token_hex(32))"
|
||||
# Required for admin session tokens. If absent: random key at startup (sessions invalidated on restart). Example: use secrets.token_hex(32)
|
||||
ADMIN_TOKEN_SECRET=
|
||||
|
||||
# ============== User Authentication ==============
|
||||
# JWT secret key for user tokens
|
||||
# Generate with: python -c "import secrets; print(secrets.token_urlsafe(64))"
|
||||
# Required. JWT signing key. If absent: app fails at startup in production. Generate: secrets.token_urlsafe(64)
|
||||
JWT_SECRET_KEY=
|
||||
|
||||
# Frontend URL for redirects
|
||||
FRONTEND_URL=http://localhost:3000
|
||||
|
||||
# ============== Stripe Payments ==============
|
||||
# Get your keys from https://dashboard.stripe.com/apikeys
|
||||
# Optional. If absent: payment features disabled. Placeholders only (pk_..., sk_..., whsec_...).
|
||||
STRIPE_PUBLISHABLE_KEY=pk_test_...
|
||||
STRIPE_SECRET_KEY=sk_test_...
|
||||
STRIPE_WEBHOOK_SECRET=whsec_...
|
||||
@@ -125,8 +183,15 @@ STRIPE_PRICE_BUSINESS_MONTHLY=price_xxx
|
||||
STRIPE_PRICE_BUSINESS_YEARLY=price_xxx
|
||||
|
||||
# ============== Monitoring ==============
|
||||
# Log level: DEBUG, INFO, WARNING, ERROR
|
||||
# Optional. Default: INFO. Log format: json or console (default from ENV/LOG_FORMAT).
|
||||
LOG_LEVEL=INFO
|
||||
LOG_FORMAT=json
|
||||
# Optional. Set to "production" for production; influences logging and some security defaults.
|
||||
# ENV=development
|
||||
|
||||
# ============== Frontend (Next.js) ==============
|
||||
# Optional. Default: http://localhost:8000 (dev), http://backend:8000 (Docker). No secrets here (NFR10).
|
||||
# NEXT_PUBLIC_API_URL=http://localhost:8000
|
||||
|
||||
# Enable request logging
|
||||
ENABLE_REQUEST_LOGGING=true
|
||||
|
||||
Reference in New Issue
Block a user