"""Production WSGI entrypoint for Gunicorn (and plain `python start.py`). - Creates the Flask app and initializes the PostgreSQL schema (with the schema-version migration gate). - Starts the background scheduler ONLY in the process that wins the PostgreSQL advisory lock, so multiple Gunicorn workers never duplicate alarms / reminders / weather jobs. - Prints the Root temp password (when one was generated or is still pending). Gunicorn usage (see Dockerfile / docker-compose.yml): gunicorn --workers 2 --threads 2 --bind 0.0.0.0:5000 start:app """ import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from backend.app import create_app from backend.scheduler import ( start_scheduler, stop_scheduler, try_acquire_scheduler_lock, start_watchdog, ) from backend.models import _EMITTED_PASSWORDS, get_auth_config # PostgreSQL connection string (kept as the historical DB_PATH config key). db_url = os.environ.get("DATABASE_URL", "postgresql://iot:iot@db:5432/iot_dboard") app = create_app(db_path=db_url) def _print_temp_password(): with app.app_context(): if not _EMITTED_PASSWORDS: cfg = get_auth_config() pw = cfg.get("temp_password", "") if pw and cfg.get("must_configure_mfa"): print(f"\n{'='*50}", file=sys.stderr) print(f" 🔐 临时密码: {pw}", file=sys.stderr) print(f" ⚠️ 请使用此密码登录并配置双因素认证", file=sys.stderr) print(f"{'='*50}\n", file=sys.stderr) else: for pw in _EMITTED_PASSWORDS: print(f"\n{'='*50}", file=sys.stderr) print(f" 🔐 Root 初始临时密码: {pw}", file=sys.stderr) print(f" ⚠️ 请使用此密码登录 Root 并配置双因素认证", file=sys.stderr) print(f"{'='*50}\n", file=sys.stderr) _print_temp_password() def _maybe_start_scheduler(): """Start the scheduler if this process wins the advisory lock.""" if os.environ.get("FLASK_DISABLE_SCHEDULER", "").lower() in ("1", "true", "yes"): return if try_acquire_scheduler_lock(db_url): start_scheduler(app) app.logger.info("Scheduler started (advisory lock acquired)") _maybe_start_scheduler() if os.environ.get("FLASK_DISABLE_SCHEDULER", "").lower() not in ("1", "true", "yes"): start_watchdog(app, db_url) if __name__ == "__main__": import atexit atexit.register(stop_scheduler) DEBUG = os.environ.get("FLASK_DEBUG", "").lower() in ("1", "true", "yes") bind_host = os.environ.get("FLASK_BIND", "127.0.0.1") # 开发模式使用 5001,避免与生产 Gunicorn(0.0.0.0:5000)抢端口。 app.run(host=bind_host, port=int(os.environ.get("FLASK_PORT", "5001")), debug=DEBUG, threaded=True)