Files
SaaS-PDF/backend/config/__init__.py
Your Name 957d37838c تم الانتهاء من آخر دفعة تحسينات على المشروع، وتشمل:
تحويل لوحة الإدارة الداخلية من secret header إلى session auth حقيقي مع صلاحيات admin.
إضافة دعم إدارة الأدوار من داخل لوحة الإدارة نفسها، مع حماية الحسابات المعتمدة عبر INTERNAL_ADMIN_EMAILS.
تحسين بيانات المستخدم في الواجهة والباكند لتشمل role وis_allowlisted_admin.
إضافة اختبار frontend مخصص لصفحة /internal/admin بدل الاعتماد فقط على build واختبار routes.
تحسين إضافي في الأداء عبر إزالة الاعتماد على pdfjs-dist/pdf.worker في عدّ صفحات PDF واستبداله بمسار أخف باستخدام pdf-lib.
تحسين تقسيم الـ chunks في build لتقليل أثر الحزم الكبيرة وفصل أجزاء مثل network, icons, pdf-core, وeditor.
التحقق الذي تم:

نجاح build للواجهة.
نجاح اختبار صفحة الإدارة الداخلية في frontend.
نجاح اختبارات auth/admin في backend.
نجاح full backend suite مسبقًا مع EXIT:0.
ولو تريد نسخة أقصر جدًا، استخدم هذه:

آخر التحديثات:
تم تحسين نظام الإدارة الداخلية ليعتمد على صلاحيات وجلسات حقيقية بدل secret header، مع إضافة إدارة أدوار من لوحة admin نفسها، وإضافة اختبارات frontend مخصصة للوحة، وتحسين أداء الواجهة عبر إزالة pdf.worker وتحسين تقسيم الـ chunks في build. جميع الاختبارات والتحققات الأساسية المطلوبة نجح
2026-03-16 13:50:45 +02:00

187 lines
6.7 KiB
Python

import os
from datetime import timedelta
from dotenv import load_dotenv
BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
REPO_ROOT = os.path.abspath(os.path.join(BASE_DIR, ".."))
# Load the repository-level .env first because the documented setup stores it there.
load_dotenv(os.path.join(REPO_ROOT, ".env"))
load_dotenv(os.path.join(BASE_DIR, ".env"), override=False)
def _parse_csv_env(name: str) -> tuple[str, ...]:
raw_value = os.getenv(name, "")
return tuple(item.strip().lower() for item in raw_value.split(",") if item.strip())
class BaseConfig:
"""Base configuration."""
SECRET_KEY = os.getenv("SECRET_KEY", "change-me-in-production")
INTERNAL_ADMIN_SECRET = os.getenv("INTERNAL_ADMIN_SECRET", "")
INTERNAL_ADMIN_EMAILS = _parse_csv_env("INTERNAL_ADMIN_EMAILS")
# File upload settings
MAX_CONTENT_LENGTH = int(
os.getenv("ABSOLUTE_MAX_CONTENT_LENGTH_MB", 100)
) * 1024 * 1024
UPLOAD_FOLDER = os.getenv("UPLOAD_FOLDER", "/tmp/uploads")
OUTPUT_FOLDER = os.getenv("OUTPUT_FOLDER", "/tmp/outputs")
FILE_EXPIRY_SECONDS = int(os.getenv("FILE_EXPIRY_SECONDS", 1800))
DATABASE_PATH = os.getenv(
"DATABASE_PATH", os.path.join(BASE_DIR, "data", "saas_pdf.db")
)
PERMANENT_SESSION_LIFETIME = timedelta(days=30)
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_SECURE = False
# Allowed file extensions and MIME types
ALLOWED_EXTENSIONS = {
"pdf": ["application/pdf"],
"doc": ["application/msword"],
"docx": [
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
],
"html": ["text/html", "application/xhtml+xml"],
"htm": ["text/html", "application/xhtml+xml"],
"png": ["image/png"],
"jpg": ["image/jpeg"],
"jpeg": ["image/jpeg"],
"webp": ["image/webp"],
"tiff": ["image/tiff"],
"bmp": ["image/bmp"],
"mp4": ["video/mp4"],
"webm": ["video/webm"],
"pptx": [
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
],
"ppt": ["application/vnd.ms-powerpoint"],
"xlsx": [
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
],
"xls": ["application/vnd.ms-excel"],
}
# File size limits per type (bytes)
FILE_SIZE_LIMITS = {
"pdf": 20 * 1024 * 1024, # 20MB
"doc": 15 * 1024 * 1024, # 15MB
"docx": 15 * 1024 * 1024, # 15MB
"html": 10 * 1024 * 1024, # 10MB
"htm": 10 * 1024 * 1024, # 10MB
"png": 10 * 1024 * 1024, # 10MB
"jpg": 10 * 1024 * 1024, # 10MB
"jpeg": 10 * 1024 * 1024, # 10MB
"webp": 10 * 1024 * 1024, # 10MB
"tiff": 15 * 1024 * 1024, # 15MB
"bmp": 15 * 1024 * 1024, # 15MB
"mp4": 50 * 1024 * 1024, # 50MB
"webm": 50 * 1024 * 1024, # 50MB
"pptx": 20 * 1024 * 1024, # 20MB
"ppt": 20 * 1024 * 1024, # 20MB
"xlsx": 15 * 1024 * 1024, # 15MB
"xls": 15 * 1024 * 1024, # 15MB
}
# Redis
REDIS_URL = os.getenv("REDIS_URL", "redis://redis:6379/0")
# Celery
CELERY_BROKER_URL = os.getenv("CELERY_BROKER_URL", "redis://redis:6379/0")
CELERY_RESULT_BACKEND = os.getenv("CELERY_RESULT_BACKEND", "redis://redis:6379/1")
# AWS S3
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
AWS_S3_BUCKET = os.getenv("AWS_S3_BUCKET", "saas-pdf-temp-files")
AWS_S3_REGION = os.getenv("AWS_S3_REGION", "eu-west-1")
# CORS
CORS_ORIGINS = os.getenv("CORS_ORIGINS", "http://localhost:5173").split(",")
# Rate Limiting
RATELIMIT_STORAGE_URI = os.getenv("REDIS_URL", "redis://redis:6379/0")
RATELIMIT_DEFAULT = "100/hour"
# OpenRouter AI
OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY", "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")
OPENROUTER_MODEL = os.getenv("OPENROUTER_MODEL", "nvidia/nemotron-3-super-120b-a12b:free")
OPENROUTER_BASE_URL = os.getenv(
"OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1/chat/completions"
)
# SMTP (for password reset emails)
SMTP_HOST = os.getenv("SMTP_HOST", "")
SMTP_PORT = int(os.getenv("SMTP_PORT", 587))
SMTP_USER = os.getenv("SMTP_USER", "")
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
SMTP_FROM = os.getenv("SMTP_FROM", "noreply@saas-pdf.com")
SMTP_USE_TLS = os.getenv("SMTP_USE_TLS", "true").lower() == "true"
FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:5173")
# Stripe
STRIPE_SECRET_KEY = os.getenv("STRIPE_SECRET_KEY", "")
STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET", "")
STRIPE_PRICE_ID_PRO_MONTHLY = os.getenv("STRIPE_PRICE_ID_PRO_MONTHLY", "")
STRIPE_PRICE_ID_PRO_YEARLY = os.getenv("STRIPE_PRICE_ID_PRO_YEARLY", "")
# Sentry
SENTRY_DSN = os.getenv("SENTRY_DSN", "")
SENTRY_ENVIRONMENT = os.getenv("SENTRY_ENVIRONMENT", "development")
# Site domain
SITE_DOMAIN = os.getenv("SITE_DOMAIN", "https://saas-pdf.com")
# PostgreSQL (production) — set DATABASE_URL to use PG instead of SQLite
DATABASE_URL = os.getenv("DATABASE_URL", "")
# Feature flags (default: enabled — set to "false" to disable a feature)
FEATURE_EDITOR = os.getenv("FEATURE_EDITOR", "true").lower() == "true"
FEATURE_OCR = os.getenv("FEATURE_OCR", "true").lower() == "true"
FEATURE_REMOVEBG = os.getenv("FEATURE_REMOVEBG", "true").lower() == "true"
class DevelopmentConfig(BaseConfig):
"""Development configuration."""
DEBUG = True
TESTING = False
class ProductionConfig(BaseConfig):
"""Production configuration."""
DEBUG = False
TESTING = False
SESSION_COOKIE_SECURE = True
# Stricter rate limits in production
RATELIMIT_DEFAULT = "60/hour"
class TestingConfig(BaseConfig):
"""Testing configuration."""
DEBUG = True
TESTING = True
UPLOAD_FOLDER = "/tmp/test_uploads"
OUTPUT_FOLDER = "/tmp/test_outputs"
DATABASE_PATH = "/tmp/test_saas_pdf.db"
FEATURE_EDITOR = False
FEATURE_OCR = False
FEATURE_REMOVEBG = False
# Disable Redis-backed rate limiting; use in-memory instead
RATELIMIT_STORAGE_URI = "memory://"
RATELIMIT_ENABLED = False
# Use in-memory transport for Celery so tests don't need Redis
CELERY_BROKER_URL = "memory://"
CELERY_RESULT_BACKEND = "cache+memory://"
REDIS_URL = "memory://"
config = {
"development": DevelopmentConfig,
"production": ProductionConfig,
"testing": TestingConfig,
"default": DevelopmentConfig,
}