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

@@ -138,7 +138,7 @@ async def create_glossary(
status_code=500,
content={
"error": "DATABASE_ERROR",
"message": "Une erreur est survenue lors de la création du glossaire.",
"message": "An error occurred while creating the glossary.",
},
)
@@ -206,7 +206,7 @@ async def list_glossaries(
status_code=500,
content={
"error": "DATABASE_ERROR",
"message": "Une erreur est survenue lors de la récupération des glossaires.",
"message": "An error occurred while retrieving glossaries.",
},
)
@@ -232,7 +232,7 @@ async def get_glossary(
status_code=400,
content={
"error": "INVALID_GLOSSARY_ID",
"message": "Format d'identifiant de glossaire invalide.",
"message": "Invalid glossary ID format.",
},
)
@@ -250,7 +250,7 @@ async def get_glossary(
status_code=404,
content={
"error": "GLOSSARY_NOT_FOUND",
"message": "Glossaire introuvable ou vous n'avez pas accès à cette ressource.",
"message": "Glossary not found or you do not have access to this resource.",
},
)
@@ -267,7 +267,7 @@ async def get_glossary(
status_code=500,
content={
"error": "DATABASE_ERROR",
"message": "Une erreur est survenue.",
"message": "An error occurred.",
},
)
@@ -294,7 +294,7 @@ async def update_glossary(
status_code=400,
content={
"error": "INVALID_GLOSSARY_ID",
"message": "Format d'identifiant de glossaire invalide.",
"message": "Invalid glossary ID format.",
},
)
@@ -322,7 +322,7 @@ async def update_glossary(
status_code=404,
content={
"error": "GLOSSARY_NOT_FOUND",
"message": "Glossaire introuvable ou vous n'avez pas accès à cette ressource.",
"message": "Glossary not found or you do not have access to this resource.",
},
)
@@ -369,7 +369,7 @@ async def update_glossary(
status_code=500,
content={
"error": "DATABASE_ERROR",
"message": "Une erreur est survenue lors de la mise à jour.",
"message": "An error occurred while updating.",
},
)
@@ -395,7 +395,7 @@ async def delete_glossary(
status_code=400,
content={
"error": "INVALID_GLOSSARY_ID",
"message": "Format d'identifiant de glossaire invalide.",
"message": "Invalid glossary ID format.",
},
)
@@ -412,7 +412,7 @@ async def delete_glossary(
status_code=404,
content={
"error": "GLOSSARY_NOT_FOUND",
"message": "Glossaire introuvable ou vous n'avez pas accès à cette ressource.",
"message": "Glossary not found or you do not have access to this resource.",
},
)
@@ -435,7 +435,7 @@ async def delete_glossary(
status_code=500,
content={
"error": "DATABASE_ERROR",
"message": "Une erreur est survenue lors de la suppression.",
"message": "An error occurred while deleting.",
},
)
@@ -460,7 +460,7 @@ async def list_glossary_templates():
status_code=404,
content={
"error": "TEMPLATES_NOT_FOUND",
"message": "Les templates de glossaires ne sont pas disponibles.",
"message": "Glossary templates are not available.",
},
)
@@ -494,7 +494,7 @@ async def list_glossary_templates():
status_code=500,
content={
"error": "TEMPLATES_ERROR",
"message": "Une erreur est survenue lors de la récupération des templates.",
"message": "An error occurred while retrieving templates.",
},
)
@@ -533,7 +533,7 @@ async def import_glossary_template(
status_code=404,
content={
"error": "TEMPLATES_NOT_FOUND",
"message": "Les templates de glossaires ne sont pas disponibles.",
"message": "Glossary templates are not available.",
},
)
@@ -546,7 +546,7 @@ async def import_glossary_template(
status_code=404,
content={
"error": "TEMPLATE_NOT_FOUND",
"message": f"Template '{template_id}' introuvable. Templates disponibles: {', '.join(categories.keys())}",
"message": f"Template '{template_id}' not found. Available templates: {', '.join(categories.keys())}",
},
)
@@ -559,7 +559,7 @@ async def import_glossary_template(
status_code=404,
content={
"error": "TEMPLATE_FILE_NOT_FOUND",
"message": f"Le fichier de template '{template_file}' est introuvable.",
"message": f"Template file '{template_file}' not found.",
},
)
@@ -572,7 +572,7 @@ async def import_glossary_template(
status_code=400,
content={
"error": "TERMS_LIMIT_EXCEEDED",
"message": f"Le template contient {len(terms)} termes, ce qui dépasse la limite de {MAX_TERMS_PER_GLOSSARY}.",
"message": f"The template contains {len(terms)} terms, exceeding the limit of {MAX_TERMS_PER_GLOSSARY}.",
},
)
@@ -620,6 +620,6 @@ async def import_glossary_template(
status_code=500,
content={
"error": "IMPORT_ERROR",
"message": "Une erreur est survenue lors de l'import du template.",
"message": "An error occurred while importing the template.",
},
)