diff --git a/app/core/application.py b/app/core/application.py index da1bf6d..a36cc4c 100644 --- a/app/core/application.py +++ b/app/core/application.py @@ -11,6 +11,7 @@ from app.exception.exceptions import setup_exception_handlers from app.router.routes import setup_routers from app.service.key.key_manager import get_key_manager_instance from app.database.connection import connect_to_db, disconnect_from_db +from app.utils.helpers import get_current_version # Import from helpers 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 @@ -20,28 +21,11 @@ logger = get_application_logger() # Define project paths using pathlib # Assuming this file is at app/core/application.py PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent -VERSION_FILE_PATH = PROJECT_ROOT / "VERSION" +# VERSION_FILE_PATH = PROJECT_ROOT / "VERSION" # Removed: Defined in helpers.py STATIC_DIR = PROJECT_ROOT / "app" / "static" TEMPLATES_DIR = PROJECT_ROOT / "app" / "templates" - -def _get_current_version(default_version: str = "0.0.0") -> str: - """Reads the current version from the VERSION file.""" - version_file = VERSION_FILE_PATH # Use Path object - try: - # Use Path object's open method - with version_file.open('r', encoding='utf-8') as f: - version = f.read().strip() - if not version: - logger.warning(f"VERSION file ('{version_file}') is empty. Using default version '{default_version}'.") - return default_version - return version - except FileNotFoundError: - logger.warning(f"VERSION file not found at '{version_file}'. Using default version '{default_version}'.") - return default_version - except IOError as e: - logger.error(f"Error reading VERSION file ('{version_file}'): {e}. Using default version '{default_version}'.") - return default_version +# Removed _get_current_version function definition, moved to helpers.py # 初始化模板引擎,并添加全局变量 templates = Jinja2Templates(directory="app/templates") @@ -88,7 +72,7 @@ def _stop_scheduler(): async def _perform_update_check(app: FastAPI): """Checks for updates and stores the info in app.state.""" update_available, latest_version, error_message = await check_for_updates() - current_version = _get_current_version() # Read from VERSION file + current_version = get_current_version() # Use imported function update_info = { "update_available": update_available, "latest_version": latest_version, @@ -119,7 +103,7 @@ async def lifespan(app: FastAPI): await _setup_database_and_config(settings) # Pass settings object # Perform update check after core components are ready - await _perform_update_check(app) + # await _perform_update_check(app) # Removed: Version check moved to frontend API call # Start the scheduler _start_scheduler() @@ -148,7 +132,7 @@ def create_app() -> FastAPI: # 创建FastAPI应用 # Read version from file for consistency - current_version = _get_current_version() + current_version = get_current_version() # Use imported function app = FastAPI( title="Gemini Balance API", description="Gemini API代理服务,支持负载均衡和密钥管理", diff --git a/app/router/routes.py b/app/router/routes.py index 7693ad0..5cf23ff 100644 --- a/app/router/routes.py +++ b/app/router/routes.py @@ -8,7 +8,7 @@ from fastapi.templating import Jinja2Templates from app.core.security import verify_auth_token from app.log.logger import get_routes_logger -from app.router import error_log_routes, gemini_routes, openai_routes, config_routes, scheduler_routes, stats_routes # 新增导入 stats_routes +from app.router import error_log_routes, gemini_routes, openai_routes, config_routes, scheduler_routes, stats_routes, version_routes # 新增导入 version_routes from app.service.key.key_manager import get_key_manager_instance from app.service.stats_service import StatsService @@ -33,6 +33,7 @@ def setup_routers(app: FastAPI) -> None: app.include_router(error_log_routes.router) app.include_router(scheduler_routes.router) # 新增包含 scheduler 路由 app.include_router(stats_routes.router) # 包含 stats API 路由 + app.include_router(version_routes.router) # 包含 version API 路由 # 添加页面路由 setup_page_routes(app) diff --git a/app/router/version_routes.py b/app/router/version_routes.py new file mode 100644 index 0000000..8f7b27a --- /dev/null +++ b/app/router/version_routes.py @@ -0,0 +1,38 @@ +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel, Field +from typing import Optional + +from app.service.update.update_service import check_for_updates +from app.utils.helpers import get_current_version +from app.log.logger import get_update_logger + +router = APIRouter(prefix="/api/version", tags=["Version"]) +logger = get_update_logger() + +class VersionInfo(BaseModel): + current_version: str = Field(..., description="当前应用程序版本") + latest_version: Optional[str] = Field(None, description="可用的最新版本") + update_available: bool = Field(False, description="是否有可用更新") + error_message: Optional[str] = Field(None, description="检查更新时发生的错误信息") + +@router.get("/check", response_model=VersionInfo, summary="检查应用程序更新") +async def get_version_info(): + """ + 检查当前应用程序版本与最新的 GitHub release 版本。 + """ + try: + current_version = get_current_version() # Use imported function + update_available, latest_version, error_message = await check_for_updates() + + # Log the result for debugging + logger.info(f"Version check API result: current={current_version}, latest={latest_version}, available={update_available}, error='{error_message}'") + + return VersionInfo( + current_version=current_version, + latest_version=latest_version, + update_available=update_available, + error_message=error_message + ) + except Exception as e: + logger.error(f"Error in /api/version/check endpoint: {e}", exc_info=True) + raise HTTPException(status_code=500, detail="检查版本信息时发生内部错误") \ No newline at end of file diff --git a/app/templates/base.html b/app/templates/base.html index 11d95bd..55e4d57 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -202,20 +202,9 @@ 免费项目,谨防诈骗 - {% if request and request.app.state.update_info %} - {% set update_info = request.app.state.update_info %} - | - v{{ update_info.current_version }} - {% if update_info.update_available %} - | - - 新版本: v{{ update_info.latest_version }} - - {% elif update_info.error_message and update_info.error_message != 'Checking...' %} - | - 更新检查失败 - {% endif %} - {% endif %} + + + @@ -279,6 +268,48 @@ }, 300); // Short delay to show spinner } + // --- Version Check --- + const versionInfoContainer = document.getElementById('version-info-container'); + + async function fetchVersionInfo() { + if (!versionInfoContainer) return; + versionInfoContainer.innerHTML = '|检查更新中...'; // Initial loading state + + try { + const response = await fetch('/api/version/check'); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + + let versionHtml = `|v${data.current_version}`; + if (data.update_available) { + versionHtml += ` + | + + 新版本: v${data.latest_version} + `; + } else if (data.error_message) { + versionHtml += ` + | + 更新检查失败`; + } else { + versionHtml += `|已是最新`; // Indicate up-to-date + } + versionInfoContainer.innerHTML = versionHtml; + + } catch (error) { + console.error('Error fetching version info:', error); + versionInfoContainer.innerHTML = `|更新检查失败`; + } + } + + // Fetch immediately on load + fetchVersionInfo(); + + // Fetch periodically (e.g., every hour) + setInterval(fetchVersionInfo, 3600000); // 3600000 ms = 1 hour + {% block body_scripts %}{% endblock %} diff --git a/app/utils/helpers.py b/app/utils/helpers.py index fef7ed6..74a129e 100644 --- a/app/utils/helpers.py +++ b/app/utils/helpers.py @@ -6,9 +6,19 @@ import re import base64 import requests from typing import Dict, Any, List, Optional, Tuple +from pathlib import Path +import logging # Import logging from app.core.constants import DATA_URL_PATTERN, IMAGE_URL_PATTERN, VALID_IMAGE_RATIOS +# Define logger for helper functions if needed, or use specific loggers +helper_logger = logging.getLogger("app.utils") # Or use a more specific logger if available + +# Define project root and version file path here for get_current_version +# Assuming this file is at app/utils/helpers.py +PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent +VERSION_FILE_PATH = PROJECT_ROOT / "VERSION" + def extract_mime_type_and_data(base64_string: str) -> Tuple[Optional[str], str]: """ @@ -146,3 +156,21 @@ def is_valid_api_key(key: str) -> bool: return False + +def get_current_version(default_version: str = "0.0.0") -> str: + """Reads the current version from the VERSION file.""" + version_file = VERSION_FILE_PATH # Use Path object defined above + try: + # Use Path object's open method + with version_file.open('r', encoding='utf-8') as f: + version = f.read().strip() + if not version: + helper_logger.warning(f"VERSION file ('{version_file}') is empty. Using default version '{default_version}'.") + return default_version + return version + except FileNotFoundError: + helper_logger.warning(f"VERSION file not found at '{version_file}'. Using default version '{default_version}'.") + return default_version + except IOError as e: + helper_logger.error(f"Error reading VERSION file ('{version_file}'): {e}. Using default version '{default_version}'.") + return default_version