Files
WonderQ-Project/WonderQ-Admin/app/config.py
duanshuwen 6245159e7c feat: 添加后台登录验证码、记住密码功能,优化媒体资源与前端规范
- 新增后台登录图形验证码功能,完善登录安全防护
- 新增登录rememberMe参数,控制Refresh Token的会话持久化策略
- 实现OSS私有桶媒体URL自动签名,统一处理图片资源的临时访问签名
- 新增素材库数据库表与上传API,规范媒体资源管理流程
- 统一前端UI图标使用@element-plus/icons-vue,重构布局图标组件
- 登录页新增验证码输入、刷新功能,添加账号记忆与记住密码逻辑
- 更新全套文档,补充API契约、技术决策记录与集成流程说明
- 修复多个业务页面的图标展示问题,新增认证流程相关测试用例
2026-08-27 07:50:59 +08:00

47 lines
2.2 KiB
Python

from functools import lru_cache
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
database_url: str = Field(default="postgresql://miniapp:miniapp_dev_password@localhost:5433/miniapp")
jwt_secret: str = Field(default="dev-only-change-me-before-production")
jwt_expires_hours: int = Field(default=8)
admin_access_expires_minutes: int = Field(default=15)
admin_refresh_expires_days: int = Field(default=7)
redis_url: str = Field(default="redis://localhost:6380/0")
admin_refresh_cookie_name: str = Field(default="wonderq_admin_refresh")
admin_refresh_cookie_secure: bool = Field(default=False)
admin_refresh_cookie_path: str = Field(default="/api/admin/auth")
admin_permission_cache_seconds: int = Field(default=300)
admin_login_rate_limit: int = Field(default=5)
admin_login_rate_window_seconds: int = Field(default=60)
admin_captcha_expires_seconds: int = Field(default=120)
log_level: str = Field(default="info")
port: int = Field(default=4000)
cors_origins: str = Field(default="*")
oss_access_key_id: str | None = Field(default=None)
oss_access_key_secret: str | None = Field(default=None)
oss_endpoint: str | None = Field(default=None)
oss_bucket_name: str | None = Field(default=None)
wechat_miniapp_appid: str | None = Field(default=None)
wechat_miniapp_secret: str | None = Field(default=None)
customer_jwt_expires_hours: int = Field(default=720)
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
@property
def sqlalchemy_url(self) -> str:
parsed = urlsplit(self.database_url)
query = urlencode([(key, value) for key, value in parse_qsl(parsed.query, keep_blank_values=True) if key != "schema"])
normalized = urlunsplit((parsed.scheme, parsed.netloc, parsed.path, query, parsed.fragment))
if normalized.startswith("postgresql://"):
return normalized.replace("postgresql://", "postgresql+psycopg://", 1)
return normalized
@lru_cache
def get_settings() -> Settings:
return Settings()