116 lines
3.5 KiB
Python
116 lines
3.5 KiB
Python
"""
|
||
应用配置管理
|
||
"""
|
||
from pydantic_settings import BaseSettings
|
||
from typing import List
|
||
from urllib.parse import quote_plus
|
||
import os
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
"""应用配置类"""
|
||
|
||
# 应用信息
|
||
APP_NAME: str = "NEX Docus"
|
||
APP_VERSION: str = "1.0.0"
|
||
DEBUG: bool = True
|
||
|
||
# 服务器配置
|
||
HOST: str = "0.0.0.0"
|
||
PORT: int = 8000
|
||
|
||
# 数据库配置
|
||
DB_HOST: str
|
||
DB_PORT: int = 3306
|
||
DB_USER: str
|
||
DB_PASSWORD: str
|
||
DB_NAME: str
|
||
DB_CHARSET: str = "utf8mb4"
|
||
|
||
@property
|
||
def DATABASE_URL(self) -> str:
|
||
"""数据库连接URL"""
|
||
# URL 编码密码,防止特殊字符导致解析错误
|
||
password = quote_plus(self.DB_PASSWORD)
|
||
return f"mysql+aiomysql://{self.DB_USER}:{password}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}?charset={self.DB_CHARSET}"
|
||
|
||
@property
|
||
def SYNC_DATABASE_URL(self) -> str:
|
||
"""同步数据库连接URL(用于 Alembic)"""
|
||
# URL 编码密码
|
||
password = quote_plus(self.DB_PASSWORD)
|
||
return f"mysql+pymysql://{self.DB_USER}:{password}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}?charset={self.DB_CHARSET}"
|
||
|
||
# Redis 配置
|
||
REDIS_HOST: str
|
||
REDIS_PORT: int = 6379
|
||
REDIS_PASSWORD: str
|
||
REDIS_DB: int = 8
|
||
|
||
@property
|
||
def REDIS_URL(self) -> str:
|
||
"""Redis 连接URL"""
|
||
return f"redis://:{self.REDIS_PASSWORD}@{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
|
||
|
||
# JWT 配置
|
||
SECRET_KEY: str
|
||
ALGORITHM: str = "HS256"
|
||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 1440 # 24小时
|
||
|
||
# 用户配置
|
||
DEFAULT_USER_PASSWORD: str = "User@123456" # 新用户默认密码
|
||
|
||
# 文件存储配置
|
||
STORAGE_ROOT: str = "/data/nex_docus_store"
|
||
PROJECTS_PATH: str = "/data/nex_docus_store/projects"
|
||
USERS_PATH: str = "/data/nex_docus_store/users"
|
||
TEMP_PATH: str = "/data/nex_docus_store/temp"
|
||
|
||
# 头像上传配置
|
||
AVATAR_MAX_SIZE: int = 1 * 1024 * 1024 # 1MB
|
||
|
||
# RAG 分块配置
|
||
CHUNK_SIZE: int = 800 # 单个文档分块的字符数
|
||
CHUNK_OVERLAP: int = 150 # 相邻分块之间的重叠字符数
|
||
|
||
# 跨域配置
|
||
CORS_ORIGINS: List[str] = ["http://localhost:5173", "http://localhost:3000"]
|
||
|
||
# 日志配置
|
||
LOG_LEVEL: str = "INFO"
|
||
LOG_PATH: str = "logs"
|
||
|
||
class Config:
|
||
env_file = ".env"
|
||
case_sensitive = True
|
||
|
||
def security_warnings(self) -> List[str]:
|
||
"""启动自检:返回仍在使用的占位/默认敏感配置说明(不阻断启动,仅告警)。"""
|
||
warnings: List[str] = []
|
||
if self.SECRET_KEY in INSECURE_SECRET_KEYS or len(self.SECRET_KEY) < 16:
|
||
warnings.append(
|
||
"SECRET_KEY 仍是占位值或过短,任何人都可伪造 JWT。"
|
||
"请在 .env 中设置为随机值:openssl rand -hex 32"
|
||
)
|
||
if self.DEFAULT_USER_PASSWORD in DEFAULT_INSECURE_PASSWORDS:
|
||
warnings.append(
|
||
"DEFAULT_USER_PASSWORD 仍是仓库内置默认值,管理员新建的账号可被猜到。"
|
||
"请在部署配置中改为随机初始密码"
|
||
)
|
||
return warnings
|
||
|
||
|
||
# 仓库/模板里出现过的占位值:命中即视为未修改
|
||
INSECURE_SECRET_KEYS = {
|
||
"your-secret-key-change-me-in-production",
|
||
"your-secret-key-change-me-in-production-use-openssl-rand-hex-32",
|
||
"change_me",
|
||
"change-me",
|
||
"secret",
|
||
}
|
||
DEFAULT_INSECURE_PASSWORDS = {"User@123", "User@123456"}
|
||
|
||
|
||
# 创建配置实例
|
||
settings = Settings()
|