Files
sentinel-home-ai/fam-core/src/fam_core/app.py

92 lines
3.1 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.
"""
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 .static_app import static_bp
logger = setup_logger('fam-core.app')
app = Flask(__name__)
# 注册蓝图:/api/* 系列必须先于 static_bp 注册——static_bp 是通配兜底路由
# Vue Router history 模式回退 index.html排在前面会吞掉 API 请求。
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(static_bp)
# 健康检查
@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}")
# 初始化运动监测通知服务Webhook 驱动,不轮询;提供推送客户端 + 摄像头名映射)
_notifier = None
try:
_notifier = get_motion_notifier()
_notifier.start()
# 启动时一次性从 SS 拉取摄像头名->id 映射非轮询失败仅告警Webhook 仍可运行
try:
_notifier.refresh_camera_map()
logger.info("MotionNotifier 摄像头映射已加载")
except Exception as e:
logger.warning(f"摄像头映射加载失败Webhook 仍可运行camera_id 可能为空): {e}")
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
if __name__ == '__main__':
cfg = load_config()
port = cfg.get('server', {}).get('port', 8000)
app.run(host='0.0.0.0', port=port, debug=False)