diff --git a/lib/license/fastapi_gate.py b/lib/license/fastapi_gate.py index d616597..a020fb8 100644 --- a/lib/license/fastapi_gate.py +++ b/lib/license/fastapi_gate.py @@ -2,11 +2,12 @@ from __future__ import annotations +import html as html_lib import os from pathlib import Path 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 ( get_device_id, @@ -17,6 +18,7 @@ from lib.license.license_lib import ( ) _TEMPLATE_PATH = Path(__file__).resolve().parent / "templates" / "license.html" +_POST_LICENSE_TARGET = "/login" 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""" + + + + + 已授权 + + + +

已激活,正在进入系统… 点击进入

+ + + +""" + 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: @app.get("/health") async def _license_health(): @@ -76,8 +115,8 @@ def install_license_middleware(app: FastAPI) -> None: @app.api_route("/license", methods=["GET", "POST"]) async def _license_page(request: Request): # 已授权:默认不可再进授权页(续费/换机:/license?renew=1) - if is_license_valid() and request.method == "GET" and not _license_manage_requested(request): - return RedirectResponse(url="/", status_code=302) + if _locally_licensed() and request.method == "GET" and not _license_manage_requested(request): + return _leave_license_response() msg = "" err = "" @@ -87,9 +126,9 @@ def install_license_middleware(app: FastAPI) -> None: ckey = str(form.get("client_api_key") or "").strip() result = redeem_code(code, client_api_key=ckey or None) if result.get("ok"): - return RedirectResponse(url="/", status_code=302) + return _leave_license_response() err = result.get("message") or "激活失败" - status = get_license_status() + status = get_license_status(skip_remote=True) html = _TEMPLATE_PATH.read_text(encoding="utf-8") filled = ( html.replace("{{ device_id }}", get_device_id()) diff --git a/lib/license/flask_gate.py b/lib/license/flask_gate.py index fc957d5..da1e131 100644 --- a/lib/license/flask_gate.py +++ b/lib/license/flask_gate.py @@ -2,10 +2,11 @@ from __future__ import annotations +import html as html_lib import os 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 ( get_device_id, @@ -17,6 +18,9 @@ from lib.license.license_lib import ( _TEMPLATE_PATH = Path(__file__).resolve().parent / "templates" / "license.html" +# 已授权离开授权页时优先去登录页(有完整 UI);已登录会再跳进系统 +_POST_LICENSE_TARGET = "/login" + def _license_public_path(path: str) -> bool: """未授权时仍可访问的路径(授权页 / 接口 / 静态资源)。""" @@ -31,7 +35,7 @@ def _license_public_path(path: str) -> bool: def _license_manage_requested() -> bool: """已授权时默认禁止进入 /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", "true", "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""" + + + + + 已授权 + + + +

已激活,正在进入系统… 点击进入

+ + + +""" + 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: """注册 /license 与 before_request 门禁。三所 Flask 共用。""" @@ -65,8 +108,8 @@ def install_license_gate(app: Flask) -> None: @app.route("/license", methods=["GET", "POST"]) def _license_page(): # 已授权:默认不可再进授权页(续费/换机:/license?renew=1) - if is_license_valid() and request.method == "GET" and not _license_manage_requested(): - return redirect("/") + if _locally_licensed() and request.method == "GET" and not _license_manage_requested(): + return _leave_license_response() msg = "" err = "" @@ -75,9 +118,9 @@ def install_license_gate(app: Flask) -> None: ckey = (request.form.get("client_api_key") or "").strip() result = redeem_code(code, client_api_key=ckey or None) if result.get("ok"): - return redirect("/") + return _leave_license_response() err = result.get("message") or "激活失败" - status = get_license_status() + status = get_license_status(skip_remote=True) html = _TEMPLATE_PATH.read_text(encoding="utf-8") return render_template_string( html,