feat: 增加运行日志(不记录敏感信息) - 启动摘要:端口/间隔/重试/超时/并发/Telegram 状态/内置平台/账号数/数据库路径 - 检查结果:成功余额、认证失败禁用、失败原因 - 提醒状态机:触发提醒/余额恢复 - API 操作:登录、平台/账号增删改、设置修改、密码修改 - Telegram 发送成功、请求耗时(DEBUG) - 全部日志不含 apikey/token/密码
This commit is contained in:
@@ -52,8 +52,10 @@ def evaluate(account: dict, platform: dict, balance: float, cfg: Config) -> None
|
|||||||
below = balance < threshold
|
below = balance < threshold
|
||||||
if below and armed:
|
if below and armed:
|
||||||
kind = "below"
|
kind = "below"
|
||||||
|
logger.info("触发提醒: %s / %s 余额 %s < 阈值 %s(%s)", platform["name"], account["name"], _fmt(balance), _fmt(threshold), platform["currency"])
|
||||||
elif not below and not armed:
|
elif not below and not armed:
|
||||||
kind = "recovered"
|
kind = "recovered"
|
||||||
|
logger.info("余额恢复: %s / %s 余额 %s >= 阈值 %s(%s)", platform["name"], account["name"], _fmt(balance), _fmt(threshold), platform["currency"])
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
message = build_alert_message(kind, account, platform, balance)
|
message = build_alert_message(kind, account, platform, balance)
|
||||||
|
|||||||
+10
@@ -118,9 +118,11 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
def login(body: LoginRequest):
|
def login(body: LoginRequest):
|
||||||
import time
|
import time
|
||||||
if body.password != cfg.password:
|
if body.password != cfg.password:
|
||||||
|
logger.warning("登录失败:密码错误")
|
||||||
raise HTTPException(status_code=401, detail="密码错误")
|
raise HTTPException(status_code=401, detail="密码错误")
|
||||||
token = secrets.token_urlsafe(32)
|
token = secrets.token_urlsafe(32)
|
||||||
_tokens[token] = time.time() + TOKEN_TTL
|
_tokens[token] = time.time() + TOKEN_TTL
|
||||||
|
logger.info("登录成功")
|
||||||
return {"token": token}
|
return {"token": token}
|
||||||
|
|
||||||
@app.post("/api/logout")
|
@app.post("/api/logout")
|
||||||
@@ -172,6 +174,7 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
if "UNIQUE" in str(exc):
|
if "UNIQUE" in str(exc):
|
||||||
raise HTTPException(status_code=409, detail="平台名称已存在")
|
raise HTTPException(status_code=409, detail="平台名称已存在")
|
||||||
raise HTTPException(status_code=400, detail=str(exc))
|
raise HTTPException(status_code=400, detail=str(exc))
|
||||||
|
logger.info("创建平台: %s (id=%s, provider=%s)", body.name, pid, body.provider_id)
|
||||||
return {"id": pid}
|
return {"id": pid}
|
||||||
|
|
||||||
@app.put("/api/platforms/{pid}", dependencies=[Depends(require_auth)])
|
@app.put("/api/platforms/{pid}", dependencies=[Depends(require_auth)])
|
||||||
@@ -219,6 +222,7 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
raise HTTPException(status_code=400, detail=str(exc))
|
raise HTTPException(status_code=400, detail=str(exc))
|
||||||
if cur.rowcount == 0:
|
if cur.rowcount == 0:
|
||||||
raise HTTPException(status_code=404, detail="平台不存在")
|
raise HTTPException(status_code=404, detail="平台不存在")
|
||||||
|
logger.info("更新平台 id=%s 字段: %s", pid, ", ".join(fields))
|
||||||
return {"id": pid}
|
return {"id": pid}
|
||||||
|
|
||||||
@app.delete("/api/platforms/{pid}", dependencies=[Depends(require_auth)])
|
@app.delete("/api/platforms/{pid}", dependencies=[Depends(require_auth)])
|
||||||
@@ -227,6 +231,7 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
cur = conn.execute("DELETE FROM platforms WHERE id=?", (pid,))
|
cur = conn.execute("DELETE FROM platforms WHERE id=?", (pid,))
|
||||||
if cur.rowcount == 0:
|
if cur.rowcount == 0:
|
||||||
raise HTTPException(status_code=404, detail="平台不存在")
|
raise HTTPException(status_code=404, detail="平台不存在")
|
||||||
|
logger.info("删除平台 id=%s", pid)
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
# ---------- 账号 ----------
|
# ---------- 账号 ----------
|
||||||
@@ -267,6 +272,7 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
aid = cur.lastrowid
|
aid = cur.lastrowid
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise HTTPException(status_code=400, detail=str(exc))
|
raise HTTPException(status_code=400, detail=str(exc))
|
||||||
|
logger.info("创建账号: %s (id=%s, 平台id=%s)", body.name, aid, body.platform_id)
|
||||||
return {"id": aid}
|
return {"id": aid}
|
||||||
|
|
||||||
@app.put("/api/accounts/{aid}", dependencies=[Depends(require_auth)])
|
@app.put("/api/accounts/{aid}", dependencies=[Depends(require_auth)])
|
||||||
@@ -300,6 +306,7 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
)
|
)
|
||||||
if cur.rowcount == 0:
|
if cur.rowcount == 0:
|
||||||
raise HTTPException(status_code=404, detail="账号不存在")
|
raise HTTPException(status_code=404, detail="账号不存在")
|
||||||
|
logger.info("更新账号 id=%s 字段: %s", aid, ", ".join(fields))
|
||||||
return {"id": aid}
|
return {"id": aid}
|
||||||
|
|
||||||
@app.delete("/api/accounts/{aid}", dependencies=[Depends(require_auth)])
|
@app.delete("/api/accounts/{aid}", dependencies=[Depends(require_auth)])
|
||||||
@@ -308,6 +315,7 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
cur = conn.execute("DELETE FROM accounts WHERE id=?", (aid,))
|
cur = conn.execute("DELETE FROM accounts WHERE id=?", (aid,))
|
||||||
if cur.rowcount == 0:
|
if cur.rowcount == 0:
|
||||||
raise HTTPException(status_code=404, detail="账号不存在")
|
raise HTTPException(status_code=404, detail="账号不存在")
|
||||||
|
logger.info("删除账号 id=%s", aid)
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
@app.post("/api/accounts/{aid}/check", dependencies=[Depends(require_auth)])
|
@app.post("/api/accounts/{aid}/check", dependencies=[Depends(require_auth)])
|
||||||
@@ -392,6 +400,7 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
changed = True
|
changed = True
|
||||||
if changed:
|
if changed:
|
||||||
save_config(cfg)
|
save_config(cfg)
|
||||||
|
logger.info("设置已更新: %s", ", ".join(f for f in ("global_interval_seconds", "retry_count", "timeout_seconds", "max_workers", "telegram_bot_token", "telegram_chat_id") if getattr(body, f) is not None))
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
@app.post("/api/settings/password", dependencies=[Depends(require_auth)])
|
@app.post("/api/settings/password", dependencies=[Depends(require_auth)])
|
||||||
@@ -400,6 +409,7 @@ def create_app(cfg: Config | None = None) -> FastAPI:
|
|||||||
raise HTTPException(status_code=401, detail="旧密码错误")
|
raise HTTPException(status_code=401, detail="旧密码错误")
|
||||||
cfg.password = body.new_password
|
cfg.password = body.new_password
|
||||||
save_config(cfg)
|
save_config(cfg)
|
||||||
|
logger.info("管理密码已修改")
|
||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
# ---------- 静态前端 ----------
|
# ---------- 静态前端 ----------
|
||||||
|
|||||||
+3
-1
@@ -60,13 +60,15 @@ def fetch_balance(
|
|||||||
headers["Content-Type"] = "application/json"
|
headers["Content-Type"] = "application/json"
|
||||||
attempts = retry_count + 1
|
attempts = retry_count + 1
|
||||||
for attempt in range(attempts):
|
for attempt in range(attempts):
|
||||||
|
t0 = time.time()
|
||||||
try:
|
try:
|
||||||
if method == "GET":
|
if method == "GET":
|
||||||
resp = requests.get(url, headers=headers, timeout=timeout)
|
resp = requests.get(url, headers=headers, timeout=timeout)
|
||||||
else:
|
else:
|
||||||
resp = requests.post(url, headers=headers, data=body, timeout=timeout)
|
resp = requests.post(url, headers=headers, data=body, timeout=timeout)
|
||||||
|
logger.debug("请求完成 %s -> %s(%.0fms)", url, resp.status_code, (time.time() - t0) * 1000)
|
||||||
except requests.RequestException as exc:
|
except requests.RequestException as exc:
|
||||||
logger.warning("请求异常(%s/%s) %s: %s", attempt + 1, attempts, url, exc)
|
logger.warning("请求异常(%s/%s) %s: %s(%.0fms)", attempt + 1, attempts, url, exc, (time.time() - t0) * 1000)
|
||||||
if attempt < attempts - 1:
|
if attempt < attempts - 1:
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -182,9 +182,13 @@ class Monitor:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if result.auth_error:
|
if result.auth_error:
|
||||||
|
logger.warning("认证失败已禁用: %s / %s(%s)", platform["name"], account["name"], result.error)
|
||||||
notify_disabled(account, platform, cfg)
|
notify_disabled(account, platform, cfg)
|
||||||
elif result.ok:
|
elif result.ok:
|
||||||
|
logger.info("检查成功: %s / %s 余额 %.4f %s", platform["name"], account["name"], result.balance, platform["currency"])
|
||||||
evaluate(account, platform, result.balance, cfg)
|
evaluate(account, platform, result.balance, cfg)
|
||||||
|
else:
|
||||||
|
logger.warning("检查失败: %s / %s -> %s", platform["name"], account["name"], result.error)
|
||||||
|
|
||||||
# 调度:无论成功失败,按间隔排下一次
|
# 调度:无论成功失败,按间隔排下一次
|
||||||
with self._lock:
|
with self._lock:
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ def send_message(bot_token: str, chat_id: str, text: str) -> bool:
|
|||||||
timeout=10,
|
timeout=10,
|
||||||
)
|
)
|
||||||
if resp.status_code == 200:
|
if resp.status_code == 200:
|
||||||
|
logger.info("Telegram 消息已发送(%d 字符)", len(text))
|
||||||
return True
|
return True
|
||||||
logger.error("Telegram 发送失败 status=%s body=%s", resp.status_code, resp.text[:200])
|
logger.error("Telegram 发送失败 status=%s body=%s", resp.status_code, resp.text[:200])
|
||||||
except requests.RequestException as exc:
|
except requests.RequestException as exc:
|
||||||
|
|||||||
@@ -6,19 +6,33 @@ import logging
|
|||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
||||||
|
from app import db
|
||||||
from app.api import create_app
|
from app.api import create_app
|
||||||
from app.config import load_config
|
from app.config import load_config
|
||||||
|
from app.providers import list_providers
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
||||||
)
|
)
|
||||||
|
logger = logging.getLogger("monitor.main")
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
app = create_app(cfg)
|
app = create_app(cfg)
|
||||||
print(f"AI Balance Monitor 已启动: http://127.0.0.1:{cfg.port}")
|
|
||||||
|
tg = "已配置" if cfg.telegram_bot_token and cfg.telegram_chat_id else "未配置"
|
||||||
|
with db.get_conn() as conn:
|
||||||
|
total = conn.execute("SELECT COUNT(*) FROM accounts").fetchone()[0]
|
||||||
|
enabled = conn.execute("SELECT COUNT(*) FROM accounts WHERE enabled=1").fetchone()[0]
|
||||||
|
logger.info("========== AI Balance Monitor 启动 ==========")
|
||||||
|
logger.info("端口 %s | 全局间隔 %ss | 重试 %s | 超时 %ss | 并发 %s", cfg.port, cfg.global_interval_seconds, cfg.retry_count, cfg.timeout_seconds, cfg.max_workers)
|
||||||
|
logger.info("Telegram 提醒: %s", tg)
|
||||||
|
logger.info("内置平台: %s", ", ".join(p["name"] for p in list_providers()))
|
||||||
|
logger.info("账号: %s 个(启用 %s)| 数据库: %s", total, enabled, db.DB_PATH)
|
||||||
|
logger.info("面板地址: http://127.0.0.1:%s", cfg.port)
|
||||||
|
|
||||||
uvicorn.run(app, host="0.0.0.0", port=cfg.port, log_level="warning")
|
uvicorn.run(app, host="0.0.0.0", port=cfg.port, log_level="warning")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user