feat: 支持 Linux 客户端配置与发布校验
This commit is contained in:
@@ -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),
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ from app.services.common_service import is_executable_path
|
||||
from app.services.signing_service import canonical_manifest_bytes, sign_manifest, sign_policy
|
||||
|
||||
|
||||
TARGET_PLATFORM = os.getenv("TARGET_PLATFORM", "windows")
|
||||
TARGET_ARCH = os.getenv("TARGET_ARCH", "x64")
|
||||
TARGET_PLATFORM = os.getenv("TARGET_PLATFORM", "windows").strip().lower() or "windows"
|
||||
TARGET_ARCH = os.getenv("TARGET_ARCH", "x64").strip() or "x64"
|
||||
SIGNING_KEY_ID = os.getenv("SIGNING_KEY_ID", "manifest-key-v1")
|
||||
|
||||
router = APIRouter(tags=["admin-version"])
|
||||
|
||||
@@ -17,8 +17,8 @@ from app.services.common_service import is_executable_path, policy_row_to_dict,
|
||||
from app.services.signing_service import canonical_manifest_bytes, sign_device_identity, sign_manifest, sign_policy
|
||||
|
||||
|
||||
TARGET_PLATFORM = __import__("os").getenv("TARGET_PLATFORM", "windows")
|
||||
TARGET_ARCH = __import__("os").getenv("TARGET_ARCH", "x64")
|
||||
TARGET_PLATFORM = __import__("os").getenv("TARGET_PLATFORM", "windows").strip().lower() or "windows"
|
||||
TARGET_ARCH = __import__("os").getenv("TARGET_ARCH", "x64").strip() or "x64"
|
||||
SIGNING_KEY_ID = __import__("os").getenv("SIGNING_KEY_ID", "manifest-key-v1")
|
||||
|
||||
router = APIRouter(tags=["client-update"])
|
||||
|
||||
Reference in New Issue
Block a user