mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-05-10 17:43:23 +08:00
152 lines
5.3 KiB
Python
152 lines
5.3 KiB
Python
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
|
||
from app.middleware.middleware import setup_middlewares
|
||
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.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):
|
||
"""
|
||
应用程序生命周期管理器
|
||
|
||
Args:
|
||
app: FastAPI应用实例
|
||
"""
|
||
# 启动事件
|
||
logger.info("Application starting up...")
|
||
try:
|
||
# 初始化数据库
|
||
initialize_database()
|
||
logger.info("Database initialized successfully")
|
||
|
||
# 连接到数据库
|
||
await connect_to_db()
|
||
|
||
# 同步初始配置(DB优先,然后同步回DB)
|
||
await sync_initial_settings()
|
||
|
||
# 初始化KeyManager (使用可能已从DB更新的settings)
|
||
await get_key_manager_instance(settings.API_KEYS)
|
||
logger.info("KeyManager initialized successfully")
|
||
except Exception as e:
|
||
logger.error(f"Failed to initialize application: {str(e)}")
|
||
# 不重新抛出,允许应用继续运行,但记录错误
|
||
# 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}")
|
||
|
||
|
||
yield # 应用程序运行期间
|
||
|
||
# 关闭事件
|
||
logger.info("Application shutting down...")
|
||
|
||
# 停止调度器
|
||
stop_scheduler()
|
||
logger.info("Scheduler stopped.")
|
||
|
||
# 断开数据库连接
|
||
await disconnect_from_db()
|
||
|
||
def create_app() -> FastAPI:
|
||
"""
|
||
创建并配置FastAPI应用程序实例
|
||
|
||
Returns:
|
||
FastAPI: 配置好的FastAPI应用程序实例
|
||
"""
|
||
# 初始化应用程序
|
||
initialize_app()
|
||
|
||
# 创建FastAPI应用
|
||
app = FastAPI(
|
||
title="Gemini Balance API",
|
||
description="Gemini API代理服务,支持负载均衡和密钥管理",
|
||
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")
|
||
|
||
# 配置中间件
|
||
setup_middlewares(app)
|
||
|
||
# 配置异常处理器
|
||
setup_exception_handlers(app)
|
||
|
||
# 配置路由
|
||
setup_routers(app)
|
||
|
||
return app
|