Files
life-echo/api/main.py

157 lines
5.0 KiB
Python
Raw Normal View History

2026-01-07 11:56:26 +08:00
"""
FastAPI 应用入口
"""
import os
import logging
from pathlib import Path
from dotenv import load_dotenv, dotenv_values
2026-01-07 11:56:26 +08:00
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
logger = logging.getLogger(__name__)
2026-01-07 11:56:26 +08:00
# 加载环境变量
env_file = Path('.env')
env_file_exists = env_file.exists()
if env_file_exists:
logger.info(f"正在从文件加载环境变量: {env_file.absolute()}")
# 先读取环境变量文件内容(用于日志记录)
env_vars = dotenv_values(env_file)
# 然后加载到环境变量中
result = load_dotenv(env_file)
# 敏感环境变量列表(只显示名称,不显示值)
sensitive_keys = ['SECRET_KEY', 'DEEPSEEK_API_KEY', 'LLM_API_KEY', 'PASSWORD', 'TOKEN', 'KEY']
# 记录加载的环境变量
loaded_vars = []
for key, value in env_vars.items():
if value is None:
continue
if any(sensitive in key.upper() for sensitive in sensitive_keys):
# 敏感信息只显示名称和部分值
masked_value = f"{value[:4]}...{value[-4:]}" if value and len(value) > 8 else "***"
loaded_vars.append(f"{key}={masked_value}")
else:
loaded_vars.append(f"{key}={value}")
logger.info(f"成功加载 {len(loaded_vars)} 个环境变量:")
for var in loaded_vars:
logger.info(f" - {var}")
else:
logger.warning(f".env 文件不存在: {env_file.absolute()}")
logger.info("尝试从系统环境变量加载配置")
load_dotenv()
2026-01-07 11:56:26 +08:00
from database import init_db
from routers import (
websocket, chapters, books, conversations, auth, memoir_state, tasks,
user, plans, orders, faqs, quota, feedback
)
2026-01-07 11:56:26 +08:00
# 初始化数据库
logger.info("正在初始化数据库...")
2026-01-07 11:56:26 +08:00
init_db()
logger.info("数据库初始化完成")
# 记录关键配置信息
logger.info("=== 应用配置信息 ===")
db_url = os.getenv('DATABASE_URL', 'postgresql://postgres:postgres@localhost:5432/life_echo')
# 隐藏密码
if '@' in db_url:
parts = db_url.split('@')
masked_url = parts[0].rsplit(':', 1)[0] + ':***@' + parts[1]
else:
masked_url = db_url
logger.info(f"数据库连接: {masked_url}")
logger.info(f"JWT 算法: {os.getenv('ALGORITHM', 'HS256')}")
logger.info(f"访问令牌过期时间: {os.getenv('ACCESS_TOKEN_EXPIRE_MINUTES', '120')} 分钟")
# LLM 配置
if os.getenv("DEEPSEEK_API_KEY"):
logger.info("LLM 提供商: DeepSeek")
logger.info(f"DeepSeek 模型: {os.getenv('DEEPSEEK_MODEL', 'deepseek-chat')}")
logger.info(f"DeepSeek Base URL: {os.getenv('DEEPSEEK_BASE_URL', 'https://api.deepseek.com')}")
elif os.getenv("LLM_API_KEY"):
logger.info("LLM 提供商: 通用 LLM")
logger.info(f"LLM 模型: {os.getenv('LLM_MODEL', 'deepseek-chat')}")
logger.info(f"LLM Base URL: {os.getenv('LLM_BASE_URL', '未设置')}")
else:
logger.warning("未检测到 LLM API Key 配置")
logger.info("===================")
2026-01-07 11:56:26 +08:00
app = FastAPI(title="Life Echo API", version="1.0.0")
@app.on_event("startup")
async def startup_event():
"""应用启动事件"""
logger.info("=" * 50)
logger.info("Life Echo API 正在启动...")
logger.info("=" * 50)
# 初始化 Redis 连接
try:
from services.redis_service import redis_service
await redis_service.get_client()
logger.info("Redis 连接已建立")
except Exception as e:
logger.warning(f"Redis 连接失败(会话存储将不可用): {e}")
@app.on_event("shutdown")
async def shutdown_event():
"""应用关闭事件"""
logger.info("Life Echo API 正在关闭...")
# 关闭 Redis 连接
try:
from services.redis_service import redis_service
await redis_service.close()
logger.info("Redis 连接已关闭")
except Exception as e:
logger.warning(f"关闭 Redis 连接失败: {e}")
2026-01-07 11:56:26 +08:00
# CORS 配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 生产环境应该限制域名
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(auth.router) # 认证路由(放在最前面)
2026-01-07 11:56:26 +08:00
app.websocket("/ws/conversation/{conversation_id}")(websocket.websocket_endpoint)
app.include_router(conversations.router)
app.include_router(chapters.router)
app.include_router(books.router)
2026-01-21 22:31:03 +01:00
app.include_router(memoir_state.router)
app.include_router(tasks.router) # 任务状态路由
app.include_router(user.router) # 用户相关路由
app.include_router(plans.router) # 订阅计划路由
app.include_router(orders.router) # 订单路由
app.include_router(faqs.router) # 常见问题路由
app.include_router(quota.router) # 配额检查路由
app.include_router(feedback.router) # 反馈路由
2026-01-07 11:56:26 +08:00
@app.get("/")
async def root():
return {"message": "Life Echo API", "version": "1.0.0"}
@app.get("/health")
async def health():
return {"status": "ok"}