支持自动加载.env并补充环境变量模板
- 为 configuration.py 增加项目根目录 .env 自动加载能力,启动时无需再手动 export\n- 新增 .env.example,补齐数据库、Redis、邮件、LLM 与后台 secret 等完整环境变量模板\n- 更新 .gitignore 与 README,明确 .env 的使用方式与优先级说明
This commit is contained in:
57
.env.example
Normal file
57
.env.example
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# ABOT 生产环境示例配置
|
||||||
|
# 使用方式:
|
||||||
|
# 1. 复制为 `.env`
|
||||||
|
# 2. 按实际情况替换敏感值
|
||||||
|
# 3. 直接启动 `python main.py`,程序会自动加载该文件
|
||||||
|
|
||||||
|
ABOT_ENVIRONMENT=production
|
||||||
|
ABOT_PLUGIN_DIR=plugins
|
||||||
|
|
||||||
|
ABOT_DB_POOL_NAME=wechat_boot_pool
|
||||||
|
ABOT_DB_POOL_SIZE=10
|
||||||
|
ABOT_DB_HOST=127.0.0.1
|
||||||
|
ABOT_DB_PORT=3306
|
||||||
|
ABOT_DB_USER=root
|
||||||
|
ABOT_DB_PASSWORD=please_change_me
|
||||||
|
ABOT_DB_NAME=message_archive
|
||||||
|
ABOT_DB_CHARSET=utf8mb4
|
||||||
|
|
||||||
|
ABOT_REDIS_HOST=127.0.0.1
|
||||||
|
ABOT_REDIS_PORT=6379
|
||||||
|
ABOT_REDIS_PASSWORD=
|
||||||
|
ABOT_REDIS_DB=0
|
||||||
|
|
||||||
|
ABOT_EMAIL_SMTP_SERVER=smtp.163.com
|
||||||
|
ABOT_EMAIL_SMTP_PORT=465
|
||||||
|
ABOT_EMAIL_SENDER=
|
||||||
|
ABOT_EMAIL_PASSWORD=
|
||||||
|
ABOT_EMAIL_ALERT_RECIPIENT=
|
||||||
|
|
||||||
|
ABOT_GLANCES_HOST=127.0.0.1
|
||||||
|
ABOT_GLANCES_PORT=61208
|
||||||
|
|
||||||
|
ABOT_WX_ADMIN=admin
|
||||||
|
|
||||||
|
ABOT_LLM_DEFAULT_BACKEND=dify_workflow_chat
|
||||||
|
ABOT_LLM_DIFY_API_BASE_URL=http://127.0.0.1:8080/v1
|
||||||
|
ABOT_LLM_DIFY_WORKFLOW_CHAT_API_KEY=
|
||||||
|
ABOT_LLM_DIFY_MEMBER_CONTEXT_API_KEY=
|
||||||
|
ABOT_LLM_DIFY_MESSAGE_SUMMARY_API_KEY=
|
||||||
|
ABOT_LLM_DIFY_DOUYU_REPORT_API_KEY=
|
||||||
|
ABOT_LLM_DIFY_GLOBAL_NEWS_API_KEY=
|
||||||
|
ABOT_LLM_DIFY_AUTO_REPLY_API_KEY=
|
||||||
|
|
||||||
|
ABOT_LLM_GAME_TASK_API_URL=https://api.example.com/v1/chat/completions
|
||||||
|
ABOT_LLM_GAME_TASK_API_KEY=
|
||||||
|
ABOT_LLM_GAME_TASK_MODEL=doubao-1-5-lite-32k-250115
|
||||||
|
|
||||||
|
ABOT_LLM_AUTO_REPLY_API_BASE_URL=https://api.example.com/v1
|
||||||
|
ABOT_LLM_AUTO_REPLY_API_KEY=
|
||||||
|
ABOT_LLM_AUTO_REPLY_MODEL=gpt-5.4
|
||||||
|
|
||||||
|
ABOT_LLM_IMAGE_API_BASE_URL=https://api.example.com/v1
|
||||||
|
ABOT_LLM_IMAGE_API_KEY=
|
||||||
|
ABOT_LLM_IMAGE_MODEL=gpt-image-1
|
||||||
|
|
||||||
|
# 可选:若希望后台登录会话在重启后保持稳定,建议显式配置。
|
||||||
|
ABOT_DASHBOARD_SECRET_KEY=
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
.*
|
.*
|
||||||
!.gitignore
|
!.gitignore
|
||||||
|
!.env.example
|
||||||
!.github/
|
!.github/
|
||||||
|
|
||||||
*pyc
|
*pyc
|
||||||
|
|||||||
10
README.MD
10
README.MD
@@ -126,18 +126,16 @@ sudo apt-get install -y fonts-noto-color-emoji fonts-noto-cjk fonts-wqy-microhei
|
|||||||
|
|
||||||
### 1. 配置文件
|
### 1. 配置文件
|
||||||
|
|
||||||
推荐先复制 `config.example.yaml` 为 `config.yaml`,再通过环境变量注入敏感信息:
|
推荐先复制 `config.example.yaml` 为 `config.yaml`,再在项目根目录放置 `.env` 文件。程序启动时会自动加载该文件,不需要每次手动 `export`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Linux / Mac
|
# Linux / Mac
|
||||||
cp config.example.yaml config.yaml
|
cp config.example.yaml config.yaml
|
||||||
export ABOT_DB_PASSWORD="你的数据库密码"
|
cp .env.example .env
|
||||||
export ABOT_LLM_DIFY_WORKFLOW_CHAT_API_KEY="你的 Dify Key"
|
|
||||||
|
|
||||||
# Windows PowerShell
|
# Windows PowerShell
|
||||||
Copy-Item config.example.yaml config.yaml
|
Copy-Item config.example.yaml config.yaml
|
||||||
$env:ABOT_DB_PASSWORD="你的数据库密码"
|
Copy-Item .env.example .env
|
||||||
$env:ABOT_LLM_DIFY_WORKFLOW_CHAT_API_KEY="你的 Dify Key"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`config.yaml` 现已支持 `${ENV_NAME}` / `${ENV_NAME:默认值}` 两种写法:
|
`config.yaml` 现已支持 `${ENV_NAME}` / `${ENV_NAME:默认值}` 两种写法:
|
||||||
@@ -145,6 +143,8 @@ $env:ABOT_LLM_DIFY_WORKFLOW_CHAT_API_KEY="你的 Dify Key"
|
|||||||
- `${ABOT_DB_PASSWORD}`:必须由环境变量提供,否则启动时报错
|
- `${ABOT_DB_PASSWORD}`:必须由环境变量提供,否则启动时报错
|
||||||
- `${ABOT_DB_HOST:127.0.0.1}`:若环境变量缺失,则回退默认值
|
- `${ABOT_DB_HOST:127.0.0.1}`:若环境变量缺失,则回退默认值
|
||||||
|
|
||||||
|
如果项目根目录存在 `.env`,系统会先自动加载该文件,再执行 `config.yaml` 占位符解析。
|
||||||
|
|
||||||
启动时系统会自动执行配置完整性检查,并在日志中输出脱敏后的配置快照。包含以下主要配置项:
|
启动时系统会自动执行配置完整性检查,并在日志中输出脱敏后的配置快照。包含以下主要配置项:
|
||||||
|
|
||||||
#### 数据库配置
|
#### 数据库配置
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ class Config(object):
|
|||||||
def __init__(self, config_path: str = None) -> None:
|
def __init__(self, config_path: str = None) -> None:
|
||||||
self.project_dir = os.path.dirname(os.path.abspath(__file__))
|
self.project_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
self.config_path = config_path or os.path.join(self.project_dir, "config.yaml")
|
self.config_path = config_path or os.path.join(self.project_dir, "config.yaml")
|
||||||
|
# 启动阶段优先尝试自动加载项目根目录下的 `.env`:
|
||||||
|
# 1. 用户只需要把 `.env` 放到线上目录,不必每次手动 export;
|
||||||
|
# 2. 这里刻意保持“已有系统环境变量优先”,避免覆盖运维层显式注入的值;
|
||||||
|
# 3. 不额外引入第三方依赖,保持部署门槛尽量低。
|
||||||
|
self._load_local_env_file(os.path.join(self.project_dir, ".env"))
|
||||||
self.raw_config = {}
|
self.raw_config = {}
|
||||||
self.resolved_config = {}
|
self.resolved_config = {}
|
||||||
self.unresolved_placeholders = []
|
self.unresolved_placeholders = []
|
||||||
@@ -54,6 +59,50 @@ class Config(object):
|
|||||||
yconfig = yaml.safe_load(fp) or {}
|
yconfig = yaml.safe_load(fp) or {}
|
||||||
return yconfig
|
return yconfig
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _strip_optional_quotes(value: str) -> str:
|
||||||
|
"""去掉 `.env` 中常见的首尾引号。"""
|
||||||
|
text = str(value or "").strip()
|
||||||
|
if len(text) >= 2 and text[0] == text[-1] and text[0] in {"'", '"'}:
|
||||||
|
return text[1:-1]
|
||||||
|
return text
|
||||||
|
|
||||||
|
def _load_local_env_file(self, env_path: str) -> None:
|
||||||
|
"""从本地 `.env` 文件加载环境变量。"""
|
||||||
|
if not os.path.exists(env_path):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(env_path, "r", encoding="utf-8") as env_file:
|
||||||
|
for raw_line in env_file:
|
||||||
|
line = str(raw_line or "").strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 兼容 `export KEY=value` 写法,方便 Linux 用户直接复用 shell 风格文件。
|
||||||
|
if line.startswith("export "):
|
||||||
|
line = line[len("export "):].strip()
|
||||||
|
|
||||||
|
if "=" not in line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
key = str(key or "").strip()
|
||||||
|
value = self._strip_optional_quotes(value)
|
||||||
|
if not key:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# `.env` 只在“当前进程还没有这个变量”时兜底注入:
|
||||||
|
# 1. 显式传入的系统环境变量优先级更高;
|
||||||
|
# 2. 这样本地调试和线上运维都可以覆盖 `.env` 默认值;
|
||||||
|
# 3. 也避免启动时误把运维平台上的密钥覆盖掉。
|
||||||
|
if key not in os.environ:
|
||||||
|
os.environ[key] = value
|
||||||
|
except Exception:
|
||||||
|
# `.env` 自动加载属于增强能力,不应因为格式问题直接把启动打死。
|
||||||
|
# 真正的必填项缺失会在后续 validate 阶段给出明确错误。
|
||||||
|
return
|
||||||
|
|
||||||
def _resolve_env_placeholders_in_string(self, raw_value: str, path: str) -> str:
|
def _resolve_env_placeholders_in_string(self, raw_value: str, path: str) -> str:
|
||||||
"""解析字符串中的环境变量占位符。"""
|
"""解析字符串中的环境变量占位符。"""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user