feat: 版本号管理机制(VERSION文件+git哈希,部署/启动/UI/health均展示) + 平台账号管理(accounts表CRUD,资产所属账号候选联动,重命名同步引用)
This commit is contained in:
+40
-5
@@ -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 启动:版本 %s,commit %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,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user