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

@@ -57,10 +57,12 @@ class FileValidator:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
"application/pdf": ".pdf",
}
# Magic bytes for Office Open XML files (ZIP format)
OFFICE_MAGIC_BYTES = b"PK\x03\x04"
PDF_MAGIC_BYTES = b"%PDF"
def __init__(
self,
@@ -70,7 +72,7 @@ class FileValidator:
):
self.max_size_bytes = max_size_mb * 1024 * 1024
self.max_size_mb = max_size_mb
self.allowed_extensions = allowed_extensions or {".xlsx", ".docx", ".pptx"}
self.allowed_extensions = allowed_extensions or {".xlsx", ".docx", ".pptx", ".pdf"}
self.scan_content = scan_content
async def validate_async(self, file: UploadFile) -> ValidationResult:
@@ -281,11 +283,20 @@ class FileValidator:
def _validate_magic_bytes(self, content: bytes, extension: str):
"""Validate file magic bytes match expected format"""
# All supported formats are Office Open XML (ZIP-based)
# PDF files start with %PDF
if extension.lower() == ".pdf":
if not content.startswith(self.PDF_MAGIC_BYTES):
raise ValidationError(
"File content does not match expected PDF format. "
"The file may be corrupted.",
code="invalid_file_content",
)
return
# Office files are ZIP-based
if not content.startswith(self.OFFICE_MAGIC_BYTES):
raise ValidationError(
"Le contenu du fichier ne correspond pas au format attendu. "
"Le fichier est peut-etre corrompu ou n'est pas un document Office valide.",
"File content does not match expected Office format. "
"The file may be corrupted or not a valid Office document.",
code="invalid_file_content",
)