Files
iot-dboard/start.py
T
2026-08-12 09:50:37 +08:00

77 lines
2.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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)
# 跨 worker SSE 广播(PG LISTEN/NOTIFY):控制端的事件能到达任意 worker 的显示端
app.event_bus.attach(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)