feat: 版本号管理机制(VERSION文件+git哈希,部署/启动/UI/health均展示) + 平台账号管理(accounts表CRUD,资产所属账号候选联动,重命名同步引用)

This commit is contained in:
gouki
2026-08-08 20:07:03 +00:00
parent 4883837dda
commit a33a2b71de
15 changed files with 373 additions and 16 deletions
+40 -5
View File
@@ -10,6 +10,7 @@ from contextlib import asynccontextmanager
from pathlib import Path
import asyncio
import logging
import subprocess
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
@@ -21,7 +22,7 @@ from sqlmodel import Session
from app.core.config import settings
from app.core.seed import seed_providers
from app.database import assets_engine, init_db
from app.routers import agent, assets, monitor, notify, providers, ssl, stats, sync
from app.routers import accounts, agent, assets, monitor, notify, providers, ssl, stats, sync
from app.services import cleanup_service
BASE_DIR = Path(__file__).resolve().parent.parent
@@ -50,6 +51,30 @@ def _asset_version() -> str:
_ASSET_VERSION_CACHE: str | None = None
def _read_app_version() -> str:
"""语义版本号:读取项目根目录 VERSION 文件(缺失时回退 dev"""
try:
return (BASE_DIR / "VERSION").read_text(encoding="utf-8").strip() or "dev"
except OSError:
return "dev"
def _read_git_commit() -> str:
"""当前 git 短哈希:用于比对本地与线上代码是否一致(非 git 环境回退 unknown"""
try:
out = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
cwd=BASE_DIR, capture_output=True, text=True, timeout=5,
)
return out.stdout.strip() or "unknown"
except Exception:
return "unknown"
APP_VERSION = _read_app_version()
GIT_COMMIT = _read_git_commit()
# 全局日志配置:统一格式,便于生产环境排查
logging.basicConfig(
level=logging.INFO,
@@ -71,7 +96,10 @@ async def lifespan(_: FastAPI):
cleanup_task.cancel()
app = FastAPI(title=settings.APP_NAME, version="0.2.0", lifespan=lifespan)
app = FastAPI(title=settings.APP_NAME, version=f"{APP_VERSION} ({GIT_COMMIT})", lifespan=lifespan)
# 启动即打印版本号,部署后可通过 journalctl 快速确认线上运行版本
logging.getLogger(__name__).info("vps-manager 启动:版本 %scommit %s", APP_VERSION, GIT_COMMIT)
app.add_middleware(
CORSMiddleware,
@@ -83,6 +111,7 @@ app.add_middleware(
app.include_router(assets.router)
app.include_router(providers.router)
app.include_router(accounts.router)
app.include_router(stats.router)
app.include_router(agent.router)
app.include_router(monitor.router)
@@ -105,7 +134,8 @@ app.mount("/static", NoCacheStaticFiles(directory=STATIC_DIR), name="static")
def index() -> HTMLResponse:
"""前端 SPA 入口"""
html = jinja_env.get_template("index.html").render(
app_name=settings.APP_NAME, asset_version=_asset_version()
app_name=settings.APP_NAME, asset_version=_asset_version(),
app_version=APP_VERSION, git_commit=GIT_COMMIT,
)
return HTMLResponse(html)
@@ -129,5 +159,10 @@ def service_worker() -> FileResponse:
@app.get("/health")
def health_check() -> dict:
"""健康检查(version 动态读取,便于验证自动更新)"""
return {"status": "ok", "app": "vps-manager", "version": app.version}
"""健康检查(version/commit 动态读取,便于验证自动更新与比对本地版本"""
return {
"status": "ok",
"app": "vps-manager",
"version": APP_VERSION,
"commit": GIT_COMMIT,
}