From 97fc6dc2a49b24e69c1133325b3239017988e7ec Mon Sep 17 00:00:00 2001 From: liuwei Date: Thu, 30 Apr 2026 15:49:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=87=AA=E5=8A=A8=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD.env=E5=B9=B6=E8=A1=A5=E5=85=85=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 configuration.py 增加项目根目录 .env 自动加载能力,启动时无需再手动 export\n- 新增 .env.example,补齐数据库、Redis、邮件、LLM 与后台 secret 等完整环境变量模板\n- 更新 .gitignore 与 README,明确 .env 的使用方式与优先级说明 --- .env.example | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 1 + README.MD | 10 ++++----- configuration.py | 49 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e43fbf1 --- /dev/null +++ b/.env.example @@ -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= diff --git a/.gitignore b/.gitignore index 2579d00..761c132 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .* !.gitignore +!.env.example !.github/ *pyc diff --git a/README.MD b/README.MD index 7cbba28..446d70e 100644 --- a/README.MD +++ b/README.MD @@ -126,18 +126,16 @@ sudo apt-get install -y fonts-noto-color-emoji fonts-noto-cjk fonts-wqy-microhei ### 1. 配置文件 -推荐先复制 `config.example.yaml` 为 `config.yaml`,再通过环境变量注入敏感信息: +推荐先复制 `config.example.yaml` 为 `config.yaml`,再在项目根目录放置 `.env` 文件。程序启动时会自动加载该文件,不需要每次手动 `export`: ```bash # Linux / Mac cp config.example.yaml config.yaml -export ABOT_DB_PASSWORD="你的数据库密码" -export ABOT_LLM_DIFY_WORKFLOW_CHAT_API_KEY="你的 Dify Key" +cp .env.example .env # Windows PowerShell Copy-Item config.example.yaml config.yaml -$env:ABOT_DB_PASSWORD="你的数据库密码" -$env:ABOT_LLM_DIFY_WORKFLOW_CHAT_API_KEY="你的 Dify Key" +Copy-Item .env.example .env ``` `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_HOST:127.0.0.1}`:若环境变量缺失,则回退默认值 +如果项目根目录存在 `.env`,系统会先自动加载该文件,再执行 `config.yaml` 占位符解析。 + 启动时系统会自动执行配置完整性检查,并在日志中输出脱敏后的配置快照。包含以下主要配置项: #### 数据库配置 diff --git a/configuration.py b/configuration.py index 8c8cf0e..114bcd0 100644 --- a/configuration.py +++ b/configuration.py @@ -42,6 +42,11 @@ class Config(object): def __init__(self, config_path: str = None) -> None: self.project_dir = os.path.dirname(os.path.abspath(__file__)) 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.resolved_config = {} self.unresolved_placeholders = [] @@ -54,6 +59,50 @@ class Config(object): yconfig = yaml.safe_load(fp) or {} 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: """解析字符串中的环境变量占位符。"""