"""Flask Application Factory.""" import os from flask import Flask, jsonify 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 from app.utils.csrf import CSRFError, apply_csrf_cookie, should_enforce_csrf, validate_csrf_request 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) if not app.config.get("DATABASE_URL"): 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", ) @app.before_request def enforce_csrf(): if not should_enforce_csrf(): return None try: validate_csrf_request() except CSRFError as exc: return jsonify({"error": exc.message}), exc.status_code return None @app.after_request def sync_csrf_cookie(response): return apply_csrf_cookie(response) # 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 from app.routes.sitemap import sitemap_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") app.register_blueprint(sitemap_bp) return app