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

@@ -14,16 +14,10 @@ Features:
from typing import List, Optional, Dict, Any
import time
try:
import structlog
from core.logging import get_logger
logger = structlog.get_logger(__name__)
_HAS_STRUCTLOG = True
except ImportError:
import logging
logger = logging.getLogger(__name__)
_HAS_STRUCTLOG = False
logger = get_logger(__name__)
_HAS_STRUCTLOG = True
def _log_info(event: str, **kwargs):
@@ -332,6 +326,41 @@ class LegacyFallbackAdapter:
source_language: str = "auto",
batch_size: int = 50,
) -> List[str]:
if not texts:
return []
from .config import ProvidersConfig
provider_names = ProvidersConfig.get_fallback_chain(self._mode)
requests = [
TranslationRequest(
text=t,
target_language=target_language,
source_language=source_language,
)
for t in texts
]
# Try each provider once with a full batch (avoids N× fallback chain walks).
for provider_name in provider_names:
p = registry.get(provider_name)
if p is None:
continue
if not p.is_available():
continue
try:
responses = p.translate_batch(requests)
except Exception:
continue
if len(responses) != len(requests):
continue
if all(r.error is None for r in responses):
self._last_provider_used = provider_name
return [
r.translated_text if r.translated_text is not None else texts[i]
for i, r in enumerate(responses)
]
results: List[str] = []
for t in texts:
req = TranslationRequest(