""" FAM-Core 主应用 - Flask 单进程(新架构 v2) 承载: Oracle-Sync(每 30 分钟拉取增量镜像)+ Member-Manager + Chat-Handler NAS 不再处理视频:无 Scheduler / Dispatcher / Poller / Event-Receiver / Video-Server。 所有视频分析在 Oracle 完成,NAS 仅作管理后台拉取展示,CPU 占用大幅降低。 """ import os import sys from flask import Flask, jsonify # 确保包路径 sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from .config_loader import load_config from .logger import setup_logger from .oracle_sync import get_sync from .motion_notifier.motion_notifier import get_motion_notifier from .chat_handler.chat_handler import chat_bp from .member_manager.member_manager import member_bp from .img_proxy import img_bp from .motion_bp import motion_bp from .ui_api import ui_bp from .auth import auth_bp, init_auth logger = setup_logger('fam-core.app') app = Flask(__name__) # 注册蓝图:全部 /api/* 路由(前端已迁云服务器 Caddy:80,NAS 不再托管 SPA 静态文件) app.register_blueprint(chat_bp) app.register_blueprint(member_bp) app.register_blueprint(img_bp) app.register_blueprint(motion_bp) app.register_blueprint(ui_bp) app.register_blueprint(auth_bp) # 登录校验全局拦截(页面未登录 302 /login;/api/* 未登录 401) init_auth(app) # 健康检查 @app.route('/health', methods=['GET']) def health(): return jsonify({"status": "ok", "service": "fam-core"}), 200 # 初始化后台同步线程(NAS 唯一常驻线程) _sync = None try: _sync = get_sync() _sync.start() logger.info("Oracle-Sync 已启动") # 启动后立刻拉一次,前端无需等待首个周期即有数据 try: _sync.trigger_now() logger.info("启动首次同步完成") except Exception as e: logger.warning(f"启动首次同步失败(后续周期会重试): {e}") except Exception as e: logger.error(f"Oracle-Sync 启动失败: {e}") # 初始化运动监测通知服务(NAS 轮询 SS EventCenter 主路径) _notifier = None try: _notifier = get_motion_notifier() _notifier.start() logger.info("MotionNotifier 已初始化") except Exception as e: logger.error(f"MotionNotifier 初始化失败: {e}") @app.route('/api/status', methods=['GET']) def status(): """系统状态""" return jsonify({ "service": "fam-core", "sync": _sync.status() if _sync else {"running": False, "error": "未初始化"}, "motion": _notifier.status() if _notifier else {"running": False, "error": "未初始化"}, }), 200 @app.route('/api/sync/trigger', methods=['POST']) def sync_trigger(): """手动立即触发一次甲骨文增量同步(服务状态页"立即同步"按钮)。 正常情况下后台线程每 30 分钟自动拉一次;这个接口给用户想立刻看到最新数据 时用,跟后台线程共用同一把拉取锁(oracle_sync._pull_lock),不会并发重复拉。 """ if not _sync: return jsonify({"error": "同步服务未初始化"}), 503 ok = _sync.trigger_now() if not ok: return jsonify({"status": "failed", "error": _sync.status().get("last_error")}), 502 return jsonify({"status": "ok", **_sync.status()}), 200 if __name__ == '__main__': cfg = load_config() port = cfg.get('server', {}).get('port', 8000) app.run(host='0.0.0.0', port=port, debug=False)