fix: show leave-license interstitial instead of blank black page.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-17 18:46:16 +08:00
parent bc00671382
commit 1664693d3d
2 changed files with 93 additions and 11 deletions
+44 -5
View File
@@ -2,11 +2,12 @@
from __future__ import annotations from __future__ import annotations
import html as html_lib
import os import os
from pathlib import Path from pathlib import Path
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse, Response
from lib.license.license_lib import ( from lib.license.license_lib import (
get_device_id, get_device_id,
@@ -17,6 +18,7 @@ from lib.license.license_lib import (
) )
_TEMPLATE_PATH = Path(__file__).resolve().parent / "templates" / "license.html" _TEMPLATE_PATH = Path(__file__).resolve().parent / "templates" / "license.html"
_POST_LICENSE_TARGET = "/login"
def _license_public_path(path: str) -> bool: def _license_public_path(path: str) -> bool:
@@ -47,6 +49,43 @@ def _license_manage_requested(request: Request) -> bool:
) )
def _locally_licensed() -> bool:
return bool(get_license_status(skip_remote=True).get("valid"))
def _leave_license_response(target: str = _POST_LICENSE_TARGET) -> Response:
safe = html_lib.escape(target, quote=True)
body = f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url={safe}">
<title>已授权</title>
<style>
body {{
margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
background: #0a0a10; color: #e8e8f0; font-family: sans-serif;
}}
a {{ color: #7ec8ff; }}
</style>
</head>
<body>
<p>已激活,正在进入系统… <a href="{safe}">点击进入</a></p>
<script>location.replace({target!r});</script>
</body>
</html>
"""
return Response(
content=body,
status_code=302,
headers={
"Location": target,
"Cache-Control": "no-store, no-cache, must-revalidate",
"Content-Type": "text/html; charset=utf-8",
},
)
def install_license_middleware(app: FastAPI) -> None: def install_license_middleware(app: FastAPI) -> None:
@app.get("/health") @app.get("/health")
async def _license_health(): async def _license_health():
@@ -76,8 +115,8 @@ def install_license_middleware(app: FastAPI) -> None:
@app.api_route("/license", methods=["GET", "POST"]) @app.api_route("/license", methods=["GET", "POST"])
async def _license_page(request: Request): async def _license_page(request: Request):
# 已授权:默认不可再进授权页(续费/换机:/license?renew=1 # 已授权:默认不可再进授权页(续费/换机:/license?renew=1
if is_license_valid() and request.method == "GET" and not _license_manage_requested(request): if _locally_licensed() and request.method == "GET" and not _license_manage_requested(request):
return RedirectResponse(url="/", status_code=302) return _leave_license_response()
msg = "" msg = ""
err = "" err = ""
@@ -87,9 +126,9 @@ def install_license_middleware(app: FastAPI) -> None:
ckey = str(form.get("client_api_key") or "").strip() ckey = str(form.get("client_api_key") or "").strip()
result = redeem_code(code, client_api_key=ckey or None) result = redeem_code(code, client_api_key=ckey or None)
if result.get("ok"): if result.get("ok"):
return RedirectResponse(url="/", status_code=302) return _leave_license_response()
err = result.get("message") or "激活失败" err = result.get("message") or "激活失败"
status = get_license_status() status = get_license_status(skip_remote=True)
html = _TEMPLATE_PATH.read_text(encoding="utf-8") html = _TEMPLATE_PATH.read_text(encoding="utf-8")
filled = ( filled = (
html.replace("{{ device_id }}", get_device_id()) html.replace("{{ device_id }}", get_device_id())
+49 -6
View File
@@ -2,10 +2,11 @@
from __future__ import annotations from __future__ import annotations
import html as html_lib
import os import os
from pathlib import Path from pathlib import Path
from flask import Flask, jsonify, redirect, render_template_string, request from flask import Flask, Response, jsonify, redirect, render_template_string, request
from lib.license.license_lib import ( from lib.license.license_lib import (
get_device_id, get_device_id,
@@ -17,6 +18,9 @@ from lib.license.license_lib import (
_TEMPLATE_PATH = Path(__file__).resolve().parent / "templates" / "license.html" _TEMPLATE_PATH = Path(__file__).resolve().parent / "templates" / "license.html"
# 已授权离开授权页时优先去登录页(有完整 UI);已登录会再跳进系统
_POST_LICENSE_TARGET = "/login"
def _license_public_path(path: str) -> bool: def _license_public_path(path: str) -> bool:
"""未授权时仍可访问的路径(授权页 / 接口 / 静态资源)。""" """未授权时仍可访问的路径(授权页 / 接口 / 静态资源)。"""
@@ -31,7 +35,7 @@ def _license_public_path(path: str) -> bool:
def _license_manage_requested() -> bool: def _license_manage_requested() -> bool:
"""已授权时默认禁止进入 /license;续费/换机用 ?renew=1。""" """已授权时默认禁止进入 /license;续费/换机用 ?renew=1。"""
return (request.args.get("renew") or request.args.get("manage") or "").strip() in ( return (request.args.get("renew") or request.args.get("manage") or "").strip().lower() in (
"1", "1",
"true", "true",
"yes", "yes",
@@ -39,6 +43,45 @@ def _license_manage_requested() -> bool:
) )
def _locally_licensed() -> bool:
"""仅看本地状态,避免授权站网络拖死页面。"""
return bool(get_license_status(skip_remote=True).get("valid"))
def _leave_license_response(target: str = _POST_LICENSE_TARGET) -> Response:
"""已授权离开 /license:302 + 可见提示,避免浏览器停在全黑空页。"""
safe = html_lib.escape(target, quote=True)
body = f"""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="0;url={safe}">
<title>已授权</title>
<style>
body {{
margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
background: #0a0a10; color: #e8e8f0; font-family: sans-serif;
}}
a {{ color: #7ec8ff; }}
</style>
</head>
<body>
<p>已激活,正在进入系统… <a href="{safe}">点击进入</a></p>
<script>location.replace({target!r});</script>
</body>
</html>
"""
return Response(
body,
status=302,
headers={
"Location": target,
"Cache-Control": "no-store, no-cache, must-revalidate",
"Content-Type": "text/html; charset=utf-8",
},
)
def install_license_gate(app: Flask) -> None: def install_license_gate(app: Flask) -> None:
"""注册 /license 与 before_request 门禁。三所 Flask 共用。""" """注册 /license 与 before_request 门禁。三所 Flask 共用。"""
@@ -65,8 +108,8 @@ def install_license_gate(app: Flask) -> None:
@app.route("/license", methods=["GET", "POST"]) @app.route("/license", methods=["GET", "POST"])
def _license_page(): def _license_page():
# 已授权:默认不可再进授权页(续费/换机:/license?renew=1 # 已授权:默认不可再进授权页(续费/换机:/license?renew=1
if is_license_valid() and request.method == "GET" and not _license_manage_requested(): if _locally_licensed() and request.method == "GET" and not _license_manage_requested():
return redirect("/") return _leave_license_response()
msg = "" msg = ""
err = "" err = ""
@@ -75,9 +118,9 @@ def install_license_gate(app: Flask) -> None:
ckey = (request.form.get("client_api_key") or "").strip() ckey = (request.form.get("client_api_key") or "").strip()
result = redeem_code(code, client_api_key=ckey or None) result = redeem_code(code, client_api_key=ckey or None)
if result.get("ok"): if result.get("ok"):
return redirect("/") return _leave_license_response()
err = result.get("message") or "激活失败" err = result.get("message") or "激活失败"
status = get_license_status() status = get_license_status(skip_remote=True)
html = _TEMPLATE_PATH.read_text(encoding="utf-8") html = _TEMPLATE_PATH.read_text(encoding="utf-8")
return render_template_string( return render_template_string(
html, html,