feat: homelab deployment - NPM + IONOS DNS + monitoring + NAS backup

- Restructured docker-compose for Nginx Proxy Manager (no custom nginx)
- Added domain wordly.art configuration
- Added Prometheus + Grafana monitoring stack with pre-configured dashboards
- Added PostgreSQL backup script to NAS (daily/weekly/monthly rotation)
- Added alert rules for backend, system, and Docker metrics
- Updated deployment guide for NPM + IONOS DNS homelab setup
- Added marketing plan document
- PDF translator and watermark support
- Enhanced middleware, routes, and translator modules

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-10 11:43:28 +02:00
parent 16ac7ca2b9
commit ce8e150a61
110 changed files with 6935 additions and 4301 deletions

View File

@@ -37,28 +37,29 @@ import os
# or google/gemini-3-flash ($0.15 / $0.60)
PLANS = {
PlanType.FREE: {
"name": "Gratuit",
"name": "Free",
"price_monthly": 0,
"price_yearly": 0,
"docs_per_month": 5,
"max_pages_per_doc": 15,
"docs_per_month": 2,
"max_pages_per_doc": 10,
"max_file_size_mb": 5,
"max_chars_per_month": 50_000,
"max_chars_per_month": 20_000,
"providers": ["google"],
"features": [
"5 documents / mois",
"Jusqu'à 15 pages par document",
"Google Traduction inclus",
"Toutes les langues (130+)",
"Support communautaire",
"pricing.plans.free.feat1",
"pricing.plans.free.feat2",
"pricing.plans.free.feat3",
"pricing.plans.free.feat4",
"pricing.plans.free.feat5",
],
"ai_translation": False,
"api_access": False,
"priority_processing": False,
"watermark": True,
"stripe_price_id_monthly": None,
"stripe_price_id_yearly": None,
"highlight": None,
"description": "Parfait pour découvrir l'application",
"description": "pricing.plans.free.description",
"badge": None,
},
PlanType.STARTER: {
@@ -71,12 +72,12 @@ PLANS = {
"max_chars_per_month": 500_000,
"providers": ["google", "deepl"],
"features": [
"50 documents / mois",
"Jusqu'à 50 pages par document",
"Google Traduction + DeepL",
"Fichiers jusqu'à 10 Mo",
"Support par e-mail",
"Historique 30 jours",
"pricing.plans.starter.feat1",
"pricing.plans.starter.feat2",
"pricing.plans.starter.feat3",
"pricing.plans.starter.feat4",
"pricing.plans.starter.feat5",
"pricing.plans.starter.feat6",
],
"ai_translation": False,
"api_access": False,
@@ -84,7 +85,7 @@ PLANS = {
"stripe_price_id_monthly": os.getenv("STRIPE_PRICE_STARTER_MONTHLY", ""),
"stripe_price_id_yearly": os.getenv("STRIPE_PRICE_STARTER_YEARLY", ""),
"highlight": None,
"description": "Pour les particuliers et petits projets",
"description": "pricing.plans.starter.description",
"badge": None,
},
PlanType.PRO: {
@@ -98,14 +99,14 @@ PLANS = {
"providers": ["google", "google_cloud", "deepl", "openrouter"],
"ai_model_essential": "deepseek/deepseek-v3.2",
"features": [
"200 documents / mois",
"Jusqu'à 200 pages par document",
"Traduction IA Essentielle incluse (DeepSeek V3.2)",
"Google Traduction + DeepL",
"Fichiers jusqu'à 25 Mo",
"Glossaires personnalisés",
"Support prioritaire par e-mail",
"Historique 90 jours",
"pricing.plans.pro.feat1",
"pricing.plans.pro.feat2",
"pricing.plans.pro.feat3",
"pricing.plans.pro.feat4",
"pricing.plans.pro.feat5",
"pricing.plans.pro.feat6",
"pricing.plans.pro.feat7",
"pricing.plans.pro.feat8",
],
"ai_translation": True,
"ai_tier": "essential",
@@ -113,9 +114,9 @@ PLANS = {
"priority_processing": True,
"stripe_price_id_monthly": os.getenv("STRIPE_PRICE_PRO_MONTHLY", ""),
"stripe_price_id_yearly": os.getenv("STRIPE_PRICE_PRO_YEARLY", ""),
"highlight": "Le plus populaire",
"description": "Pour les professionnels et équipes en croissance",
"badge": "POPULAIRE",
"highlight": "pricing.plans.pro.highlight",
"description": "pricing.plans.pro.description",
"badge": "POPULAR",
},
PlanType.BUSINESS: {
"name": "Business",
@@ -129,17 +130,16 @@ PLANS = {
"ai_model_essential": "deepseek/deepseek-v3.2",
"ai_model_premium": "anthropic/claude-3.5-haiku",
"features": [
"1 000 documents / mois",
"Jusqu'à 500 pages par document",
"Traduction IA Essentielle + Premium (Claude Haiku)",
"Tous les fournisseurs de traduction",
"Fichiers jusqu'à 50 Mo",
"Accès API (10 000 appels/mois)",
"Webhooks de notification",
"Glossaires + Prompts personnalisés",
"Support dédié",
"Historique 1 an",
"Analytiques avancées",
"pricing.plans.business.feat1",
"pricing.plans.business.feat2",
"pricing.plans.business.feat3",
"pricing.plans.business.feat4",
"pricing.plans.business.feat5",
"pricing.plans.business.feat6",
"pricing.plans.business.feat7",
"pricing.plans.business.feat8",
"pricing.plans.business.feat9",
"pricing.plans.business.feat10",
],
"ai_translation": True,
"ai_tier": "premium",
@@ -150,11 +150,11 @@ PLANS = {
"stripe_price_id_monthly": os.getenv("STRIPE_PRICE_BUSINESS_MONTHLY", ""),
"stripe_price_id_yearly": os.getenv("STRIPE_PRICE_BUSINESS_YEARLY", ""),
"highlight": None,
"description": "Pour les équipes et organisations",
"description": "pricing.plans.business.description",
"badge": None,
},
PlanType.ENTERPRISE: {
"name": "Entreprise",
"name": "Enterprise",
"price_monthly": -1,
"price_yearly": -1,
"docs_per_month": -1,
@@ -163,15 +163,14 @@ PLANS = {
"max_chars_per_month": -1,
"providers": ["google", "google_cloud", "deepl", "openrouter", "openrouter_premium", "openai", "zai", "custom"],
"features": [
"Documents illimités",
"Tous les modèles IA (GPT-5, Claude Opus 4.6...)",
"Déploiement on-premise ou cloud dédié",
"SLA 99,9 % garanti",
"Support 24/7 dédié",
"Modèles IA personnalisés",
"Marque blanche (white-label)",
"Équipes illimitées",
"Intégrations sur mesure",
"pricing.plans.enterprise.feat1",
"pricing.plans.enterprise.feat2",
"pricing.plans.enterprise.feat3",
"pricing.plans.enterprise.feat4",
"pricing.plans.enterprise.feat5",
"pricing.plans.enterprise.feat6",
"pricing.plans.enterprise.feat7",
"pricing.plans.enterprise.feat8",
],
"ai_translation": True,
"ai_tier": "custom",
@@ -182,8 +181,8 @@ PLANS = {
"stripe_price_id_monthly": None,
"stripe_price_id_yearly": None,
"highlight": None,
"description": "Solutions sur mesure pour grandes organisations",
"badge": "SUR DEVIS",
"description": "pricing.plans.enterprise.description",
"badge": "ON REQUEST",
},
}
@@ -243,13 +242,13 @@ class UserCreate(BaseModel):
def validate_password_strength(cls, v: str) -> str:
"""Validate password meets minimum security requirements."""
if len(v) < 8:
raise ValueError("Le mot de passe doit contenir au moins 8 caractères")
raise ValueError("Password must be at least 8 characters")
if not re.search(r"[A-Z]", v):
raise ValueError("Le mot de passe doit contenir au moins une majuscule")
raise ValueError("Password must contain at least one uppercase letter")
if not re.search(r"[a-z]", v):
raise ValueError("Le mot de passe doit contenir au moins une minuscule")
raise ValueError("Password must contain at least one lowercase letter")
if not re.search(r"[0-9]", v):
raise ValueError("Le mot de passe doit contenir au moins un chiffre")
raise ValueError("Password must contain at least one digit")
return v