From db5c7522f767b730d6fc54a0872c32cc31cf5824 Mon Sep 17 00:00:00 2001 From: ABAPPLO <1134226402@qq.com> Date: Thu, 27 Aug 2026 11:42:02 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=8E=AF=E5=A2=83=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E7=BB=9F=E4=B8=80=E4=B8=BA=20.env.app=20=E5=8D=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6=20+=20=E6=B8=85=E7=90=86=E6=AD=BB=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 .env.app.example 统一模板(两套 compose 共用,数据库按模式分节注释),删除 .env.self-hosted.example / .env.production.example - 部署指南与 README 的 --env-file 引用统一为 .env.app;.gitignore / .dockerignore 白名单同步调整 - runtime-env.ts 移除零消费的 MCP_API_KEY / COLLECTION_MCP_URL / COLLECTION_MCP_KEY 遗留声明 - .dockerignore 补充 .dev.vars 排除;mcp-task-server 测试改读新模板 --- .dockerignore | 1 + .env.app.example | 58 +++++++++++++++++++++++++++++++++ .env.production.example | 47 -------------------------- .env.self-hosted.example | 42 ------------------------ .gitignore | 3 +- README.md | 2 +- docs/KOC LOOP 私有化部署指南.md | 22 ++++++------- lib/runtime-env.ts | 3 -- tests/mcp-task-server.test.mjs | 2 +- 9 files changed, 72 insertions(+), 108 deletions(-) create mode 100644 .env.app.example delete mode 100644 .env.production.example delete mode 100644 .env.self-hosted.example diff --git a/.dockerignore b/.dockerignore index 003601a..a4696b3 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,4 +9,5 @@ koc-portal/node_modules .data .env* !.env.self-hosted.example +.dev.vars npm-debug.log* diff --git a/.env.app.example b/.env.app.example new file mode 100644 index 0000000..74f197c --- /dev/null +++ b/.env.app.example @@ -0,0 +1,58 @@ +# 复制为 .env.app 后填写,两套 compose 编排共用这一份文件(--env-file .env.app)。 +# 不要把真实密钥提交到 Git。 + +# ── 数据库(按部署模式二选一)────────────────────────────── +# 模式 A:公共 MySQL(docker-compose.production.yml,仅 app + nginx) +# 要求 MySQL 8 + utf8mb4,账号需对目标库有建表与读写权限。 +MYSQL_HOST=replace-with-shared-mysql-host +MYSQL_PORT=3306 + +# 模式 B:自带 MySQL 容器(docker-compose.self-hosted.yml,全套) +# 只需填 root 密码;MYSQL_HOST 由 compose 固定为容器内的 mysql,这里无需填写。 +MYSQL_ROOT_PASSWORD=replace-with-strong-root-password + +# 两种模式共用的库与账号。 +MYSQL_DATABASE=koc_loop +MYSQL_USER=koc +MYSQL_PASSWORD=replace-with-strong-app-password + +# ── 对外地址 ────────────────────────────────────────────── +APP_ORIGIN=https://koc.example.com +KOC_PORTAL_URL=https://koc.example.com/koc/ + +# ── 后台账号(仅首次启动创建超管时使用)─────────────────── +# 创建成功后建议移除明文密码并重启;后续账号在「用户管理」维护。 +SUPER_ADMIN_USERNAME=admin +SUPER_ADMIN_PASSWORD=replace-with-strong-admin-password +ADMIN_INTERNAL_TOKEN=replace-with-random-internal-token + +# KOC LOOP 自身 MCP 的访问密钥。 +KOC_MCP_API_KEY=replace-with-random-mcp-key + +# ── 飞书内容表读取 ──────────────────────────────────────── +FEISHU_APP_ID= +FEISHU_APP_SECRET= + +# ── 小红书公开数据采集 MCP ──────────────────────────────── +AI_TOOL_CENTER_MCP_URL= +AI_TOOL_CENTER_MCP_KEY= + +# 账号 LLM 自动打标(可选):AI_TOOL_CENTER 的 structured_text_process 工具,留空 = 关闭自动打标。 +AI_TAGGING_TOOL= + +# ── 企业微信通知(临期催办 + 管理员汇总,全部选填)───────── +# 只收群汇总:仅填 WECOM_ROBOT_WEBHOOK。 +# KOC 侧催办/客户群群发:CORP_ID + AGENT_ID + SECRET 三件套同时填, +# 并在后台 partners 编辑里把 wecom_external_user_id 填好。 +WECOM_ROBOT_WEBHOOK= +WECOM_CORP_ID= +WECOM_AGENT_ID= +WECOM_SECRET= +WECOM_NOTIFY_DUE_DAYS=3 +WECOM_NOTIFY_ENABLED=true + +# ── 调度与入口 ──────────────────────────────────────────── +# 每天北京时间 09:00 自动执行采集计划,生产保持 true。 +ENABLE_SCHEDULER=true +SEED_DEMO_DATA=false +HTTP_PORT=80 diff --git a/.env.production.example b/.env.production.example deleted file mode 100644 index 0d13994..0000000 --- a/.env.production.example +++ /dev/null @@ -1,47 +0,0 @@ -# 复制为 .env.production 后填写,配合 docker-compose.production.yml 使用。 -# 与 self-hosted 模式的区别:数据库用公司公共 MySQL,不启动数据库容器,无 MYSQL_ROOT_PASSWORD。 -# 不要把真实密钥提交到 Git。 - -# 公共 MySQL 连接(要求 MySQL 8 + utf8mb4,账号需对目标库有建表/读写权限)。 -MYSQL_HOST=replace-with-shared-mysql-host -MYSQL_PORT=3306 -MYSQL_DATABASE=koc_loop -MYSQL_USER=koc -MYSQL_PASSWORD=replace-with-strong-app-password - -# 对外地址。部署域名确定后替换。 -APP_ORIGIN=https://koc.example.com -KOC_PORTAL_URL=https://koc.example.com/koc/ - -# 后台首次启动会创建唯一超级管理员;账号创建后建议移除明文密码并重启。 -SUPER_ADMIN_USERNAME=admin -SUPER_ADMIN_PASSWORD=replace-with-strong-admin-password -ADMIN_INTERNAL_TOKEN=replace-with-random-internal-token - -# KOC LOOP 自身 MCP 的访问密钥。 -KOC_MCP_API_KEY=replace-with-random-mcp-key - -# 飞书内容表读取。 -FEISHU_APP_ID= -FEISHU_APP_SECRET= - -# 小红书公开数据采集 MCP。 -AI_TOOL_CENTER_MCP_URL= -AI_TOOL_CENTER_MCP_KEY= - -# 账号 LLM 自动打标(可选):AI_TOOL_CENTER 的 structured_text_process 工具,留空 = 关闭自动打标。 -AI_TAGGING_TOOL= - -# 企业微信通知(临期催办 + 管理员汇总)。群机器人只需 webhook;KOC 侧催办还需 corp/agent/secret, -# 并在后台 partners 编辑里把 wecom_external_user_id 填好。 -WECOM_ROBOT_WEBHOOK= -WECOM_CORP_ID= -WECOM_AGENT_ID= -WECOM_SECRET= -WECOM_NOTIFY_DUE_DAYS=3 -WECOM_NOTIFY_ENABLED=true - -# 每天北京时间 09:00 自动执行采集计划。 -ENABLE_SCHEDULER=true -SEED_DEMO_DATA=false -HTTP_PORT=80 diff --git a/.env.self-hosted.example b/.env.self-hosted.example deleted file mode 100644 index ab2f213..0000000 --- a/.env.self-hosted.example +++ /dev/null @@ -1,42 +0,0 @@ -# 复制为 .env.self-hosted 后填写。不要把真实密钥提交到 Git。 -MYSQL_ROOT_PASSWORD=replace-with-strong-root-password -MYSQL_DATABASE=koc_loop -MYSQL_USER=koc -MYSQL_PASSWORD=replace-with-strong-app-password - -# 对外地址。部署域名确定后替换。 -APP_ORIGIN=https://koc.example.com -KOC_PORTAL_URL=https://koc.example.com/koc/ - -# 后台首次启动会创建唯一超级管理员;账号创建后建议移除明文密码并重启。 -SUPER_ADMIN_USERNAME=admin -SUPER_ADMIN_PASSWORD=replace-with-strong-admin-password -ADMIN_INTERNAL_TOKEN=replace-with-random-internal-token - -# KOC LOOP 自身 MCP 的访问密钥。 -KOC_MCP_API_KEY=replace-with-random-mcp-key - -# 飞书内容表读取。 -FEISHU_APP_ID= -FEISHU_APP_SECRET= - -# 小红书公开数据采集 MCP。 -AI_TOOL_CENTER_MCP_URL= -AI_TOOL_CENTER_MCP_KEY= - -# 账号 LLM 自动打标(可选):AI_TOOL_CENTER 的 structured_text_process 工具,留空 = 关闭自动打标。 -AI_TAGGING_TOOL= - -# 企业微信通知(临期催办 + 管理员汇总)。群机器人只需 webhook;KOC 侧催办还需 corp/agent/secret, -# 并在后台 partners 编辑里把 wecom_external_user_id 填好。 -WECOM_ROBOT_WEBHOOK= -WECOM_CORP_ID= -WECOM_AGENT_ID= -WECOM_SECRET= -WECOM_NOTIFY_DUE_DAYS=3 -WECOM_NOTIFY_ENABLED=true - -# 每天北京时间 09:00 自动执行采集计划。 -ENABLE_SCHEDULER=true -SEED_DEMO_DATA=false -HTTP_PORT=80 diff --git a/.gitignore b/.gitignore index c27a67b..58fe3c8 100644 --- a/.gitignore +++ b/.gitignore @@ -33,8 +33,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* -!.env.self-hosted.example -!.env.production.example +!.env.app.example .dev.vars # 真实 KOC 账号资源数据(含真实主页链接与昵称),仅本地使用,禁止入库 diff --git a/README.md b/README.md index ea61c3a..2867c2b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ npm run dev npm run build ``` -复制 `.env.self-hosted.example` 为 `.env.self-hosted`,设置 `DATABASE_URL` 或 `MYSQL_*` 连接信息。飞书动态导入需要: +复制 `.env.app.example` 为 `.env.app`,设置 `DATABASE_URL` 或 `MYSQL_*` 连接信息。飞书动态导入需要: - `FEISHU_APP_ID` - `FEISHU_APP_SECRET` diff --git a/docs/KOC LOOP 私有化部署指南.md b/docs/KOC LOOP 私有化部署指南.md index 02dc5d0..818cc64 100644 --- a/docs/KOC LOOP 私有化部署指南.md +++ b/docs/KOC LOOP 私有化部署指南.md @@ -19,10 +19,10 @@ 第一版按单实例 KOC 服务设计。MySQL 和上传目录均持久化。以后需要横向扩容时,可以把上传卷换成共享 NAS;对象存储接口已与业务代码分离。 -仓库提供两套编排文件,按数据库归属选用: +仓库提供两套编排文件,按数据库归属选用,共用同一份环境文件 `.env.app`(模板 `.env.app.example`): - `docker-compose.self-hosted.yml`:自带 MySQL 容器与数据卷,适合独立服务器或没有公共数据库的环境; -- `docker-compose.production.yml`:只包含 KOC 服务与入口 Nginx,直连公司公共 MySQL(要求 MySQL 8 + utf8mb4,账号需对目标库有建表与读写权限),配套环境模板 `.env.production.example`。 +- `docker-compose.production.yml`:只包含 KOC 服务与入口 Nginx,直连公司公共 MySQL(要求 MySQL 8 + utf8mb4,账号需对目标库有建表与读写权限)。 两套文件的入口 Nginx 都建议保留:它除了反向代理,还托管 `/koc/` 领取页静态文件、`/api/mcp` 长连接规则与 85 MB 上传限制,公司网关只需把域名转发到容器的 `HTTP_PORT`。 @@ -43,10 +43,10 @@ git clone ssh://git@gta.gbotai.cn:42001/wufengping/koc-loop.git cd koc-loop git checkout main -cp .env.self-hosted.example .env.self-hosted +cp .env.app.example .env.app ``` -编辑 `.env.self-hosted`。必须替换所有 `replace-with-*` 占位值: +编辑 `.env.app`。必须替换所有 `replace-with-*` 占位值(公共 MySQL 模式还需把 `MYSQL_HOST` 指向公司实例): | 变量 | 用途 | | --- | --- | @@ -75,23 +75,21 @@ cp .env.self-hosted.example .env.self-hosted ## 4. 首次启动 ```bash -docker compose --env-file .env.self-hosted \ +docker compose --env-file .env.app \ -f docker-compose.self-hosted.yml up -d --build ``` 生产环境使用公司公共 MySQL 时改用: ```bash -cp .env.production.example .env.production -# 编辑 .env.production:MYSQL_HOST 指向公共实例,其余变量同 self-hosted -docker compose --env-file .env.production \ +docker compose --env-file .env.app \ -f docker-compose.production.yml up -d --build ``` -应用容器启动时会先执行同一套数据库迁移脚本,成功后才启动 KOC 服务。查看状态(生产模式把 `-f` 与 `--env-file` 换成对应文件即可): +应用容器启动时会先执行同一套数据库迁移脚本,成功后才启动 KOC 服务。查看状态(生产模式把 `-f` 换成对应编排文件即可): ```bash -docker compose --env-file .env.self-hosted \ +docker compose --env-file .env.app \ -f docker-compose.self-hosted.yml ps curl -fsS http://127.0.0.1:${HTTP_PORT:-80}/api/health @@ -205,7 +203,7 @@ UPLOAD_DIR=/data/koc/uploads \ ```bash git pull -docker compose --env-file .env.self-hosted \ +docker compose --env-file .env.app \ -f docker-compose.self-hosted.yml up -d --build ``` @@ -267,7 +265,7 @@ KOC LOOP 的企业微信通知是**单向外发**:每天 09:00 与定时采集 - 「测试群机器人」:群内收到 `[KOC LOOP 测试] 群机器人连通性正常,时间 ...` 即配置成功; - 合作方行的「测试发送」:对应外部联系人收到同样格式的测试消息即绑定正确。 -如果面板显示「未配置」但已填环境变量,先确认容器加载了新的 `.env.self-hosted`(重启服务),再回到该页面点「刷新状态」。 +如果面板显示「未配置」但已填环境变量,先确认容器加载了新的 `.env.app`(重启服务),再回到该页面点「刷新状态」。 ### 11.5 任务发布到企微客户群(企业群发) diff --git a/lib/runtime-env.ts b/lib/runtime-env.ts index 13327d4..5fddc62 100644 --- a/lib/runtime-env.ts +++ b/lib/runtime-env.ts @@ -13,14 +13,11 @@ export type RuntimeEnv = { ADMIN_INTERNAL_TOKEN?: string; KOC_MCP_API_KEY?: string; KOC_LOOP_MCP_API_KEY?: string; - MCP_API_KEY?: string; FEISHU_APP_ID?: string; FEISHU_APP_SECRET?: string; AI_TOOL_CENTER_MCP_URL?: string; AI_TOOL_CENTER_MCP_KEY?: string; AI_TAGGING_TOOL?: string; - COLLECTION_MCP_URL?: string; - COLLECTION_MCP_KEY?: string; WECOM_CORP_ID?: string; WECOM_AGENT_ID?: string; WECOM_SECRET?: string; diff --git a/tests/mcp-task-server.test.mjs b/tests/mcp-task-server.test.mjs index 84eeff4..7f28b4a 100644 --- a/tests/mcp-task-server.test.mjs +++ b/tests/mcp-task-server.test.mjs @@ -13,7 +13,7 @@ test("exposes authenticated KOC task, recovery, collection, and resource MCP too readFile(new URL("../lib/task-service.ts", import.meta.url), "utf8"), readFile(new URL("../app/api/action/route.ts", import.meta.url), "utf8"), readFile(new URL("../README.md", import.meta.url), "utf8"), - readFile(new URL("../.env.self-hosted.example", import.meta.url), "utf8"), + readFile(new URL("../.env.app.example", import.meta.url), "utf8"), readFile(new URL("../package.json", import.meta.url), "utf8"), ]);