mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-09-04 23:19:17 +08:00
feat(update): 实现应用内更新检查和版本显示
- 新增 `VERSION` 文件用于跟踪当前应用版本 (当前为 2.0.7)。
- 创建 `app/service/update/update_service.py` 服务,用于:
- 从 `VERSION` 文件读取当前版本。
- 通过 GitHub API 获取指定仓库 (`GITHUB_REPO_OWNER`/`GITHUB_REPO_NAME`) 的最新 Release Tag。
- 使用 `packaging` 库比较版本,判断是否有可用更新。
- 在应用启动 (`app/core/application.py`) 时异步调用更新检查服务。
- 将当前版本和更新检查结果(是否可用、最新版本号、错误信息)存储在 `app.state.update_info` 中,供模板使用。
- 在基础模板 (`app/templates/base.html`) 的页脚动态显示当前版本。
- 如果检测到新版本,在页脚显示更新提示和指向最新 Release 的链接。
- 如果更新检查失败,在页脚显示错误提示。
- 在 `app/config/config.py` 中添加 `GITHUB_REPO_OWNER` 和 `GITHUB_REPO_NAME` 配置项,并提供默认值。
- 在 `requirements.txt` 中添加 `packaging` 依赖。
- 添加 `update_service` 专用的 logger (`app/log/logger.py`)。
- 改进配置编辑器 (`config_editor.js`, `config_editor.html`):
- 限制预算输入框 (`budget_map`) 的值在 0 到 24576 之间。
- 移除了预算映射项的删除按钮(预算项应随模型列表自动增删)。
- 更新了预算输入的提示文本。
This commit is contained in:
+64
-5
@@ -4,6 +4,7 @@
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from app.config.config import settings, sync_initial_settings
|
||||
from app.log.logger import get_application_logger
|
||||
@@ -15,9 +16,41 @@ from app.core.initialization import initialize_app
|
||||
from app.database.connection import connect_to_db, disconnect_from_db
|
||||
from app.database.initialization import initialize_database
|
||||
from app.scheduler.key_checker import start_scheduler, stop_scheduler # 导入调度器函数
|
||||
from app.service.update.update_service import check_for_updates # 导入更新检查服务
|
||||
|
||||
logger = get_application_logger()
|
||||
|
||||
VERSION_FILE_PATH = "VERSION" # Path relative to project root
|
||||
|
||||
def _get_current_version(default_version: str = "0.0.0") -> str:
|
||||
"""Reads the current version from the VERSION file."""
|
||||
try:
|
||||
# Assuming execution from project root d:/develop/pythonProjects/gemini-balance
|
||||
with open(VERSION_FILE_PATH, 'r', encoding='utf-8') as f:
|
||||
version = f.read().strip()
|
||||
if not version:
|
||||
logger.warning(f"VERSION file ('{VERSION_FILE_PATH}') is empty. Using default version '{default_version}'.")
|
||||
return default_version
|
||||
return version
|
||||
except FileNotFoundError:
|
||||
logger.warning(f"VERSION file not found at '{VERSION_FILE_PATH}'. Using default version '{default_version}'.")
|
||||
return default_version
|
||||
except IOError as e:
|
||||
logger.error(f"Error reading VERSION file ('{VERSION_FILE_PATH}'): {e}. Using default version '{default_version}'.")
|
||||
return default_version
|
||||
|
||||
# 初始化模板引擎,并添加全局变量
|
||||
templates = Jinja2Templates(directory="app/templates")
|
||||
|
||||
# 定义一个函数来更新模板全局变量
|
||||
def update_template_globals(app: FastAPI, update_info: dict):
|
||||
# Jinja2Templates 实例没有直接更新全局变量的方法
|
||||
# 我们需要在请求上下文中传递这些变量,或者修改 Jinja 环境
|
||||
# 更简单的方法是将其存储在 app.state 中,并在渲染时传递
|
||||
app.state.update_info = update_info
|
||||
logger.info(f"Update info stored in app.state: {update_info}")
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""
|
||||
@@ -44,11 +77,29 @@ async def lifespan(app: FastAPI):
|
||||
logger.info("KeyManager initialized successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to initialize application: {str(e)}")
|
||||
raise
|
||||
# 不重新抛出,允许应用继续运行,但记录错误
|
||||
# raise # 取消注释以在初始化失败时停止应用
|
||||
|
||||
# 检查更新 (在核心初始化之后)
|
||||
update_available, latest_version, error_message = await check_for_updates()
|
||||
update_info = {
|
||||
"update_available": update_available,
|
||||
"latest_version": latest_version,
|
||||
"error_message": error_message,
|
||||
"current_version": _get_current_version() # Read from VERSION file
|
||||
}
|
||||
# 将更新信息存储在 app.state 中
|
||||
app.state.update_info = update_info
|
||||
logger.info(f"Update check completed. Info: {update_info}")
|
||||
|
||||
|
||||
# 启动调度器 (如果初始化成功)
|
||||
try:
|
||||
start_scheduler()
|
||||
logger.info("Scheduler started successfully.")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to start scheduler: {e}")
|
||||
|
||||
# 启动调度器
|
||||
start_scheduler()
|
||||
logger.info("Scheduler started successfully.")
|
||||
|
||||
yield # 应用程序运行期间
|
||||
|
||||
@@ -79,7 +130,15 @@ def create_app() -> FastAPI:
|
||||
version="1.0.0",
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
|
||||
# 初始化 app.state (如果尚未存在)
|
||||
if not hasattr(app, "state"):
|
||||
from starlette.datastructures import State
|
||||
app.state = State()
|
||||
# 确保 update_info 即使在 lifespan 之前访问也不会出错
|
||||
app.state.update_info = {"update_available": False, "latest_version": None, "error_message": "Checking...", "current_version": _get_current_version()} # Read from VERSION file for initial state
|
||||
|
||||
|
||||
# 配置静态文件
|
||||
app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user