Spaces:
Sleeping
Sleeping
| """ | |
| Application factory. | |
| Create and configure the Flask app with all extensions, blueprints, and middleware. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| from flask import Flask | |
| from flask_wtf.csrf import CSRFProtect | |
| from flask_limiter import Limiter | |
| from flask_limiter.util import get_remote_address | |
| from app.config import get_config | |
| from app.models import db | |
| from app.middleware import register_middleware | |
| from app.utils.logging_config import configure_logging | |
| csrf = CSRFProtect() | |
| limiter = Limiter(key_func=get_remote_address) | |
| logger = logging.getLogger(__name__) | |
| def create_app(config_class=None) -> Flask: | |
| """Application factory — creates a fully configured Flask app.""" | |
| app = Flask( | |
| __name__, | |
| template_folder="templates", | |
| static_folder="static", | |
| ) | |
| # --- Load config --- | |
| cfg = config_class or get_config() | |
| app.config.from_object(cfg) | |
| cfg.init_app(app) | |
| # --- Logging --- | |
| configure_logging( | |
| log_level=app.config.get("LOG_LEVEL", "INFO"), | |
| log_dir=str(app.config.get("LOG_DIR", "logs")), | |
| ) | |
| # --- Extensions --- | |
| db.init_app(app) | |
| csrf.init_app(app) | |
| limiter.init_app(app) | |
| # --- Middleware --- | |
| register_middleware(app) | |
| # --- Blueprints --- | |
| from app.routes import main_bp, jobs_bp | |
| app.register_blueprint(main_bp) | |
| app.register_blueprint(jobs_bp) | |
| # Apply rate limiting to API routes | |
| limiter.limit("60 per minute")(jobs_bp) | |
| # --- Database init --- | |
| with app.app_context(): | |
| db.create_all() | |
| _seed_defaults() | |
| logger.info("WebScraper Platform started [%s]", app.config.get("FLASK_ENV", "development")) | |
| return app | |
| def _seed_defaults() -> None: | |
| """Seed default app settings if not present.""" | |
| from app.models import AppSetting | |
| defaults = [ | |
| ("max_concurrent_jobs", "5", "Maximum parallel scrape jobs"), | |
| ("default_delay", "1.0", "Default seconds between requests"), | |
| ("default_timeout", "30", "Default HTTP timeout in seconds"), | |
| ] | |
| for key, value, desc in defaults: | |
| if not AppSetting.query.filter_by(key=key).first(): | |
| db.session.add(AppSetting(key=key, value=value, description=desc)) | |
| db.session.commit() | |