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:
Sepehr Ramezani
2026-04-25 15:01:47 +02:00
parent 2ba4fedfc8
commit 26bd096a06
1178 changed files with 136435 additions and 3047 deletions

View File

@@ -20,12 +20,9 @@ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeou
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional
try:
import structlog
logger = structlog.get_logger(__name__)
except ImportError:
import logging
logger = logging.getLogger(__name__)
from core.logging import get_logger
logger = get_logger(__name__)
from .base import TranslationProvider
from .schemas import (
@@ -42,6 +39,23 @@ GOOGLE_NETWORK_ERROR = "GOOGLE_NETWORK_ERROR"
GOOGLE_UNSUPPORTED_LANGUAGE = "GOOGLE_UNSUPPORTED_LANGUAGE"
GOOGLE_TEXT_TOO_LONG = "GOOGLE_TEXT_TOO_LONG"
# Align with services.translation_service.GoogleTranslationProvider (deep_translator codes)
_GOOGLE_LANG_MAP: dict[str, str] = {
"zh": "zh-CN",
"zh-cn": "zh-CN",
"zh-tw": "zh-TW",
"iw": "he",
"he": "iw",
"jv": "jw",
"nb": "no",
}
def _normalize_lang_for_google(code: str) -> str:
if not code or code == "auto":
return "auto"
return _GOOGLE_LANG_MAP.get(code, _GOOGLE_LANG_MAP.get(code.lower(), code))
_RETRYABLE_ERRORS = {GOOGLE_NETWORK_ERROR, GOOGLE_QUOTA_EXCEEDED}
@@ -121,13 +135,13 @@ class GoogleTranslationProvider(TranslationProvider):
"""Get or create a translator instance for the current thread."""
from deep_translator import GoogleTranslator
key = f"{source_language}_{target_language}"
src = _normalize_lang_for_google(source_language)
tgt = _normalize_lang_for_google(target_language)
key = f"{src}_{tgt}"
if not hasattr(self._local, "translators"):
self._local.translators = {}
if key not in self._local.translators:
self._local.translators[key] = GoogleTranslator(
source=source_language, target=target_language
)
self._local.translators[key] = GoogleTranslator(source=src, target=tgt)
return self._local.translators[key]
def _make_api_request(
@@ -377,15 +391,42 @@ class GoogleTranslationProvider(TranslationProvider):
"""
Translate multiple texts with optimized batch processing.
Args:
requests: List of TranslationRequest objects
Returns:
List of TranslationResponse objects
When all requests share the same source/target languages, delegates to
services.translation_service.GoogleTranslationProvider.translate_batch
(batched deep_translator calls). Otherwise falls back to per-item
translate_text.
"""
if not requests:
return []
tgt0 = requests[0].target_language
src0 = requests[0].source_language or "auto"
uniform = all(
r.target_language == tgt0 and (r.source_language or "auto") == src0
for r in requests
)
if uniform:
try:
from services.translation_service import (
GoogleTranslationProvider as LegacyGoogle,
)
texts = [r.text for r in requests]
outs = LegacyGoogle().translate_batch(texts, tgt0, src0)
return [
TranslationResponse(
translated_text=out,
provider_name=self._provider_name,
from_cache=False,
)
for out in outs
]
except Exception as e:
logger.warning(
"google_batch_legacy_fallback",
error_type=type(e).__name__,
)
return [self.translate_text(req) for req in requests]
def health_check(self) -> ProviderHealthStatus: