中控总览改为后台常驻并行拉取热缓存,浏览器只读缓存并经 SSE 更新。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,8 +3,6 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from functools import wraps
|
||||
from pathlib import Path
|
||||
|
||||
@@ -19,6 +17,7 @@ from flask import (
|
||||
request,
|
||||
send_file,
|
||||
session,
|
||||
stream_with_context,
|
||||
url_for,
|
||||
)
|
||||
|
||||
@@ -62,6 +61,7 @@ from lib.instances_lib import (
|
||||
list_instances,
|
||||
update_instance,
|
||||
)
|
||||
from lib.overview_cache_lib import overview_store
|
||||
from lib.sso_lib import build_embed_open_url
|
||||
from lib.stats_agg_lib import build_stats_curve, build_stats_summary, normalize_window
|
||||
from lib.wechat_lib import (
|
||||
@@ -486,38 +486,54 @@ def desk():
|
||||
)
|
||||
|
||||
|
||||
def _on_overview_pulled(rows: list) -> None:
|
||||
"""常驻 pull 回调:健康巡检 + 余额采样(与浏览器是否打开无关)."""
|
||||
try:
|
||||
inspect_overview_rows(rows)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
record_cash_samples_from_overview(rows)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@app.route("/api/overview")
|
||||
@login_required
|
||||
def api_overview():
|
||||
rows = pull_all_enabled()
|
||||
return jsonify({"ok": True, "instances": rows})
|
||||
"""只读热缓存;不在请求内现场拉实例(后台常驻并行 pull)."""
|
||||
force = (request.args.get("force") or "").strip().lower() in ("1", "true", "yes")
|
||||
if force:
|
||||
overview_store.request_refresh()
|
||||
return jsonify(overview_store.snapshot_dict())
|
||||
|
||||
|
||||
def _health_pull_interval_sec() -> float:
|
||||
try:
|
||||
return max(5.0, float(os.getenv("HUB_PULL_INTERVAL_SEC") or 10))
|
||||
except (TypeError, ValueError):
|
||||
return 10.0
|
||||
@app.route("/api/overview/refresh", methods=["POST"])
|
||||
@login_required
|
||||
def api_overview_refresh():
|
||||
overview_store.request_refresh()
|
||||
return jsonify({"ok": True, "msg": "已触发后台刷新"})
|
||||
|
||||
|
||||
def _health_watch_loop() -> None:
|
||||
"""后台巡检:即使无人打开总览也会告警;并采样余额曲线."""
|
||||
while True:
|
||||
try:
|
||||
rows = pull_all_enabled()
|
||||
inspect_overview_rows(rows)
|
||||
record_cash_samples_from_overview(rows)
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(_health_pull_interval_sec())
|
||||
@app.route("/api/overview/stream")
|
||||
@login_required
|
||||
def api_overview_stream():
|
||||
return Response(
|
||||
stream_with_context(overview_store.iter_sse()),
|
||||
mimetype="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"Connection": "keep-alive",
|
||||
"X-Accel-Buffering": "no",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def start_health_watch() -> None:
|
||||
t = threading.Thread(target=_health_watch_loop, name="hub-health-watch", daemon=True)
|
||||
t.start()
|
||||
def start_overview_cache() -> None:
|
||||
overview_store.start(pull_all_enabled, on_pull=_on_overview_pulled)
|
||||
|
||||
|
||||
start_health_watch()
|
||||
start_overview_cache()
|
||||
|
||||
try:
|
||||
start_hub_backup_scheduler(
|
||||
|
||||
Reference in New Issue
Block a user