mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-05-12 11:30:20 +08:00
本次提交主要包含以下内容:
1. **日志自动删除功能**:
* 新增环境变量 (`AUTO_DELETE_ERROR_LOGS_ENABLED`, `AUTO_DELETE_ERROR_LOGS_DAYS`, `AUTO_DELETE_REQUEST_LOGS_ENABLED`, `AUTO_DELETE_REQUEST_LOGS_DAYS`) 用于控制错误日志和请求日志的自动删除策略。
* 在 `app/config/config.py` 中添加了对这些新配置项的支持和验证逻辑 (Pydantic `validator` 更新为 `field_validator`)。
* 修改了 `app/log/logger.py` 以适应新的日志配置。
* 新增 `app/scheduler/scheduled_tasks.py` 用于执行定期的日志清理任务。
* 新增 `app/service/error_log/error_log_service.py` 和 `app/service/request_log/request_log_service.py` 来处理具体的日志删除逻辑。
* 更新了 `app/router/error_log_routes.py` 和 `app/router/scheduler_routes.py` 以集成新功能。
2. **前端配置页面更新**:
* 在 `app/templates/config_editor.html` 和 `app/static/js/config_editor.js` 中添加了用于配置日志自动删除选项的用户界面元素。
3. **代码和文件结构调整**:
* 删除了不再使用的 `app/scheduler/key_checker.py` 文件。
* 在 `.gitignore` 文件中添加了 `default_db` 以忽略该目录。
4. **其他**:
* 对 `app/core/application.py` 进行了相应调整。
该更新旨在增强应用的日志管理能力,提供更灵活的日志保留策略,并优化了配置界面的用户体验。
166 lines
5.9 KiB
Python
166 lines
5.9 KiB
Python
from contextlib import asynccontextmanager
|
||
from pathlib import Path # Add pathlib import
|
||
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.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.scheduled_tasks import start_scheduler, stop_scheduler
|
||
from app.service.update.update_service import check_for_updates
|
||
|
||
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" # Removed: Defined in helpers.py
|
||
STATIC_DIR = PROJECT_ROOT / "app" / "static"
|
||
TEMPLATES_DIR = PROJECT_ROOT / "app" / "templates"
|
||
|
||
# Removed _get_current_version function definition, moved to helpers.py
|
||
|
||
# 初始化模板引擎,并添加全局变量
|
||
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}")
|
||
|
||
|
||
# --- Helper functions for lifespan ---
|
||
|
||
async def _setup_database_and_config(app_settings):
|
||
"""Initializes database, syncs settings, and initializes KeyManager."""
|
||
initialize_database()
|
||
logger.info("Database initialized successfully")
|
||
await connect_to_db()
|
||
await sync_initial_settings()
|
||
# Initialize KeyManager using potentially updated settings
|
||
await get_key_manager_instance(app_settings.API_KEYS)
|
||
logger.info("Database, config sync, and KeyManager initialized successfully")
|
||
|
||
async def _shutdown_database():
|
||
"""Disconnects from the database."""
|
||
await disconnect_from_db()
|
||
|
||
def _start_scheduler():
|
||
"""Starts the background scheduler."""
|
||
try:
|
||
start_scheduler()
|
||
logger.info("Scheduler started successfully.")
|
||
except Exception as e:
|
||
logger.error(f"Failed to start scheduler: {e}")
|
||
|
||
def _stop_scheduler():
|
||
"""Stops the background scheduler."""
|
||
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() # Use imported function
|
||
update_info = {
|
||
"update_available": update_available,
|
||
"latest_version": latest_version,
|
||
"error_message": error_message,
|
||
"current_version": current_version
|
||
}
|
||
# Ensure app.state exists and store update info
|
||
if not hasattr(app, "state"):
|
||
from starlette.datastructures import State
|
||
app.state = State()
|
||
app.state.update_info = update_info
|
||
logger.info(f"Update check completed. Info: {update_info}")
|
||
|
||
# --- Application Lifespan ---
|
||
|
||
@asynccontextmanager
|
||
async def lifespan(app: FastAPI):
|
||
"""
|
||
Manages the application startup and shutdown events.
|
||
|
||
Args:
|
||
app: FastAPI应用实例
|
||
"""
|
||
# Startup events
|
||
logger.info("Application starting up...")
|
||
try:
|
||
# Setup database, config, and KeyManager
|
||
await _setup_database_and_config(settings) # Pass settings object
|
||
|
||
# Perform update check after core components are ready
|
||
# await _perform_update_check(app) # Removed: Version check moved to frontend API call
|
||
|
||
# Start the scheduler
|
||
_start_scheduler()
|
||
|
||
except Exception as e:
|
||
logger.critical(f"Critical error during application startup: {str(e)}", exc_info=True)
|
||
# Depending on the severity, you might want to prevent the app from fully starting
|
||
# For now, we log critically and let it yield, potentially in a broken state.
|
||
# Consider adding more robust error handling here if startup failures should halt the app.
|
||
|
||
yield # Application runs
|
||
|
||
# Shutdown events
|
||
logger.info("Application shutting down...")
|
||
_stop_scheduler()
|
||
await _shutdown_database()
|
||
|
||
def create_app() -> FastAPI:
|
||
"""
|
||
创建并配置FastAPI应用程序实例
|
||
|
||
Returns:
|
||
FastAPI: 配置好的FastAPI应用程序实例
|
||
"""
|
||
# Removed: initialize_app() call
|
||
|
||
# 创建FastAPI应用
|
||
# Read version from file for consistency
|
||
current_version = get_current_version() # Use imported function
|
||
app = FastAPI(
|
||
title="Gemini Balance API",
|
||
description="Gemini API代理服务,支持负载均衡和密钥管理",
|
||
version=current_version,
|
||
lifespan=lifespan
|
||
)
|
||
|
||
# Initialize app.state early to ensure it exists before lifespan potentially uses it
|
||
if not hasattr(app, "state"):
|
||
from starlette.datastructures import State
|
||
app.state = State()
|
||
# Set a default/initial state for update_info
|
||
app.state.update_info = {
|
||
"update_available": False,
|
||
"latest_version": None,
|
||
"error_message": "Initializing...",
|
||
"current_version": current_version # Use version read earlier
|
||
}
|
||
|
||
# 配置静态文件
|
||
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
||
|
||
# 配置中间件
|
||
setup_middlewares(app)
|
||
|
||
# 配置异常处理器
|
||
setup_exception_handlers(app)
|
||
|
||
# 配置路由
|
||
setup_routers(app)
|
||
|
||
return app
|