Files
crypto_monitor_user/lib/license/flask_gate.py
T
dekun 8eefc6cfd5 feat: accept CLIENT_API_KEY on /license page.
Users can paste the key from the seller instead of editing license.env; it persists in license_state.json.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 17:20:47 +08:00

91 lines
3.0 KiB
Python

"""Flask 实例许可门禁与 /license 页。"""
from __future__ import annotations
import os
from pathlib import Path
from flask import Flask, jsonify, redirect, render_template_string, request
from lib.license.license_lib import (
get_device_id,
get_license_status,
is_license_valid,
redeem_code,
validate_license,
)
_TEMPLATE_PATH = Path(__file__).resolve().parent / "templates" / "license.html"
def _allowed_path(path: str) -> bool:
if path in ("/license", "/api/license/status", "/api/license/redeem", "/api/license/validate", "/health"):
return True
if path.startswith("/static/"):
return True
if path.startswith("/favicon"):
return True
return False
def install_license_gate(app: Flask) -> None:
"""注册 /license 与 before_request 门禁。三所 Flask 共用。"""
@app.get("/health")
def _license_health():
st = get_license_status(skip_remote=True)
return jsonify({"ok": True, "license_valid": bool(st.get("valid"))})
@app.get("/api/license/status")
def _license_status_api():
return jsonify(get_license_status())
@app.post("/api/license/redeem")
def _license_redeem_api():
data = request.get_json(silent=True) or {}
code = (data.get("code") or request.form.get("code") or "").strip()
ckey = (data.get("client_api_key") or request.form.get("client_api_key") or "").strip()
return jsonify(redeem_code(code, client_api_key=ckey or None))
@app.post("/api/license/validate")
def _license_validate_api():
return jsonify(validate_license(force=True))
@app.route("/license", methods=["GET", "POST"])
def _license_page():
msg = ""
err = ""
if request.method == "POST":
code = (request.form.get("code") or "").strip()
ckey = (request.form.get("client_api_key") or "").strip()
result = redeem_code(code, client_api_key=ckey or None)
if result.get("ok"):
msg = result.get("message") or "激活成功"
else:
err = result.get("message") or "激活失败"
status = get_license_status()
html = _TEMPLATE_PATH.read_text(encoding="utf-8")
return render_template_string(
html,
device_id=get_device_id(),
status=status,
message=msg,
error=err,
api_url=status.get("api_url") or "",
wechat="dekun03",
)
@app.before_request
def _license_before_request():
if os.getenv("LICENSE_DISABLED", "").strip().lower() in ("1", "true", "yes", "on"):
return None
path = request.path or "/"
if _allowed_path(path):
return None
# hub bridge 内部调用:仍要求已授权(整机许可)
if is_license_valid():
return None
if path.startswith("/api/"):
return jsonify({"ok": False, "error": "license_required", "message": "请先激活许可"}), 403
return redirect("/license")