mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-21 04:46:26 +00:00
1、后端优化多环境动态管理:python3 main.py run --env=dev(dev、test、prod,可以不加--env,默认dev)
2、前端加入切换主题功能(布局页面头部背景未生效,待优化)
This commit is contained in:
+254
-194
@@ -1,20 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
from datetime import date
|
||||
from functools import lru_cache
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Union
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Union, Literal
|
||||
from pydantic import MongoDsn, RedisDsn
|
||||
from pydantic_settings import BaseSettings
|
||||
from sqlalchemy import URL
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from uvicorn.config import LifespanType
|
||||
|
||||
from app.common.enums import Environment
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""系统配置类"""
|
||||
|
||||
BANNER: ClassVar[str] = """
|
||||
model_config = SettingsConfigDict(
|
||||
env_file_encoding="utf-8",
|
||||
case_sensitive=True,
|
||||
)
|
||||
|
||||
ENVIRONMENT: Literal["dev", "test", "prod"]
|
||||
|
||||
BANNER: ClassVar[str] = f"""
|
||||
______ _ _
|
||||
| ____| | | /\ (_)
|
||||
| |__ __ _ ___| |_ / \ _ __ _
|
||||
@@ -30,53 +37,134 @@ class Settings(BaseSettings):
|
||||
# 项目根目录
|
||||
BASE_DIR: Path = Path(__file__).parent.parent.parent
|
||||
|
||||
# 服务器配置
|
||||
SERVER_HOST: str = 'localhost' # 允许访问的IP地址
|
||||
SERVER_PORT: int = 8000 # 服务端口
|
||||
RELOAD: bool = True # 是否自动重启
|
||||
WORKERS: int = 1 # 启动进程数
|
||||
FACTORY: bool = True # 是否使用异步模式
|
||||
LIMIT_CONCURRENCY: int = 1000 # 最大并发连接数
|
||||
BACKLOG: int = 2048 # 等待队列最大连接数
|
||||
LIMIT_MAX_REQUESTS: int = 4094 # HTTP最大请求数
|
||||
TIMEOUT_KEEP_ALIVE: int = 5 # 保持连接时间(秒)
|
||||
LIFESPAN: LifespanType = "on" # 生命周期模式
|
||||
# ================================================= #
|
||||
# ******************* 服务器配置 ****************** #
|
||||
# ================================================= #
|
||||
SERVER_HOST: str # 允许访问的IP地址
|
||||
SERVER_PORT: int # 服务端口
|
||||
RELOAD: bool # 是否自动重启
|
||||
FACTORY: bool # 是否使用异步模式
|
||||
LIFESPAN: LifespanType # 生命周期模式
|
||||
WORKERS: int # 启动进程数
|
||||
LIMIT_CONCURRENCY: int # 最大并发连接数
|
||||
BACKLOG: int # 等待队列最大连接数
|
||||
LIMIT_MAX_REQUESTS: int # HTTP最大请求数
|
||||
TIMEOUT_KEEP_ALIVE: int # 保持连接时间(秒)
|
||||
|
||||
# ================================================= #
|
||||
# ******************* API文档配置 ****************** #
|
||||
# ================================================= #
|
||||
DEBUG: bool = True # 调试模式
|
||||
TITLE: str = "🎉 Fastapi Vue Admin 🎉" # 文档标题
|
||||
VERSION: str = '0.1.0' # 版本号
|
||||
DESCRIPTION: str = "该项目是一个基于python的web服务框架,基于fastapi和sqlalchemy实现。" # 文档描述
|
||||
SUMMARY: str | None = "接口汇总" # 文档概述
|
||||
DOCS_URL: str | None = "/docs" # Swagger UI路径
|
||||
REDOC_URL: str | None = "/redoc" # ReDoc路径
|
||||
ROOT_PATH: str = "/api/v1" # API路由前缀
|
||||
DEBUG: bool # 调试模式
|
||||
TITLE: str # 文档标题
|
||||
VERSION: str # 版本号
|
||||
DESCRIPTION: str # 文档描述
|
||||
SUMMARY: str | None # 文档概述
|
||||
DOCS_URL: str | None # Swagger UI路径
|
||||
REDOC_URL: str | None # ReDoc路径
|
||||
ROOT_PATH: str # API路由前缀
|
||||
|
||||
# ================================================= #
|
||||
# ******************** 跨域配置 ******************** #
|
||||
# ================================================= #
|
||||
CORS_ORIGIN_ENABLE: bool = True # 是否启用跨域
|
||||
ALLOW_ORIGINS: List[str] = ["*"] # 允许的域名列表
|
||||
ALLOW_METHODS: List[str] = ["*"] # 允许的HTTP方法
|
||||
ALLOW_HEADERS: List[str] = ["*"] # 允许的请求头
|
||||
ALLOW_CREDENTIALS: bool = True # 是否允许携带cookie
|
||||
CORS_ORIGIN_ENABLE: bool # 是否启用跨域
|
||||
ALLOW_ORIGINS: List[str] # 允许的域名列表
|
||||
ALLOW_METHODS: List[str] # 允许的HTTP方法
|
||||
ALLOW_HEADERS: List[str] # 允许的请求头
|
||||
ALLOW_CREDENTIALS: bool # 是否允许携带cookie
|
||||
|
||||
# ================================================= #
|
||||
# ******************* 登录认证配置 ****************** #
|
||||
# ================================================= #
|
||||
SECRET_KEY: str = "vgb0tnl9d58+6n-6h-ea&u^1#s0ccp!794=krylxcjq75vzps$" # JWT密钥
|
||||
ALGORITHM: str = "HS256" # JWT算法
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 # access_token过期时间(分钟)
|
||||
REFRESH_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # refresh_token过期时间(分钟)
|
||||
TOKEN_TYPE: str = "bearer" # token类型
|
||||
JWT_REDIS_EXPIRE_MINUTES: int = 30 # redis缓存过期时间(分钟)
|
||||
SECRET_KEY: str # JWT密钥
|
||||
ALGORITHM: str # JWT算法
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int # access_token过期时间(分钟)
|
||||
REFRESH_TOKEN_EXPIRE_MINUTES: int # refresh_token过期时间(分钟)
|
||||
TOKEN_TYPE: str # token类型
|
||||
JWT_REDIS_EXPIRE_MINUTES: int # redis缓存过期时间(分钟)
|
||||
|
||||
# ================================================= #
|
||||
# ******************** 数据库配置 ******************* #
|
||||
# ================================================= #
|
||||
SQL_DB_ENABLE: bool # 是否启用数据库
|
||||
DATABASE_ECHO: bool # 是否显示SQL日志
|
||||
ECHO_POOL: bool # 是否显示连接池日志
|
||||
POOL_SIZE: int # 连接池大小
|
||||
MAX_OVERFLOW: int # 最大溢出连接数
|
||||
POOL_TIMEOUT: int # 连接超时时间(秒)
|
||||
POOL_RECYCLE: int # 连接回收时间(秒)
|
||||
POOL_PRE_PING: bool # 是否开启连接预检
|
||||
FUTURE: bool # 是否使用SQLAlchemy 2.0特性
|
||||
AUTOCOMMIT: bool # 是否自动提交
|
||||
AUTOFETCH: bool # 是否自动获取
|
||||
EXPIRE_ON_COMMIT: bool # 是否在提交时过期
|
||||
# SQLite数据库连接
|
||||
DB_DRIVER: str
|
||||
SQLITE_DB_NAME: str
|
||||
|
||||
# MySQL数据库连接
|
||||
MYSQL_USER: str
|
||||
MYSQL_PASSWORD: str
|
||||
MYSQL_HOST: str
|
||||
MYSQL_PORT: int
|
||||
MYSQL_DB_NAME: str
|
||||
|
||||
# PostgreSQL数据库连接
|
||||
POSTGRESQL_USER: str
|
||||
POSTGRESQL_PASSWORD: str
|
||||
POSTGRESQL_HOST: str
|
||||
POSTGRESQL_PORT: int
|
||||
POSTGRESQL_DB_NAME: str
|
||||
|
||||
# ================================================= #
|
||||
# ******************** MongoDB配置 ******************* #
|
||||
# ================================================= #
|
||||
MONGO_DB_ENABLE: bool # 是否启用MongoDB
|
||||
MONGO_DB_HOST: str
|
||||
MONGO_DB_PORT: int
|
||||
MONGO_DB_NAME: str
|
||||
|
||||
# ================================================= #
|
||||
# ******************** Redis配置 ******************* #
|
||||
# ================================================= #
|
||||
REDIS_ENABLE: bool # 是否启用Redis
|
||||
REDIS_HOST: str
|
||||
REDIS_PORT: int
|
||||
REDIS_DB: int
|
||||
|
||||
# ================================================= #
|
||||
# ******************** 验证码配置 ******************* #
|
||||
# ================================================= #
|
||||
CAPTCHA_ENABLE: bool # 是否启用验证码
|
||||
CAPTCHA_EXPIRE_SECONDS: int # 验证码过期时间(秒)
|
||||
CAPTCHA_FONT_SIZE: int # 字体大小
|
||||
CAPTCHA_FONT_PATH: Path = 'static/assets/font/Arial.ttf' # 字体路径
|
||||
|
||||
# ================================================= #
|
||||
# ********************* 日志配置 ******************* #
|
||||
# ================================================= #
|
||||
LOGGER_NAME: str = date.today().strftime(r'%Y-%m-%d.log') # 日志文件名
|
||||
LOGGER_FILEPATH: Path = BASE_DIR.joinpath('logs', LOGGER_NAME) # 日志文件路径
|
||||
BACKUPCOUNT: int # 日志文件备份数
|
||||
WHEN: str # 日志分割时间
|
||||
INTERVAL: int # 日志分割间隔
|
||||
ENCODING: str # 日志编码
|
||||
LOGGER_LEVEL: str # 日志级别
|
||||
LOGGER_FORMAT: str # 日志格式
|
||||
OPERATION_LOG_RECORD: bool # 是否记录操作日志
|
||||
OPERATION_RECORD_METHOD: List[str] # 需要记录的请求方法
|
||||
IGNORE_OPERATION_FUNCTION: List[str] # 忽略记录的函数
|
||||
|
||||
# ================================================= #
|
||||
# ******************* Gzip压缩配置 ******************* #
|
||||
# ================================================= #
|
||||
GZIP_ENABLE: bool # 是否启用Gzip
|
||||
GZIP_MIN_SIZE: int # 最小压缩大小(字节)
|
||||
GZIP_COMPRESS_LEVEL: int # 压缩级别(1-9)
|
||||
|
||||
# ================================================= #
|
||||
# ***************** 演示模型配置 ***************** #
|
||||
# ================================================= #
|
||||
DEMO_ENABLE: bool = False # 是否开启演示模式
|
||||
DEMO_ENABLE: bool = False # 是否开启演示模式
|
||||
DEMO_WHITE_LIST_PATH: List[str] = [ # 演示白名单
|
||||
"/system/auth/login",
|
||||
"/system/auth/token/refresh",
|
||||
@@ -89,25 +177,21 @@ class Settings(BaseSettings):
|
||||
# ================================================= #
|
||||
# ***************** 静态文件配置 ***************** #
|
||||
# ================================================= #
|
||||
STATIC_ENABLE: bool = True # 是否启用静态文件
|
||||
STATIC_ENABLE: bool = True # 是否启用静态文件
|
||||
STATIC_URL: str = "/static" # 访问路由
|
||||
STATIC_DIR: str = "static" # 目录名
|
||||
STATIC_DIR: str = "static" # 目录名
|
||||
STATIC_ROOT: Path = BASE_DIR.joinpath(STATIC_DIR) # 绝对路径
|
||||
|
||||
# ================================================= #
|
||||
# ***************** 模板文件配置 ***************** #
|
||||
# ***************** 动态文件配置 ***************** #
|
||||
# ================================================= #
|
||||
TEMPLATES_ENABLE: bool = True # 是否启用模板
|
||||
TEMPLATES_URL: str = "/templates" # 访问路由
|
||||
TEMPLATES_DIR: str = "templates" # 目录名
|
||||
TEMPLATES_ROOT: Path = BASE_DIR.joinpath(TEMPLATES_DIR) # 绝对路径
|
||||
|
||||
# ================================================= #
|
||||
# ******************* 临时文件配置 ******************* #
|
||||
# ================================================= #
|
||||
UPLOAD_PROFILE_PATH: Path = Path('static/upload')
|
||||
UPLOAD_FILE_PATH: Path = STATIC_ROOT.joinpath('upload') # 上传目录
|
||||
DOWNLOAD_FILE_PATH: Path = STATIC_ROOT.joinpath('download') # 下载目录
|
||||
PROFILES_ENABLE: bool = True # 是否启用模板
|
||||
PROFILES_URL: str = "/profiles" # 访问路由
|
||||
PROFILES_DIR: str = "profiles" # 目录名
|
||||
PROFILES_ROOT: Path = BASE_DIR.joinpath(PROFILES_DIR) # 绝对路径
|
||||
UPLOAD_PROFILE_PATH: Path = Path('profiles/upload')
|
||||
UPLOAD_FILE_PATH: Path = PROFILES_ROOT.joinpath('upload') # 上传目录
|
||||
DOWNLOAD_FILE_PATH: Path = PROFILES_ROOT.joinpath('download') # 下载目录
|
||||
UPLOAD_MACHINE: str = 'A' # 上传机器标识
|
||||
ALLOWED_EXTENSIONS: list[str] = [ # 允许的文件类型
|
||||
# 图片
|
||||
@@ -119,15 +203,8 @@ class Settings(BaseSettings):
|
||||
# 视频
|
||||
'.mp4', '.avi', '.rmvb'
|
||||
]
|
||||
MAX_FILE_SIZE: int = 10 * 1024 * 1024 # 最大文件大小(10MB)
|
||||
MAX_FILE_SIZE: int # 最大文件大小(10MB)
|
||||
|
||||
# ================================================= #
|
||||
# ******************** 缓存配置 ******************* #
|
||||
# ================================================= #
|
||||
CACHE_ENABLE: bool = True # 是否启用缓存
|
||||
CACHES_DIR: Path = Path('static/caches') # 缓存目录
|
||||
CACHE_EXPIRE_SECONDS: int = 300 # 缓存过期时间(秒)
|
||||
|
||||
# ================================================= #
|
||||
# ***************** Swagger配置 ***************** #
|
||||
# ================================================= #
|
||||
@@ -135,139 +212,104 @@ class Settings(BaseSettings):
|
||||
SWAGGER_JS_URL: Path = "static/swagger/swagger-ui/swagger-ui-bundle.js"
|
||||
REDOC_JS_URL: Path = "static/swagger/redoc/bundles/redoc.standalone.js"
|
||||
FAVICON_URL: Path = "static/swagger/favicon.png"
|
||||
|
||||
# ================================================= #
|
||||
# ******************** 数据库配置 ******************* #
|
||||
# ================================================= #
|
||||
SQL_DB_ENABLE: bool = True # 是否启用数据库
|
||||
DATABASE_ECHO: bool = False # 是否显示SQL日志
|
||||
ECHO_POOL: bool = False # 是否显示连接池日志
|
||||
POOL_SIZE: int = 20 # 连接池大小
|
||||
MAX_OVERFLOW: int = 10 # 最大溢出连接数
|
||||
POOL_TIMEOUT: int = 30 # 连接超时时间(秒)
|
||||
POOL_RECYCLE: int = 1800 # 连接回收时间(秒)
|
||||
POOL_PRE_PING: bool = True # 是否开启连接预检
|
||||
FUTURE: bool = True # 是否使用SQLAlchemy 2.0特性
|
||||
AUTOCOMMIT: bool = False # 是否自动提交
|
||||
AUTOFETCH: bool = False # 是否自动获取
|
||||
EXPIRE_ON_COMMIT: bool = False # 是否在提交时过期
|
||||
# SQLite数据库连接
|
||||
SQLALCHEMY_DATABASE_URI: str | URL = f"sqlite+aiosqlite:///{BASE_DIR.joinpath('dev_sql.db')}?characterEncoding=UTF-8"
|
||||
# MySQL数据库连接
|
||||
# SQLALCHEMY_DATABASE_URI: str | URL = f"mysql+asyncmy://root:123456@localhost:3306/fastapi_vue_admin?charset=utf8mb4"
|
||||
# PostgreSQL数据库连接
|
||||
# SQLALCHEMY_DATABASE_URI: str | URL = f"postgresql+asyncpg://postgres:123456@localhost:5432/fastapi_vue_admin"
|
||||
|
||||
# ================================================= #
|
||||
# ******************** MongoDB配置 ******************* #
|
||||
# ================================================= #
|
||||
MONGO_DB_ENABLE: bool = True # 是否启用MongoDB
|
||||
MONGO_DB_NAME: str = 'fastapi_vue_admin' # 数据库名
|
||||
# 连接地址
|
||||
MONGO_DB_URL: MongoDsn = f"mongodb://localhost:27017/{MONGO_DB_NAME}"
|
||||
|
||||
# ================================================= #
|
||||
# ******************** Redis配置 ******************* #
|
||||
# ================================================= #
|
||||
REDIS_ENABLE: bool = True # 是否启用Redis
|
||||
REDIS_URL: RedisDsn = f"redis://localhost:6379/1"
|
||||
|
||||
# ================================================= #
|
||||
# ******************** 验证码配置 ******************* #
|
||||
# ================================================= #
|
||||
CAPTCHA_ENABLE: bool = True # 是否启用验证码
|
||||
CAPTCHA_EXPIRE_SECONDS: int = 60 # 验证码过期时间(秒)
|
||||
CAPTCHA_FONT_PATH: Path = 'static/assets/font/Arial.ttf' # 字体路径
|
||||
CAPTCHA_FONT_SIZE: int = 40
|
||||
|
||||
# ================================================= #
|
||||
# ********************* 日志配置 ******************* #
|
||||
# ================================================= #
|
||||
LOGGER_NAME: str = date.today().strftime(r'%Y-%m-%d.log') # 日志文件名
|
||||
LOGGER_FILEPATH: Path = BASE_DIR.joinpath('logs', LOGGER_NAME) # 日志文件路径
|
||||
BACKUPCOUNT: int = 10 # 日志文件备份数
|
||||
WHEN: str = 'MIDNIGHT' # 日志分割时间
|
||||
INTERVAL: int = 1 # 日志分割间隔
|
||||
ENCODING: str = 'utf-8' # 日志编码
|
||||
LOGGER_LEVEL: str = 'INFO' # 日志级别
|
||||
# 日志格式
|
||||
LOGGER_FORMAT: str = '%(asctime)s - %(levelname)s - [%(name)s:%(filename)s:%(funcName)s:%(lineno)d] %(message)s'
|
||||
|
||||
# 日志配置字典
|
||||
LOGGING_CONFIG: dict[str, Any] = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"()": "uvicorn.logging.DefaultFormatter",
|
||||
"fmt": LOGGER_FORMAT,
|
||||
"use_colors": None,
|
||||
},
|
||||
"access": {
|
||||
"()": "uvicorn.logging.AccessFormatter",
|
||||
"fmt": LOGGER_FORMAT,
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
"access_console": {
|
||||
"formatter": "access",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stdout",
|
||||
},
|
||||
"file": {
|
||||
"formatter": "default",
|
||||
"class": "logging.handlers.TimedRotatingFileHandler",
|
||||
"filename": LOGGER_FILEPATH,
|
||||
"when": WHEN,
|
||||
"backupCount": BACKUPCOUNT,
|
||||
"encoding": ENCODING,
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"uvicorn": {"handlers": ["file", "console"], "level": LOGGER_LEVEL, "propagate": False},
|
||||
"uvicorn.error": {"handlers": ["file", "console"], "level": LOGGER_LEVEL, "propagate": False},
|
||||
"uvicorn.access": {"handlers": ["file", "access_console"], "level": LOGGER_LEVEL, "propagate": False},
|
||||
},
|
||||
}
|
||||
|
||||
REQUEST_LOG_RECORD: bool = True # 是否记录请求日志
|
||||
OPERATION_LOG_RECORD: bool = True # 是否记录操作日志
|
||||
OPERATION_RECORD_METHOD: List[str] = ["POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] # 需要记录的请求方法
|
||||
IGNORE_OPERATION_FUNCTION: List[str] = ["get_captcha_for_login"] # 忽略记录的函数
|
||||
|
||||
# ================================================= #
|
||||
# ******************* Gzip压缩配置 ******************* #
|
||||
# ================================================= #
|
||||
GZIP_ENABLE: bool = True # 是否启用Gzip
|
||||
GZIP_MIN_SIZE: int = 1000 # 最小压缩大小(字节)
|
||||
GZIP_COMPRESS_LEVEL: int = 9 # 压缩级别(1-9)
|
||||
|
||||
|
||||
# ================================================= #
|
||||
# ******************* 初始化数据 ****************** #
|
||||
# ================================================= #
|
||||
SCRIPT_DIR: Path = 'app/scripts/data' # 管理员路由目录
|
||||
SCRIPT_DIR: Path = BASE_DIR.joinpath('app/scripts/data') # 管理员路由目录
|
||||
|
||||
# ================================================= #
|
||||
# ******************* 其他配置 ******************* #
|
||||
# ================================================= #
|
||||
# 中间件列表
|
||||
MIDDLEWARES: List[Optional[str]] = [
|
||||
"app.core.middlewares.CustomCORSMiddleware" if CORS_ORIGIN_ENABLE else None,
|
||||
"app.core.middlewares.RequestLogMiddleware" if REQUEST_LOG_RECORD else None,
|
||||
"app.core.middlewares.CustomGZipMiddleware" if GZIP_ENABLE else None,
|
||||
"app.core.middlewares.DemoEnvMiddleware" if DEMO_ENABLE else None,
|
||||
]
|
||||
@property
|
||||
def get_middlewares(self) -> List[Optional[str]]:
|
||||
"""获取项目根目录"""
|
||||
# 中间件列表
|
||||
MIDDLEWARES: List[Optional[str]] = [
|
||||
"app.core.middlewares.CustomCORSMiddleware" if self.CORS_ORIGIN_ENABLE else None,
|
||||
"app.core.middlewares.RequestLogMiddleware" if self.OPERATION_LOG_RECORD else None,
|
||||
"app.core.middlewares.CustomGZipMiddleware" if self.GZIP_ENABLE else None,
|
||||
"app.core.middlewares.DemoEnvMiddleware" if self.DEMO_ENABLE else None,
|
||||
]
|
||||
return MIDDLEWARES
|
||||
|
||||
# 事件列表
|
||||
EVENTS: List[Optional[str]] = [
|
||||
# "app.core.database.mongodb_connect" if MONGO_DB_ENABLE else None,
|
||||
"app.core.database.redis_connect" if REDIS_ENABLE else None,
|
||||
]
|
||||
@property
|
||||
def get_events(self) -> List[Optional[str]]:
|
||||
|
||||
# 事件列表
|
||||
EVENTS: List[Optional[str]] = [
|
||||
# "app.core.database.mongodb_connect" if MONGO_DB_ENABLE else None,
|
||||
"app.core.database.redis_connect" if self.REDIS_ENABLE else None,
|
||||
]
|
||||
return EVENTS
|
||||
|
||||
@property
|
||||
def get_uvicorn_log_config(self) -> dict[str, Any]:
|
||||
# 日志配置字典
|
||||
LOGGING_CONFIG= {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"()": "uvicorn.logging.DefaultFormatter",
|
||||
"fmt": self.LOGGER_FORMAT,
|
||||
"use_colors": None,
|
||||
},
|
||||
"access": {
|
||||
"()": "uvicorn.logging.AccessFormatter",
|
||||
"fmt": self.LOGGER_FORMAT,
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
"access_console": {
|
||||
"formatter": "access",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stdout",
|
||||
},
|
||||
"file": {
|
||||
"formatter": "default",
|
||||
"class": "logging.handlers.TimedRotatingFileHandler",
|
||||
"filename": self.LOGGER_FILEPATH,
|
||||
"when": self.WHEN,
|
||||
"backupCount": self.BACKUPCOUNT,
|
||||
"encoding": self.ENCODING,
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"uvicorn": {"handlers": ["file", "console"], "level": self.LOGGER_LEVEL, "propagate": False},
|
||||
"uvicorn.error": {"handlers": ["file", "console"], "level": self.LOGGER_LEVEL, "propagate": False},
|
||||
"uvicorn.access": {"handlers": ["file", "access_console"], "level": self.LOGGER_LEVEL, "propagate": False},
|
||||
},
|
||||
}
|
||||
|
||||
return LOGGING_CONFIG
|
||||
|
||||
@property
|
||||
def get_database_uri(self) -> str:
|
||||
supported_db_drivers = ("sqlite", "mysql", "postgresql")
|
||||
if self.DB_DRIVER not in supported_db_drivers:
|
||||
raise ValueError(f"数据库驱动不支持: {self.DB_DRIVER}, 请选择 {supported_db_drivers}")
|
||||
if self.DB_DRIVER == "sqlite":
|
||||
return f"sqlite+aiosqlite:///{self.BASE_DIR.joinpath(self.SQLITE_DB_NAME)}?characterEncoding=UTF-8"
|
||||
elif self.DB_DRIVER == "mysql":
|
||||
return f"mysql+asyncmy://{self.MYSQL_USER}:{self.MYSQL_PASSWORD}@{self.MYSQL_HOST}:{self.MYSQL_PORT}/{self.MYSQL_DB_NAME}?charset=utf8mb4"
|
||||
else:
|
||||
return f"postgresql+asyncpg://{self.POSTGRESQL_USER}:{self.POSTGRESQL_PASSWORD}@{self.POSTGRESQL_HOST}:{self.POSTGRESQL_PORT}/{self.POSTGRESQL_DB_NAME}"
|
||||
|
||||
@property
|
||||
def get_redis_uri(self) -> RedisDsn:
|
||||
REDIS_URL: RedisDsn = f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
|
||||
return REDIS_URL
|
||||
|
||||
@property
|
||||
def get_mongodb_uri(self) -> MongoDsn:
|
||||
MONGO_DB_URL: MongoDsn = f"mongodb://{self.MONGO_DB_HOST}:{self.MONGO_DB_PORT}/{self.MONGO_DB_NAME}"
|
||||
return MONGO_DB_URL
|
||||
|
||||
@property
|
||||
def get_backend_app_attributes(self) -> Dict[str, Union[str, bool, None]]:
|
||||
@@ -293,19 +335,37 @@ class Settings(BaseSettings):
|
||||
"allow_credentials": self.ALLOW_CREDENTIALS
|
||||
}
|
||||
|
||||
class Config:
|
||||
"""配置类"""
|
||||
env_file = ".env" # 环境变量文件
|
||||
env_file_encoding = "utf-8" # 环境变量文件编码
|
||||
case_sensitive = True # 大小写敏感
|
||||
|
||||
@property
|
||||
def get_uvicorn_config(self) -> Dict[str, Any]:
|
||||
"""获取Uvicorn配置"""
|
||||
return {
|
||||
"host": self.SERVER_HOST,
|
||||
"port": self.SERVER_PORT,
|
||||
"reload": self.RELOAD,
|
||||
"log_config": self.get_uvicorn_log_config,
|
||||
"workers": self.WORKERS,
|
||||
"limit_concurrency": self.LIMIT_CONCURRENCY,
|
||||
"backlog": self.BACKLOG,
|
||||
"limit_max_requests": self.LIMIT_MAX_REQUESTS,
|
||||
"timeout_keep_alive": self.TIMEOUT_KEEP_ALIVE,
|
||||
"lifespan": self.LIFESPAN,
|
||||
"factory": self.FACTORY,
|
||||
}
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def get_settings() -> Settings:
|
||||
"""获取配置"""
|
||||
env = os.getenv("ENVIRONMENT", "dev") # 获取环境变量 dev
|
||||
env_file = f".env.{env}" # 环境变量文件 .env.dev
|
||||
return Settings(_env_file=env_file) # 加载对应环境的配置文件
|
||||
"""获取配置实例"""
|
||||
env = os.getenv('ENVIRONMENT', Environment.DEV.value)
|
||||
if env not in [e.value for e in Environment]:
|
||||
raise ValueError(f"无效的环境配置: {env}")
|
||||
|
||||
current_dir = Path(__file__).parent
|
||||
env_file = current_dir / f".env.{env}"
|
||||
|
||||
if not env_file.exists():
|
||||
raise FileNotFoundError(f"环境配置文件不存在: {env_file}")
|
||||
|
||||
return Settings(_env_file=env_file)
|
||||
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
Reference in New Issue
Block a user