feat: 支持 Linux 客户端配置与发布校验

This commit is contained in:
2026-07-14 08:43:58 +00:00
parent 6df8e3ad31
commit 1b4293e57c
10 changed files with 173 additions and 36 deletions
+39 -4
View File
@@ -1,11 +1,12 @@
import json
import os
from datetime import datetime, timezone
from fastapi import APIRouter, Depends, HTTPException, Request
from app.core.config import ENV_FILE_PATH
from app.core.security import admin_auth, persist_env_value, set_admin_token
from app.repositories import app_channel_repository
from app.core.security import admin_auth, persist_env_value, set_admin_token, token_digest
from app.repositories import app_channel_repository, license_repository
from app.schemas.admin_config import ChangeAdminTokenRequest, ClientConfigGenerateRequest
from app.services.admin_config_service import (
PUBLISH_MAX_FIELDS,
@@ -18,6 +19,7 @@ from app.services.admin_config_service import (
crash_report_test_values,
crash_report_test_text,
normalize_executable_name,
platform_executable_path,
public_api_base_url,
)
from app.services.common_service import validate_channel_code
@@ -26,6 +28,31 @@ from app.services.common_service import validate_channel_code
router = APIRouter(tags=["admin-config"])
def client_config_license_warning(license_key: str, app_id: str, channel: str) -> str:
if not license_key:
return ""
license_row = license_repository.get_license_by_hash_with_usage(token_digest(license_key))
if not license_row:
return "当前 License 不存在或已删除,生成的客户端配置未写入 License,请创建或申请新的 License。"
if license_row["app_id"] != app_id or license_row["channel_code"] != channel:
return "当前 License 不属于所选应用或渠道,生成的客户端配置未写入 License,请选择匹配的 License。"
if license_row["status"] != "active":
return "当前 License 已禁用,生成的客户端配置未写入 License,请启用该 License 或创建新的 License。"
try:
expiry = datetime.fromisoformat(str(license_row["valid_until"]).replace("Z", "+00:00"))
except ValueError:
expiry = datetime.now(timezone.utc)
if expiry.tzinfo is None:
expiry = expiry.replace(tzinfo=timezone.utc)
if expiry <= datetime.now(timezone.utc):
return "当前 License 已过期,生成的客户端配置未写入 License,请创建或申请新的 License。"
used_devices = int(license_row["used_devices"] or 0)
max_devices = int(license_row["max_devices"] or 0)
if used_devices >= max_devices:
return f"当前 License 已绑定 {used_devices}/{max_devices} 台设备,已达到上限。生成的客户端配置未写入 License,请创建或申请新的 License。"
return ""
@router.get("/admin/auth/check")
def admin_auth_check(auth=Depends(admin_auth)):
return {"code": 0, "msg": "管理员令牌有效"}
@@ -83,7 +110,14 @@ def admin_generate_client_config(body: ClientConfigGenerateRequest, request: Req
raise HTTPException(status_code=400, detail={"error": "channel_not_found", "msg": f"渠道 {channel} 不存在"})
defaults = default_client_config_values(request)
main_executable = normalize_executable_name(body.main_executable) or defaults["main_executable"]
main_executable = platform_executable_path(
normalize_executable_name(body.main_executable),
defaults["main_executable"],
)
license_key = body.license_key.strip()
license_warning = client_config_license_warning(license_key, app_id, channel)
if license_warning:
license_key = ""
config = {
"app_id": app_id,
"app_name": app_row["app_name"],
@@ -91,7 +125,7 @@ def admin_generate_client_config(body: ClientConfigGenerateRequest, request: Req
"current_version": current_version,
"client_protocol": str(client_protocol),
"launch_token": defaults["launch_token"],
"license_key": body.license_key.strip(),
"license_key": license_key,
"api_base_url": api_base_url,
"client_token": defaults["client_token"],
"request_timeout_ms": defaults["request_timeout_ms"],
@@ -110,6 +144,7 @@ def admin_generate_client_config(body: ClientConfigGenerateRequest, request: Req
return {
"config": config,
"json_text": json.dumps(config, ensure_ascii=False, indent=2),
"license_warning": license_warning,
"crash_test_config": crash_test_config,
"crash_test_text": crash_report_test_text(api_base_url),
}