feat:重新初始化
This commit is contained in:
85
config/auth.py
Normal file
85
config/auth.py
Normal 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
23
config/database.py
Normal 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
65
config/logging.py
Normal 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
19
config/setting.py
Normal 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()
|
||||
Reference in New Issue
Block a user