Files
zkxw/backend/app/core/config.py
T
2026-09-28 10:49:53 +08:00

225 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from pydantic import field_validator
from pydantic_settings import BaseSettings
from typing import Optional
from pathlib import Path
import os
# 已知弱/占位 JWT 密钥:启动时一律拒绝(勿再当作默认值)
_INSECURE_JWT_SECRET_KEYS = frozenset(
{
"",
"secret",
"changeme",
"change-me",
"your-secret-key",
"your-secret-key-change-in-production",
"your-secret-key-change-in-production-please-use-random-string",
}
)
_MIN_JWT_SECRET_KEY_LEN = 32
class Settings(BaseSettings):
"""应用配置"""
# 应用基础配置
APP_NAME: str = "zkxw 管理骨架"
DEBUG: bool = True
API_V1_PREFIX: str = "/api"
# 数据库配置
DATABASE_URL: str = "mysql+pymysql://root:password@localhost:3306/sanqiang"
# JWT配置(必须通过环境变量配置;禁止弱默认值)
SECRET_KEY: str = ""
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 * 24 * 60 # 30天
@field_validator("SECRET_KEY")
@classmethod
def secret_key_must_be_strong(cls, value: str) -> str:
key = (value or "").strip()
if key.lower() in _INSECURE_JWT_SECRET_KEYS or key in _INSECURE_JWT_SECRET_KEYS:
raise ValueError(
"SECRET_KEY 未配置或仍为弱占位值。"
"请在环境文件中设置至少 32 字符的随机密钥,例如:"
'python -c "import secrets; print(secrets.token_urlsafe(48))"'
)
if len(key) < _MIN_JWT_SECRET_KEY_LEN:
raise ValueError(
f"SECRET_KEY 长度不足(当前 {len(key)},至少 {_MIN_JWT_SECRET_KEY_LEN})。"
"请使用 secrets.token_urlsafe(48) 等方式生成。"
)
return key
# CORS配置
BACKEND_CORS_ORIGINS: list = [
"http://localhost:5173", # art-design-pro 开发服务器
"http://localhost:8080",
"http://localhost:8081", # uni-app H5 端口被占用时常用
"http://localhost:8082",
"http://127.0.0.1:5173",
"http://127.0.0.1:8080",
"http://127.0.0.1:8081",
"http://127.0.0.1:8082",
"http://localhost:3000", # Web开发
"http://localhost:3006", # Web开发
"http://127.0.0.1:3000",
"http://127.0.0.1:3006",
]
# 文件存储配置
STORAGE_TYPE: str = "local" # local, qiniu, oss, minio
# 本地存储配置
LOCAL_UPLOAD_DIR: str = "uploads"
LOCAL_STORAGE_URL: str = "/uploads"
# Redis 配置(用于登录会话与 refreshToken 吊销)
# 未配置 REDIS_HOST 时,后端会保持兼容:仍然只校验 JWT,不强制会话吊销。
REDIS_HOST: str = ""
REDIS_PORT: int = 6379
REDIS_DB: int = 0
REDIS_PASSWORD: str = ""
REDIS_TIMEOUT_SEC: int = 30
REDIS_SSL_ENABLED: bool = False
# 七牛云存储配置 (生产环境推荐)
QINIU_ACCESS_KEY: str = ""
QINIU_SECRET_KEY: str = ""
QINIU_BUCKET: str = ""
QINIU_DOMAIN: str = ""
# 阿里云OSS配置 (可选)
OSS_ACCESS_KEY_ID: str = ""
OSS_ACCESS_KEY_SECRET: str = ""
OSS_BUCKET: str = ""
OSS_ENDPOINT: str = ""
OSS_DOMAIN: str = ""
# MinIO配置
MINIO_ENDPOINT: str = ""
MINIO_ACCESS_KEY: str = ""
MINIO_SECRET_KEY: str = ""
MINIO_BUCKET: str = ""
MINIO_SECURE: bool = False
MINIO_BASE_URL: str = ""
# 微信支付配置 (生产环境必需)
WXPAY_MCHID: str = "" # 商户号
WXPAY_APPID: str = "" # 公众号AppID
WXPAY_MINI_APPID: str = "" # 小程序AppID
WXPAY_API_KEY: str = "" # API密钥 (V2)
WXPAY_SANDBOX: bool = False # 是否使用沙箱环境
WXPAY_NOTIFY_URL: str = "https://your-domain.com/api/payment/notify" # 支付回调地址
WXPAY_REFUND_NOTIFY_URL: str = "https://your-domain.com/api/payment/refund-notify" # 退款回调地址
# V3 API 证书配置
WXPAY_CERT_PATH: str = "" # API证书(apiclient_cert.pem)路径
WXPAY_PRIVATE_KEY_PATH: str = "" # API私钥(apiclient_key.pem)路径
WXPAY_SERIAL_NO: str = "" # 证书序列号
# SSL证书配置 (退款必需)
WXPAY_SSL_CERT: str = "" # SSL证书路径
WXPAY_SSL_KEY: str = "" # SSL私钥路径
# 微信小程序配置
WXPAY_SECRET: str = "" # 小程序Secret
# 微信模板消息ID配置
WX_TEMPLATE_ORDER_STATUS: str = "" # 订单状态通知模板ID
WX_TEMPLATE_DELIVERY_NOTICE: str = "" # 配送通知模板ID
WX_TEMPLATE_PAYMENT_SUCCESS: str = "" # 支付成功通知模板ID
WX_TEMPLATE_REFUND_RESULT: str = "" # 退款结果通知模板ID
WX_TEMPLATE_DELIVERY_WARNING: str = "" # 配送时效预警模板ID
# 微信服务号(OA 审批/抄送通知)
# 总开关:false 时不发送审批/抄送/申请人进度模板消息(模板未就绪或微信能力不可用时关闭)
WX_MP_NOTIFY_ENABLED: bool = False
WX_MP_APPID: str = ""
WX_MP_SECRET: str = ""
WX_MP_TEMPLATE_APPROVAL: str = ""
WX_MP_TEMPLATE_CC: str = ""
WX_MP_TEMPLATE_APPLICANT_PROGRESS: str = ""
# 审批模板:申请事项/操作人(申请人)/当前环节/操作时间/申请类型(流程名)
# 生产字段:thing12 / thing18 / thing7 / time17 / thing8
WX_MP_TPL_FIELD_TITLE: str = "thing12"
WX_MP_TPL_FIELD_APPLICANT: str = "thing18"
WX_MP_TPL_FIELD_NODE: str = "thing7"
WX_MP_TPL_FIELD_TIME: str = "time17"
WX_MP_TPL_FIELD_PROCESS: str = "thing8"
# 抄送模板「OA抄送通知」:业务名称/申请人/申请时间/流程名称(无当前环节)
WX_MP_TPL_CC_FIELD_TITLE: str = "thing5"
WX_MP_TPL_CC_FIELD_APPLICANT: str = "thing2"
WX_MP_TPL_CC_FIELD_TIME: str = "time3"
WX_MP_TPL_CC_FIELD_PROCESS: str = "thing1"
# 申请人进度:业务名称/流程名称/审批人/审批结果(const)/审批时间
WX_MP_TPL_PROGRESS_FIELD_BUSINESS: str = "thing5"
WX_MP_TPL_PROGRESS_FIELD_PROCESS: str = "thing1"
WX_MP_TPL_PROGRESS_FIELD_APPROVER: str = "thing6"
WX_MP_TPL_PROGRESS_FIELD_RESULT: str = "const4"
WX_MP_TPL_PROGRESS_FIELD_TIME: str = "time11"
# 与 docs/sql/zkxw.sql 基线一致:登录按租户 + sys_user.user_no
DEFAULT_TENANT_NO: str = "zkxw"
# 已废弃:无角色用户固定为 guest,不可通过环境变量提升为 super_admin
WEB_AUTH_DEFAULT_ROLE: str = "guest"
# 登录 RSA:私钥 PEM(多实例建议配置同一私钥;留空则各 Worker 共用内置默认密钥 login_rsa_default.pem)
LOGIN_RSA_PRIVATE_KEY_PEM: str = ""
# 是否允许明文 username/password 登录(仅建议本地排障临时开启;生产务必 false)
LOGIN_ALLOW_PLAINTEXT_LOGIN: bool = False
# 加密载荷内时间戳允许偏差(秒),防简单重放
LOGIN_ENCRYPT_TS_SKEW_SEC: int = 300
# 临时文件目录
TEMP_FILE_DIR: str = "temp_files"
# 全量 API 请求日志(中间件写入 sys_api_request_log)
API_REQUEST_LOG_ENABLED: bool = True
API_REQUEST_LOG_MAX_BODY: int = 65536
API_REQUEST_LOG_MAX_BODY_BYTES: int = 131072
# Web API 请求/响应加密配置
API_ENCRYPT_ENABLED: bool = False
API_ENCRYPT_FAIL_OPEN: bool = True
API_ENCRYPT_FIXED_SECRET: str = ""
API_ENCRYPT_TS_SKEW_SEC: int = 300
API_ENCRYPT_NONCE_TTL_SEC: int = 300
WX_QR_SESSION_TTL_SEC: int = 180
WX_QR_ENV_VERSION: str = "release" # release | trial | develop
WX_QR_PAGE: str = "pages/login/web-qr/index"
API_ENCRYPT_EXCLUDE_PATHS: str = (
"/,"
"/health,"
"/docs,"
"/openapi.json,"
"/api/web/auth/login-rsa-public-key,"
"/api/web/auth/login,"
"/api/web/auth/refresh-token,"
"/api/mini/auth/wechat-login,"
"/api/web/admin/attachment/upload,"
"/api/web/admin/products/upload-image,"
"/api/web/admin/categories/upload-image,"
"/api/web/admin/users/upload-avatar,"
"/api/web/auth/upload-avatar,"
"/api/invoice/download,"
"/api/web/auth/wx-qr/create,"
"/api/web/auth/wx-qr/status,"
"/api/mini/auth/web-qr-scanned,"
"/api/mini/auth/web-qr-confirm"
)
# 中国节假日日历(ailcc)同步
CN_HOLIDAY_API_BASE: str = "https://holiday.ailcc.com"
CN_HOLIDAY_HTTP_TIMEOUT_SEC: int = 15
CN_HOLIDAY_HTTP_RETRIES: int = 2
class Config:
# 支持通过 ENV_FILE 指定环境文件;未指定时回退到 backend/.env
_default_env_file = Path(__file__).resolve().parents[2] / ".env"
env_file = os.getenv("ENV_FILE", str(_default_env_file))
case_sensitive = True
settings = Settings()