feat:重新初始化

This commit is contained in:
2025-09-23 15:13:29 +08:00
parent de43a551bc
commit 842da15886
31 changed files with 2500 additions and 0 deletions

85
config/auth.py Normal file
View File

@@ -0,0 +1,85 @@
from contextvars import ContextVar
from datetime import datetime, timedelta
from typing import Optional
from fastapi import Depends
from fastapi.security import OAuth2PasswordBearer
import jwt
from passlib.context import CryptContext
from middleware.exceptions import get_credentials_exception
# 密钥和算法配置
SECRET_KEY = "sjdi@!#3ksj2780se1283"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# 密码哈希上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2 方案
# 设置默认登录接口
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="session")
# 请求上下文
context_sub: ContextVar[str] = ContextVar('sub')
# 获取数据库 加密密码
def get_password_hash(password: str):
return pwd_context.hash(password)
# 验证数据库密码
def verify_password(plain_password: str, hashed_password: str):
return pwd_context.verify(plain_password, hashed_password)
# 生成payload
def create_payload(sub: str) -> dict:
return {
"sub": sub
}
# 验证payload
def verify_payload(payload: dict) -> bool:
return "sub" in payload
# 从payload中获取用户
def get_payload_sub(payload: dict) -> str:
return payload["sub"]
# 创建token
def create_token(payload: dict, expires_delta: Optional[timedelta] = None):
# 复制一份
payload_copy = payload.copy()
# 加上有效时间
if expires_delta:
expire = datetime.now() + expires_delta
else:
expire = datetime.now() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
payload_copy.update({"exp": expire})
# 生成jwt Token
return jwt.encode(payload_copy, SECRET_KEY, algorithm=ALGORITHM)
# 验证token
async def verify_token(token: str = Depends(oauth2_scheme)):
try:
# 1. 检验token信息
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
# 2. 校验payload信息
if not verify_payload(payload):
raise get_credentials_exception()
# 3. 校验数据库中是否存在 payload中的账户信息
sub = get_payload_sub(payload)
# 4. 存储账户信息
context_sub.set(sub)
except jwt.exceptions.InvalidTokenError:
raise get_credentials_exception()

23
config/database.py Normal file
View File

@@ -0,0 +1,23 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from config.setting import settings
DATABASE_URL = f"mysql+pymysql://{settings.DB_USER}:{settings.DB_PASSWORD}@{settings.DB_HOST}:{settings.DB_PORT}/{settings.DB_NAME}"
engine = create_engine(url=DATABASE_URL)
# 会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# ORM基类
Base = declarative_base()
def get_db():
# 创建数据库会话实例
db = SessionLocal()
try:
yield db
finally:
db.close()

65
config/logging.py Normal file
View File

@@ -0,0 +1,65 @@
from loguru import logger
from pathlib import Path
from config.setting import settings
# 日志目录
LOG_DIR = Path(__file__).parent.parent.parent / "logs"
LOG_DIR.mkdir(exist_ok=True)
# 日志级别
LOG_LEVEL = settings.LOG_LEVEL.upper()
# 日志格式
STDOUT_FORMAT = (
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
"<level>{message}</level>"
)
FILE_FORMAT = (
"{time:YYYY-MM-DD HH:mm:ss.SSS} | "
"{level: <8} | "
"{name}:{function}:{line} - {message}"
)
# 移除默认处理器
logger.remove()
# 添加控制台处理器
logger.add(
sink="sys.stdout",
level=LOG_LEVEL,
format=STDOUT_FORMAT,
colorize=True,
backtrace=True, # 显示完整异常堆栈
diagnose=True, # 显示详细异常信息
)
# 添加文件处理器 - 常规日志
logger.add(
sink=LOG_DIR / "app.log",
level="INFO",
format=FILE_FORMAT,
rotation="10 MB", # 日志文件大小达到10MB时自动分割
retention="7 days", # 保留7天的日志
compression="zip", # 归档时压缩为zip
enqueue=True, # 异步写入
serialize=False, # 不使用JSON格式
)
# 添加文件处理器 - 错误日志
logger.add(
sink=LOG_DIR / "error.log",
level="ERROR",
format=FILE_FORMAT,
rotation="10 MB",
retention="30 days",
compression="zip",
enqueue=True,
serialize=False,
)
# 导出配置好的logger
__all__ = ["logger"]

19
config/setting.py Normal file
View File

@@ -0,0 +1,19 @@
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
DB_HOST: str
DB_PORT: int
DB_USER: str
DB_PASSWORD: str
DB_NAME: str
LOG_LEVEL: str
class Config:
env_file = ".env" # 指定.env文件路径
case_sensitive = False # 忽略变量名大小写
# 全局配置实例
settings = Settings()