تحويل لوحة الإدارة الداخلية من 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. جميع الاختبارات والتحققات الأساسية المطلوبة نجح
179 lines
6.9 KiB
Python
179 lines
6.9 KiB
Python
"""Flask Application Factory."""
|
|
import os
|
|
|
|
from flask import Flask
|
|
|
|
from config import config
|
|
from app.extensions import cors, limiter, talisman, init_celery
|
|
from app.services.account_service import init_account_db
|
|
from app.services.rating_service import init_ratings_db
|
|
from app.services.ai_cost_service import init_ai_cost_db
|
|
from app.services.site_assistant_service import init_site_assistant_db
|
|
from app.services.contact_service import init_contact_db
|
|
from app.services.stripe_service import init_stripe_db
|
|
|
|
|
|
def _init_sentry(app):
|
|
"""Initialize Sentry error monitoring if DSN is configured."""
|
|
dsn = app.config.get("SENTRY_DSN", "")
|
|
if not dsn:
|
|
return
|
|
try:
|
|
import sentry_sdk
|
|
from sentry_sdk.integrations.flask import FlaskIntegration
|
|
from sentry_sdk.integrations.celery import CeleryIntegration
|
|
|
|
sentry_sdk.init(
|
|
dsn=dsn,
|
|
environment=app.config.get("SENTRY_ENVIRONMENT", "development"),
|
|
integrations=[FlaskIntegration(), CeleryIntegration()],
|
|
traces_sample_rate=0.1,
|
|
send_default_pii=False,
|
|
)
|
|
except ImportError:
|
|
app.logger.warning("sentry-sdk not installed — monitoring disabled.")
|
|
|
|
|
|
def create_app(config_name=None):
|
|
"""Create and configure the Flask application."""
|
|
if config_name is None:
|
|
config_name = os.getenv("FLASK_ENV", "development")
|
|
|
|
app = Flask(__name__)
|
|
app.config.from_object(config[config_name])
|
|
|
|
# Initialize Sentry early
|
|
_init_sentry(app)
|
|
|
|
# Create upload/output/database directories
|
|
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
|
|
os.makedirs(app.config["OUTPUT_FOLDER"], exist_ok=True)
|
|
db_dir = os.path.dirname(app.config["DATABASE_PATH"])
|
|
if db_dir:
|
|
os.makedirs(db_dir, exist_ok=True)
|
|
|
|
# Initialize extensions
|
|
cors.init_app(
|
|
app,
|
|
origins=app.config["CORS_ORIGINS"],
|
|
supports_credentials=True,
|
|
)
|
|
|
|
limiter.init_app(app)
|
|
|
|
# Talisman security headers (relaxed CSP for AdSense)
|
|
csp = {
|
|
"default-src": "'self'",
|
|
"script-src": [
|
|
"'self'",
|
|
"'unsafe-inline'",
|
|
"https://pagead2.googlesyndication.com",
|
|
"https://www.googletagmanager.com",
|
|
"https://www.google-analytics.com",
|
|
],
|
|
"style-src": ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
|
"font-src": ["'self'", "https://fonts.gstatic.com"],
|
|
"img-src": [
|
|
"'self'",
|
|
"data:",
|
|
"https://pagead2.googlesyndication.com",
|
|
"https://tpc.googlesyndication.com",
|
|
"https://www.google-analytics.com",
|
|
],
|
|
"frame-src": [
|
|
"https://googleads.g.doubleclick.net",
|
|
"https://tpc.googlesyndication.com",
|
|
],
|
|
"connect-src": [
|
|
"'self'",
|
|
"https://www.google-analytics.com",
|
|
"https://pagead2.googlesyndication.com",
|
|
"https://*.amazonaws.com",
|
|
],
|
|
}
|
|
talisman.init_app(
|
|
app,
|
|
content_security_policy=csp,
|
|
force_https=config_name == "production",
|
|
)
|
|
|
|
# Initialize Celery
|
|
init_celery(app)
|
|
|
|
with app.app_context():
|
|
init_account_db()
|
|
init_ratings_db()
|
|
init_ai_cost_db()
|
|
init_site_assistant_db()
|
|
init_contact_db()
|
|
init_stripe_db()
|
|
|
|
# Register blueprints
|
|
from app.routes.health import health_bp
|
|
from app.routes.auth import auth_bp
|
|
from app.routes.account import account_bp
|
|
from app.routes.admin import admin_bp
|
|
from app.routes.convert import convert_bp
|
|
from app.routes.compress import compress_bp
|
|
from app.routes.image import image_bp
|
|
from app.routes.video import video_bp
|
|
from app.routes.history import history_bp
|
|
from app.routes.tasks import tasks_bp
|
|
from app.routes.download import download_bp
|
|
from app.routes.pdf_tools import pdf_tools_bp
|
|
from app.routes.flowchart import flowchart_bp
|
|
from app.routes.v1.tools import v1_bp
|
|
from app.routes.config import config_bp
|
|
from app.routes.ocr import ocr_bp
|
|
from app.routes.removebg import removebg_bp
|
|
from app.routes.pdf_editor import pdf_editor_bp
|
|
from app.routes.compress_image import compress_image_bp
|
|
from app.routes.pdf_to_excel import pdf_to_excel_bp
|
|
from app.routes.qrcode import qrcode_bp
|
|
from app.routes.html_to_pdf import html_to_pdf_bp
|
|
from app.routes.pdf_ai import pdf_ai_bp
|
|
from app.routes.rating import rating_bp
|
|
from app.routes.assistant import assistant_bp
|
|
from app.routes.contact import contact_bp
|
|
from app.routes.stripe import stripe_bp
|
|
from app.routes.stats import stats_bp
|
|
from app.routes.pdf_convert import pdf_convert_bp
|
|
from app.routes.pdf_extra import pdf_extra_bp
|
|
from app.routes.image_extra import image_extra_bp
|
|
from app.routes.barcode import barcode_bp
|
|
|
|
app.register_blueprint(health_bp, url_prefix="/api")
|
|
app.register_blueprint(auth_bp, url_prefix="/api/auth")
|
|
app.register_blueprint(account_bp, url_prefix="/api/account")
|
|
app.register_blueprint(admin_bp, url_prefix="/api/internal/admin")
|
|
app.register_blueprint(convert_bp, url_prefix="/api/convert")
|
|
app.register_blueprint(compress_bp, url_prefix="/api/compress")
|
|
app.register_blueprint(image_bp, url_prefix="/api/image")
|
|
app.register_blueprint(video_bp, url_prefix="/api/video")
|
|
app.register_blueprint(history_bp, url_prefix="/api")
|
|
app.register_blueprint(pdf_tools_bp, url_prefix="/api/pdf-tools")
|
|
app.register_blueprint(flowchart_bp, url_prefix="/api/flowchart")
|
|
app.register_blueprint(tasks_bp, url_prefix="/api/tasks")
|
|
app.register_blueprint(download_bp, url_prefix="/api/download")
|
|
app.register_blueprint(v1_bp, url_prefix="/api/v1")
|
|
app.register_blueprint(config_bp, url_prefix="/api/config")
|
|
app.register_blueprint(ocr_bp, url_prefix="/api/ocr")
|
|
app.register_blueprint(removebg_bp, url_prefix="/api/remove-bg")
|
|
app.register_blueprint(pdf_editor_bp, url_prefix="/api/pdf-editor")
|
|
app.register_blueprint(compress_image_bp, url_prefix="/api/image")
|
|
app.register_blueprint(pdf_to_excel_bp, url_prefix="/api/convert")
|
|
app.register_blueprint(qrcode_bp, url_prefix="/api/qrcode")
|
|
app.register_blueprint(html_to_pdf_bp, url_prefix="/api/convert")
|
|
app.register_blueprint(pdf_ai_bp, url_prefix="/api/pdf-ai")
|
|
app.register_blueprint(rating_bp, url_prefix="/api/ratings")
|
|
app.register_blueprint(assistant_bp, url_prefix="/api/assistant")
|
|
app.register_blueprint(contact_bp, url_prefix="/api/contact")
|
|
app.register_blueprint(stripe_bp, url_prefix="/api/stripe")
|
|
app.register_blueprint(stats_bp, url_prefix="/api/stats")
|
|
app.register_blueprint(pdf_convert_bp, url_prefix="/api/convert")
|
|
app.register_blueprint(pdf_extra_bp, url_prefix="/api/pdf-tools")
|
|
app.register_blueprint(image_extra_bp, url_prefix="/api/image")
|
|
app.register_blueprint(barcode_bp, url_prefix="/api/barcode")
|
|
|
|
return app
|