Refactor config, logging, and startup structure
Introduced pkg modules for environment config, logging, and startup initialization. Replaced direct config and logging setup in main.py with modularized functions. Updated database and migration modules to use environment variables and improved DEBUG handling. Removed tool.py and migrated password utilities to pkg. Cleaned up legacy comments and unused code in models and routes.
This commit is contained in:
1
pkg/__init__.py
Normal file
1
pkg/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .password import Password
|
||||
48
pkg/env.py
Normal file
48
pkg/env.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""
|
||||
环境变量管理模块
|
||||
|
||||
负责 .env 文件的创建和环境变量的加载
|
||||
"""
|
||||
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
def ensure_env_file(env_file_path: str = '.env') -> None:
|
||||
"""
|
||||
确保 .env 文件存在,如果不存在则创建默认配置
|
||||
|
||||
:param env_file_path: .env 文件路径
|
||||
"""
|
||||
if not os.path.exists(env_file_path):
|
||||
default_env_content = """HOST=127.0.0.1
|
||||
PORT=8167
|
||||
|
||||
DEBUG=false
|
||||
|
||||
DATABASE_URL=sqlite+aiosqlite:///data.db
|
||||
"""
|
||||
with open(env_file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(default_env_content)
|
||||
print(f"Created default .env file at {env_file_path}")
|
||||
|
||||
|
||||
def load_config() -> Tuple[str, int, bool]:
|
||||
"""
|
||||
加载配置信息
|
||||
|
||||
:return: (host, port, debug) 元组
|
||||
"""
|
||||
# 确保 .env 文件存在
|
||||
ensure_env_file()
|
||||
|
||||
# 从.env文件加载环境变量
|
||||
load_dotenv('.env')
|
||||
|
||||
# 从环境变量中加载主机、端口、调试模式等配置
|
||||
host = os.getenv("HOST", "127.0.0.1")
|
||||
port = int(os.getenv("PORT", 8167))
|
||||
debug = os.getenv("DEBUG", "false").lower() in ("true", "1", "yes")
|
||||
|
||||
return host, port, debug
|
||||
75
pkg/logger.py
Normal file
75
pkg/logger.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
日志配置模块
|
||||
|
||||
负责配置 loguru 和接管标准库的日志
|
||||
"""
|
||||
|
||||
import sys
|
||||
import logging
|
||||
from loguru import logger
|
||||
|
||||
|
||||
class InterceptHandler(logging.Handler):
|
||||
"""
|
||||
拦截标准库的日志并转发到 loguru
|
||||
"""
|
||||
def emit(self, record: logging.LogRecord) -> None:
|
||||
# 获取对应的 loguru 级别
|
||||
try:
|
||||
level = logger.level(record.levelname).name
|
||||
except ValueError:
|
||||
level = record.levelno
|
||||
|
||||
# 查找调用者的帧
|
||||
frame, depth = logging.currentframe(), 2
|
||||
while frame.f_code.co_filename == logging.__file__:
|
||||
frame = frame.f_back
|
||||
depth += 1
|
||||
|
||||
logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
|
||||
|
||||
|
||||
def setup_logging(debug: bool = False) -> None:
|
||||
"""
|
||||
配置日志系统
|
||||
|
||||
:param debug: 是否启用调试模式
|
||||
"""
|
||||
# 配置 loguru
|
||||
# 移除默认的 handler
|
||||
logger.remove()
|
||||
|
||||
# 根据 DEBUG 模式配置不同的日志级别和格式
|
||||
if debug:
|
||||
logger.add(
|
||||
sys.stderr,
|
||||
level="DEBUG"
|
||||
)
|
||||
else:
|
||||
logger.add(
|
||||
sys.stderr,
|
||||
level="INFO"
|
||||
)
|
||||
|
||||
# 接管 uvicorn 的日志
|
||||
logging.getLogger("uvicorn").handlers = [InterceptHandler()]
|
||||
logging.getLogger("uvicorn.access").handlers = [InterceptHandler()]
|
||||
logging.getLogger("uvicorn.error").handlers = [InterceptHandler()]
|
||||
|
||||
# 接管 SQLAlchemy 的日志
|
||||
logging.getLogger("sqlalchemy.engine").handlers = [InterceptHandler()]
|
||||
logging.getLogger("sqlalchemy.pool").handlers = [InterceptHandler()]
|
||||
|
||||
# 设置日志级别
|
||||
if debug:
|
||||
logging.getLogger("uvicorn").setLevel(logging.DEBUG)
|
||||
logging.getLogger("uvicorn.access").setLevel(logging.DEBUG)
|
||||
logging.getLogger("uvicorn.error").setLevel(logging.DEBUG)
|
||||
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
|
||||
logging.getLogger("sqlalchemy.pool").setLevel(logging.DEBUG)
|
||||
else:
|
||||
logging.getLogger("uvicorn").setLevel(logging.INFO)
|
||||
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
|
||||
logging.getLogger("uvicorn.error").setLevel(logging.WARNING)
|
||||
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
|
||||
logging.getLogger("sqlalchemy.pool").setLevel(logging.WARNING)
|
||||
@@ -9,7 +9,7 @@ class Password():
|
||||
|
||||
@staticmethod
|
||||
def generate(
|
||||
length: int = 8
|
||||
length: int = 8
|
||||
) -> str:
|
||||
"""
|
||||
生成指定长度的随机密码。
|
||||
|
||||
44
pkg/startup.py
Normal file
44
pkg/startup.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
应用启动模块
|
||||
|
||||
负责应用启动时的初始化工作
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from loguru import logger
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from model.database import Database
|
||||
|
||||
|
||||
async def init_database() -> None:
|
||||
"""
|
||||
初始化数据库
|
||||
"""
|
||||
await Database().init_db()
|
||||
|
||||
|
||||
def mount_static_files(app: FastAPI) -> None:
|
||||
"""
|
||||
挂载静态文件目录
|
||||
|
||||
:param app: FastAPI 应用实例
|
||||
"""
|
||||
try:
|
||||
app.mount("/dist", StaticFiles(directory="dist"), name="dist")
|
||||
except RuntimeError as e:
|
||||
logger.warning(f'Unable to mount static directory: {str(e)}, starting in backend-only mode')
|
||||
|
||||
|
||||
def startup(app: FastAPI) -> None:
|
||||
"""
|
||||
执行应用启动流程
|
||||
|
||||
:param app: FastAPI 应用实例
|
||||
"""
|
||||
# 初始化数据库
|
||||
asyncio.run(init_database())
|
||||
|
||||
# 挂载静态文件
|
||||
mount_static_files(app)
|
||||
Reference in New Issue
Block a user