You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iTi-Flask/iti/config.py

303 lines
9.5 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import os
from datetime import timedelta
import json
# 项目根目录
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
def _load_env_file():
"""加载 .env 文件(模块级别调用)"""
try:
from dotenv import load_dotenv
# 按优先级查找 .env 文件
env_files = [
".env.local", # 本地配置(最高优先级)
f".env.{os.getenv('FLASK_ENV', 'dev')}", # 环境特定配置
".env", # 通用配置
]
for env_file in env_files:
# 构建绝对路径
env_path = os.path.join(os.path.dirname(__file__), env_file)
if os.path.exists(env_path):
loaded = load_dotenv(env_path)
print(f"📝 加载环境配置: {env_path} - {loaded}")
return True
print("⚠️ 未找到 .env 文件")
return False
except ImportError:
print("⚠️ python-dotenv 未安装,跳过 .env 文件加载")
return False
# 在定义配置类之前加载环境变量
_load_env_file()
class BaseConfig:
"""基础配置类 - 所有环境共享的配置"""
# 应用配置
SECRET_KEY = os.getenv("SECRET_KEY", "dev-secret-key-change-in-production")
# 数据库配置
SQLALCHEMY_ENGINE_OPTIONS = {
"json_serializer": lambda obj: json.dumps(obj, ensure_ascii=False),
# "json_deserializer": json.loads,
}
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_RECORD_QUERIES = True
SQLALCHEMY_ECHO = False
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SQLALCHEMY_SHOW_ERROR_DETAILS = True
SQLALCHEMY_DATABASE_URI = os.getenv(
"DATABASE_URL", f"sqlite:///{BASE_DIR}/runtime/iti-flask.db"
)
# JWT 配置
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "dev-jwt-secret-key")
JWT_TOKEN_LOCATION = ["headers"]
JWT_HEADER_NAME = "Authorization"
JWT_HEADER_TYPE = "Bearer"
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1)
JWT_REFRESH_TOKEN_EXPIRES = timedelta(days=30)
# 跨域配置
CORS_ORIGINS = ["*"]
# 限流配置
RATELIMIT_ENABLED = True
RATELIMIT_KEY_PREFIX = "iti_rate_limit_"
RATELIMIT_STORAGE_URI = "memory://"
RATELIMIT_DEFAULT = "200 per hour"
# JSON 序列化配置
JSON_ENSURE_ASCII = False # 支持中文,不转义为 Unicode
JSON_SORT_KEYS = False # 不对 key 排序
# 缓存配置
CACHE_SIMPLE = {
# 类型 NullCache | SimpleCache | FileSystemCache | RedisCache | RedisSentinelCache | RedisClusterCache | UWSGICache | MemcachedCache | SASLMemcachedCache | SpreadSASLMemcachedCache
"ENABLED": True,
"CACHE_TYPE": "SimpleCache",
"CACHE_NO_NULL_WARNING": False,
"CACHE_ARGS": [],
"CACHE_OPTIONS": None,
"CACHE_DEFAULT_TIMEOUT": 0,
"CACHE_IGNORE_ERRORS": False,
"CACHE_THRESHOLD": 500,
"CACHE_KEY_PREFIX": "iti_cache_",
"CACHE_SOURCE_CHECK": False,
# # UWSGI 配置
# "CACHE_UWSGI_NAME": "mycache@localhost:3031",
# # MEMCACHED 配置
# "CACHE_MEMCACHED_SERVERS": ["localhost:11211"],
# "CACHE_MEMCACHED_USERNAME": "None",
# "CACHE_MEMCACHED_PASSWORD": "None",
# # REDIS 配置
# "CACHE_REDIS_URL": "redis://localhost:6379/2",
# "CACHE_REDIS_HOST": "localhost",
# "CACHE_REDIS_PORT": 6379,
# "CACHE_REDIS_PASSWORD": None,
# "CACHE_REDIS_DB": 0,
# "CACHE_REDIS_SENTINELS": ["localhost:26379"],
# "CACHE_REDIS_SENTINEL_MASTER": "mymaster",
# "CACHE_REDIS_CLUSTER": "localhost:6379,localhost:6380,localhost:6381",
# # FILE-SYSTEM 配置
# "CACHE_DIR": r"/tmp/iti_cache",
}
# Redis缓存配置
CACHE_REDIS = {
"ENABLED": False,
"CACHE_TYPE": "RedisCache",
"CACHE_NO_NULL_WARNING": False,
"CACHE_ARGS": [],
"CACHE_OPTIONS": None,
"CACHE_DEFAULT_TIMEOUT": 0,
"CACHE_IGNORE_ERRORS": False,
"CACHE_THRESHOLD": 500,
"CACHE_KEY_PREFIX": "iti_cache_",
"CACHE_SOURCE_CHECK": False,
# REDIS 配置
# "CACHE_REDIS_URL": "redis://localhost:6379/0",
"CACHE_REDIS_HOST": "localhost",
"CACHE_REDIS_PORT": 6379,
"CACHE_REDIS_PASSWORD": None,
"CACHE_REDIS_DB": 0,
# "CACHE_REDIS_SENTINELS": ["localhost:26379"],
# "CACHE_REDIS_SENTINEL_MASTER": "mymaster",
# "CACHE_REDIS_CLUSTER": "localhost:6379,localhost:6380,localhost:6381",
}
# 文件存储配置
FILE_STORAGE = {
"DEFAULT_STORAGE_TYPE": "local",
"MAX_FILE_SIZE": 100 * 1024 * 1024 * 1024, # 100GB
"TUS_CHUNK_SIZE": 5 * 1024 * 1024, # 5MB
# 本地存储配置
"LOCAL": {
"base_path": os.path.join(BASE_DIR, "runtime", "uploads"),
},
# 阿里云OSS配置
"ALIYUN_OSS": {
"access_key_id": os.getenv("ALIYUN_OSS_ACCESS_KEY_ID"),
"access_key_secret": os.getenv("ALIYUN_OSS_ACCESS_KEY_SECRET"),
"endpoint": os.getenv(
"ALIYUN_OSS_ENDPOINT"
), # 例如oss-cn-hangzhou.aliyuncs.com
"bucket": os.getenv("ALIYUN_OSS_BUCKET"),
},
# 腾讯云COS配置
"TENCENT_COS": {
"secret_id": os.getenv("TENCENT_COS_SECRET_ID"),
"secret_key": os.getenv("TENCENT_COS_SECRET_KEY"),
"region": os.getenv("TENCENT_COS_REGION"), # 例如ap-guangzhou
"bucket": os.getenv("TENCENT_COS_BUCKET"),
},
# 七牛云Kodo配置
"QINIU_KODO": {
"access_key": os.getenv("QINIU_KODO_ACCESS_KEY"),
"secret_key": os.getenv("QINIU_KODO_SECRET_KEY"),
"bucket": os.getenv("QINIU_KODO_BUCKET"),
"domain": os.getenv("QINIU_KODO_DOMAIN"), # CDN域名
},
# 华为云OBS配置
"HUAWEI_OBS": {
"access_key_id": os.getenv("HUAWEI_OBS_ACCESS_KEY_ID"),
"secret_access_key": os.getenv("HUAWEI_OBS_SECRET_ACCESS_KEY"),
"server": os.getenv(
"HUAWEI_OBS_SERVER"
), # 例如obs.cn-north-4.myhuaweicloud.com
"bucket": os.getenv("HUAWEI_OBS_BUCKET"),
},
}
# 业务日志配置
SYSLOG_MAX_BODY_CHARS = 2048
SYSLOG_ITEMS_SAMPLE = 5
# 权限配置
PERMISSION_CONFIG = {
# 超级管理员角色代码(自动跳过所有权限检查)
"SUPER_ADMIN_ROLE": "SUPER_ADMIN",
# 默认错误消息
"DEFAULT_ERROR_MESSAGE": "无权访问!",
# 默认错误代码
"DEFAULT_ERROR_CODE": 403,
# 是否启用超级管理员跳过权限检查
"SKIP_SUPER_ADMIN_DEFAULT": True,
}
class DevConfig(BaseConfig):
"""开发环境配置"""
DEBUG = True
TESTING = False
# 开发环境数据库
SQLALCHEMY_DATABASE_URI = os.getenv(
"DATABASE_URL", f"sqlite:///{BASE_DIR}/runtime/iti-flask_dev.db"
)
SQLALCHEMY_ECHO = True # 开发环境打印 SQL
SQLALCHEMY_SHOW_ERROR_DETAILS = True
# JSON 配置(开发环境:格式化输出,方便调试)
JSON_INDENT = 2 # 缩进 2 个空格
JSON_SEPARATORS = (", ", ": ") # 使用空格分隔
# JWT 开发环境配置
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=24) # 开发环境延长有效期
# JWT_ACCESS_TOKEN_EXPIRES = timedelta(seconds=5) # 测试过期
# 限流配置(开发环境较宽松)
RATELIMIT_ENABLED = True
RATELIMIT_DEFAULT = "1000 per hour"
class TestConfig(BaseConfig):
"""测试环境配置"""
DEBUG = False
TESTING = True
# 测试数据库(使用内存数据库)
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
SQLALCHEMY_ECHO = False
SQLALCHEMY_SHOW_ERROR_DETAILS = False
# JSON 配置(测试环境:与生产一致)
JSON_INDENT = None # 不缩进
JSON_SEPARATORS = (",", ":") # 紧凑分隔符
# 限流配置
RATELIMIT_ENABLED = False # 测试环境禁用限流
class ProdConfig(BaseConfig):
"""生产环境配置"""
DEBUG = False
TESTING = False
# 生产环境必须使用环境变量
SECRET_KEY = os.getenv("SECRET_KEY")
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY")
# 生产环境数据库
SQLALCHEMY_DATABASE_URI = os.getenv(
"DATABASE_URL", f"sqlite:///{BASE_DIR}/runtime/iti-flask_prod.db"
)
# 处理 PostgreSQL URL 格式
if SQLALCHEMY_DATABASE_URI and SQLALCHEMY_DATABASE_URI.startswith("postgres://"):
SQLALCHEMY_DATABASE_URI = SQLALCHEMY_DATABASE_URI.replace(
"postgres://", "postgresql://", 1
)
SQLALCHEMY_SHOW_ERROR_DETAILS = False
# 限流配置(生产环境严格)
RATELIMIT_STORAGE_URL = os.getenv("REDIS_URL", "memory://")
RATELIMIT_DEFAULT = "100 per hour"
# JSON 配置(生产环境:紧凑输出,减小体积)
JSON_INDENT = None # 不缩进
JSON_SEPARATORS = (",", ":") # 紧凑分隔符
# 性能优化
SQLALCHEMY_POOL_SIZE = 10
SQLALCHEMY_POOL_TIMEOUT = 30
SQLALCHEMY_POOL_RECYCLE = 3600
# 配置字典
config = {
"dev": DevConfig,
"test": TestConfig,
"prod": ProdConfig,
"default": DevConfig,
}
def get_config(env_name=None):
"""
根据环境名称获取配置类
Args:
env_name: 环境名称 ('dev', 'test', 'prod')
如果为 None 则从环境变量 FLASK_ENV 读取
Returns:
配置类
"""
if env_name is None:
env_name = os.getenv("FLASK_ENV", "dev")
return config.get(env_name, config["default"])