feat(server): modularize backend and admin console

This commit is contained in:
2026-07-14 01:38:41 +00:00
parent 9a6ac1e4ed
commit 95dd32c75a
261 changed files with 39289 additions and 2156 deletions
+110
View File
@@ -0,0 +1,110 @@
import json
import os
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.schemas.admin_config import ChangeAdminTokenRequest, ClientConfigGenerateRequest
from app.services.admin_config_service import (
PUBLISH_MAX_FIELDS,
PUBLISH_MAX_FILES,
PUBLISH_MAX_REQUEST_BYTES,
RELEASE_MAIN_EXECUTABLE,
TARGET_ARCH,
TARGET_PLATFORM,
default_client_config_values,
normalize_executable_name,
public_api_base_url,
)
from app.services.common_service import validate_channel_code
router = APIRouter(tags=["admin-config"])
@router.get("/admin/auth/check")
def admin_auth_check(auth=Depends(admin_auth)):
return {"code": 0, "msg": "管理员令牌有效"}
@router.post("/admin/token/change")
def admin_change_token(body: ChangeAdminTokenRequest, auth=Depends(admin_auth)):
new_token = body.new_token.strip()
if len(new_token) < 8:
raise HTTPException(status_code=400, detail="新令牌至少需要 8 个字符")
if len(new_token) > 128:
raise HTTPException(status_code=400, detail="新令牌不能超过 128 个字符")
try:
persist_env_value(ENV_FILE_PATH, "ADMIN_TOKEN", new_token)
except Exception as exc:
raise HTTPException(status_code=500, detail=f"令牌已通过校验,但写入 .env 失败: {exc}")
set_admin_token(new_token)
os.environ["ADMIN_TOKEN"] = new_token
return {"code": 0, "msg": f"管理员令牌已更新,并已写入 {ENV_FILE_PATH}"}
@router.get("/admin/runtime-config")
def admin_runtime_config(request: Request, auth=Depends(admin_auth)):
return {
"release_main_executable": RELEASE_MAIN_EXECUTABLE,
"target_platform": TARGET_PLATFORM,
"target_arch": TARGET_ARCH,
"publish_max_request_bytes": PUBLISH_MAX_REQUEST_BYTES,
"publish_max_files": PUBLISH_MAX_FILES,
"publish_max_fields": PUBLISH_MAX_FIELDS,
"api_base_url": public_api_base_url(request),
"default_client_config": default_client_config_values(request),
}
@router.post("/admin/client-config/generate")
def admin_generate_client_config(body: ClientConfigGenerateRequest, request: Request, auth=Depends(admin_auth)):
app_id = body.app_id.strip()
channel = body.channel.strip() or "stable"
current_version = body.current_version.strip() or "1.0.0"
client_protocol = max(1, int(body.client_protocol or 3))
api_base_url = (body.api_base_url.strip() or public_api_base_url(request)).rstrip("/")
if not app_id:
raise HTTPException(status_code=400, detail="请先选择或填写 App ID")
if not validate_channel_code(channel):
raise HTTPException(status_code=400, detail="渠道代码无效")
if not api_base_url.startswith(("http://", "https://")):
raise HTTPException(status_code=400, detail="api_base_url 必须以 http:// 或 https:// 开头")
app_row = app_channel_repository.get_app(app_id)
if not app_row:
raise HTTPException(status_code=404, detail="应用不存在,请先在应用管理中创建")
channel_row = app_channel_repository.get_channel(app_id, channel)
if not channel_row:
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"]
config = {
"app_id": app_id,
"app_name": app_row["app_name"],
"channel": channel,
"current_version": current_version,
"client_protocol": str(client_protocol),
"launch_token": defaults["launch_token"],
"license_key": body.license_key.strip(),
"api_base_url": api_base_url,
"client_token": defaults["client_token"],
"request_timeout_ms": defaults["request_timeout_ms"],
"temp_folder": defaults["temp_folder"],
"device_id": "",
"install_root": defaults["install_root"],
"main_executable": main_executable,
"launcher_executable": defaults["launcher_executable"],
"updater_executable": defaults["updater_executable"],
"bootstrap_executable": defaults["bootstrap_executable"],
"health_check_timeout_ms": defaults["health_check_timeout_ms"],
"platform": defaults["platform"],
"arch": defaults["arch"],
}
return {
"config": config,
"json_text": json.dumps(config, ensure_ascii=False, indent=2),
}