Skip to content

Commit 1338d17

Browse files
authored
feat: add nginx config generator (#170)
* refactor: modularize workspace setup flow * update docs * feat: add nginx config generator * fix: reuse repo credentials in workspace setup * fix: streamline workspace token prompt * fix: tighten cli help and prompts * fix: align remaining cli help and prompts * fix: tighten repo-scoped github credentials * fix: finalize setup alias defaults * fix: keep setup alias dry-run wording stable
1 parent d53e670 commit 1338d17

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+2553
-1398
lines changed

.gitignore

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
##### user defined ######
22
*.jsonl
3-
playground/
4-
Playground/
5-
workspace/
3+
/playground/
4+
/Playground/
5+
/workspace/
66

77

88
##### seperated line ######

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ src/chattool/
1515
├── client/ # 统一 CLI 主入口(chattool)
1616
├── llm/ # LLM 路由、Chat 对象
1717
├── config/ # 环境变量管理(chatenv)
18-
├── tools/ # 工具箱(dns、lark、image、network 等)
18+
├── tools/ # 工具箱(dns、lark、image、network、nginx 等)
1919
├── mcp/ # MCP 入口与编排(工具实现下放到 tools/*/mcp.py)
2020
├── setup/ # 环境安装脚本
2121
├── serve/ # 服务端(截图、证书分发等)
@@ -37,7 +37,7 @@ mkdocs serve --no-livereload # 本地预览文档
3737
- 必要参数缺失时自动触发 interactive 模式
3838
- `-i` 强制开启交互,`-I` 强制关闭(参数不全则报错)
3939
- 参数默认值从环境变量读取,敏感值在提示中自动 mask
40-
- 所有 CLI 交互统一走 `utils/tui.py`
40+
- 所有 CLI 交互统一走 `src/chattool/interaction/`
4141
- 进入 interactive 后,补全当前任务相关的关键参数
4242
- interactive 展示的默认值必须与实际执行一致
4343
- `-i` 进入当前命令交互流程,`-I` 完全禁用交互
@@ -49,7 +49,7 @@ mkdocs serve --no-livereload # 本地预览文档
4949
- setup 命令记录关键阶段日志(开始、依赖检测、安装执行、配置写入、失败原因)
5050
- 出错时同时保留用户可读错误输出与 logger 错误记录
5151
- 若 setup 流程涉及 `sudo` 命令,必须提供显式 `--sudo` 开关;未指定 `--sudo` 时,无论非交互还是交互未确认,都只打印建议命令,由用户自行处理
52-
- setup 类命令若同时支持显式参数、`-e/--env`、已有工具配置、环境变量与 typed `.env`,统一优先级必须为:`显式参数 > -e/--env > 工具默认配置位置 > 系统环境变量 > typed .env`
52+
- setup 类命令若同时支持显式参数、`-e/--env`、已有工具配置、环境变量与 ChatTool `.env`,统一优先级必须为:`显式参数 > -e/--env > 工具默认配置位置 > 系统环境变量 > ChatTool .env > 默认值`
5353

5454
### 工具结构
5555

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
88

99
### Fixed
1010
- `chattool setup` 中通过 npm 执行安装或检查时,现会先打印真实的 `npm ...` 命令,方便用户确认当前实际在执行什么
11+
- `chattool pypi init` 在交互终端里省略包名时,现会自动进入交互式补参;显式 `-I` 关闭交互时才保持缺参报错
12+
- `chattool setup workspace` 在交互式启用 ChatTool / RexBlog 模块时,现会分别提示是否配置 GitHub token;默认值优先读取当前仓库 git credential,并以 mask 形式展示,便于直接复用到目标仓库
1113
- `chatenv init` / `chatenv new` 更新 active typed env 后,`chatenv cat` 与运行时配置加载现在会优先读取 `envs/<Config>/.env`,不再被已有 shell 环境变量意外覆盖,避免交互里刚保存的新值看起来“没有生效”
1214
- `chatenv new` 现在收紧为纯 profile 创建语义:无论直接传 profile 名还是交互式补参,都只写入 `envs/<Config>/<profile>.env`,不再顺手覆盖当前 active `.env`
1315
- `chatskill install` / `chattool skill install` 现在在交互终端里缺少 skill 名时会进入选择页,而不是直接因缺参数中断;非交互环境仍保持显式报错
@@ -26,6 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
2628
- `chattool docker nas` 新增 NAS 静态文件服务模板,生成 compose 与 env 示例占位模板;镜像、路径、端口与 URL 前缀需由用户显式填写或通过 `--set` 覆盖
2729
- 新增 `chattool setup docker`,用于检查 Docker / Docker Compose / docker 组状态;涉及 `sudo` 的建议命令默认只打印,显式传入 `--sudo` 后才允许在确认后执行
2830
- `chattool setup playground` 现支持 `--language zh|en`;默认生成中文的 `AGENTS.md``CHATTOOL.md``MEMORY.md` 和相关 README,也可显式切到英文模板
31+
- 新增 `chattool nginx`,用于按模板生成常见的 Nginx 配置片段;当前收口为配置导向的最小模板集:基础 `proxy_pass`、HTTPS 入口反代、WebSocket 转发、静态目录站点和重定向,并支持 `-i` 交互式补齐必要参数
2932

3033
### Changed
3134
- `chattool setup workspace` / `chattool setup playground` 的任务集汇报结构现统一为 `reports/MM-DD-<set-name>/`:集合目录下直接维护 `TASKSET.md``progress.md` 与各子任务目录,不再额外套一层 `task-sets/``tasks/`

DEVELOP.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- 统一入口:`src/chattool/client/`
66
- 核心能力:`src/chattool/tools/`
7+
- 模板型工具优先复用现有生成器风格,例如 `chattool docker``chattool nginx`
78
- 协议入口:`src/chattool/mcp/`(实现下放 `tools/*/mcp.py`
89
- 环境安装:`src/chattool/setup/`
910
- 远程服务:`src/chattool/serve/`
@@ -13,9 +14,13 @@
1314

1415
- 新能力先落 `tools/<name>/`,再接入 `client/mcp/serve`
1516
- CLI 统一 `-i/-I` 与缺参自动交互规则。
16-
- 所有 CLI 交互统一走 `utils/tui.py`
17+
- 所有 CLI 交互统一走 `src/chattool/interaction/`
1718
- 进入 interactive 后,补全当前任务相关的关键参数。
1819
- prompt 默认值必须与实际执行一致,敏感值默认脱敏。
20+
- 任何新增或修改的 CLI 都必须统一满足这些规则,不能只让当前命令局部符合。
21+
- 若交互先做分类/模板选择,再逐项补参,文档、测试和实现顺序必须一致。
22+
- CLI 顶层 import 保持最小化,执行期才需要的实现优先下沉到命令函数或局部 helper。
23+
- 一级命令 help 也要校对:语言风格统一、描述不超出实际能力、group 子命令要带 short help。
1924
- setup 命令必须记录关键阶段日志。
2025
- 入口层只做编排,不承载业务实现。
2126

@@ -25,11 +30,12 @@
2530
- 环境变量:更新 `docs/configuration.md`
2631
- 开发规范:更新 `docs/development-guide/`
2732
- 导航变更:同步更新 `mkdocs.yml`
33+
- 新工具落地后,同步更新 `README.md` / `README_en.md` / `AGENTS.md`
2834

2935
## 配置机制
3036

31-
- 默认优先级统一为:`显式参数 > environment variable > envs/<Config>/.env > default`
32-
- 对支持 `-e/--env` 的命令,统一为:`显式参数 > -e/显式 env > environment variable > envs/<Config>/.env > default`
37+
- 默认优先级统一为:`显式参数 > 配置(若存在) > environment variable > ChatTool .env > default`
38+
- 对支持 `-e/--env` 的命令,统一为:`显式参数 > -e/显式 env > 配置(若存在) > environment variable > ChatTool .env > default`
3339
- profile 固定保存在 `envs/<Config>/<profile>.env`
3440
- 新命令需要临时切换配置时,优先复用 `-e/--env`,不要再新增一套临时环境变量语义
3541

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,16 @@ chattool dns ddns home.example.com --monitor
109109
chattool dns cert-update -d example.com -e admin@example.com
110110
```
111111

112+
### Nginx 配置生成 (`chattool nginx`)
113+
114+
```bash
115+
chattool nginx --list
116+
chattool nginx proxy-pass --set SERVER_NAME=app.example.com --set PROXY_PASS=http://127.0.0.1:8080
117+
chattool nginx websocket-proxy ./websocket.conf --set SERVER_NAME=ws.example.com --set PROXY_PASS=http://127.0.0.1:3000
118+
chattool nginx static-root ./nas.conf --set SERVER_NAME=share.example.com --set ROOT_DIR=/storage/nas
119+
chattool nginx -i # 交互式选择模板并逐项填写
120+
```
121+
112122
### AI 绘图 (`chattool image`)
113123

114124
```bash
@@ -130,6 +140,7 @@ chattool explore arxiv get 1706.03762 -v
130140
| 工具 | 命令 | 说明 |
131141
|------|------|------|
132142
| 网络扫描 | `chattool network` | 扫描局域网主机和端口 |
143+
| Nginx 配置 | `chattool nginx` | 按模板生成常见的反向代理、路径转发和静态目录配置 |
133144
| PyPI 工具 | `chattool pypi` | 创建、构建、校验、上传与探测 Python 包 |
134145
| MCP 服务 | `chattool mcp start` | 标准 MCP Server,供 Claude/Cursor 调用 |
135146
| 环境安装 | `chattool setup codex/claude/opencode/lark-cli/docker` | 安装或检查常用 CLI / Docker 环境,并在确认后执行建议的系统命令 |

README_en.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,21 @@ chattool image siliconflow list-models
157157
chattool image siliconflow generate "a cute dog" -o dog.png
158158
```
159159

160-
### 6. Other Tools
160+
### 6. Nginx Config Generation (`chattool nginx`)
161+
162+
```bash
163+
chattool nginx --list
164+
chattool nginx proxy-pass --set SERVER_NAME=app.example.com --set PROXY_PASS=http://127.0.0.1:8080
165+
chattool nginx websocket-proxy ./websocket.conf --set SERVER_NAME=ws.example.com --set PROXY_PASS=http://127.0.0.1:3000
166+
chattool nginx -i
167+
```
168+
169+
### 7. Other Tools
161170

162171
| Tool | Command | Description |
163172
|------|---------|-------------|
164173
| Network Scan | `chattool network scan` | Scan LAN for active hosts and SSH ports |
174+
| Nginx Config | `chattool nginx` | Generate template-based reverse proxy and static site configs |
165175
| MCP Server | `chattool mcp info` / `chattool mcp inspect` | Inspect MCP server capabilities (JSON supported) |
166176
| Screenshot | `chattool serve capture` | Local webpage screenshot service |
167177
| Cert Mgmt | `chattool serve cert` / `chattool client cert` | SSL certificate distribution |

cli-tests/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ cli-tests/
3131
│ └── test_chattool_mcp_basic.md
3232
├── network/
3333
│ └── test_chattool_network_basic.md
34+
├── nginx/
35+
│ └── test_chattool_nginx_render_basic.md
3436
├── pypi/
3537
│ ├── test_chattool_pypi_basic.md
3638
│ └── test_chattool_pypi_probe.md
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# test_chattool_nginx_render_basic
2+
3+
测试 `chattool nginx` 的真实基础链路,覆盖通用反代、WebSocket 转发、静态目录模板与文件输出。
4+
5+
## 元信息
6+
7+
- 命令:`chattool nginx [template] [output_file] [--set KEY=VALUE]`
8+
- 目的:验证 Nginx 配置模板可直接从真实 CLI 产出。
9+
- 标签:`cli`
10+
- 前置条件:无第三方服务依赖。
11+
- 环境准备:准备临时输出目录。
12+
- 回滚:删除生成的配置文件。
13+
14+
## 用例 1:stdout 输出通用 HTTP 反代模板
15+
16+
- 初始环境准备:
17+
-
18+
- 相关文件:
19+
-
20+
21+
预期过程和结果:
22+
1. 执行 `chattool nginx proxy-pass --set SERVER_NAME=app.example.com --set PROXY_PASS=http://127.0.0.1:8080`,预期 stdout 包含 `server_name app.example.com;``proxy_pass http://127.0.0.1:8080;`
23+
24+
参考执行脚本(伪代码):
25+
26+
```sh
27+
chattool nginx proxy-pass --set SERVER_NAME=app.example.com --set PROXY_PASS=http://127.0.0.1:8080
28+
```
29+
30+
## 用例 2:写入 WebSocket 转发模板文件
31+
32+
- 初始环境准备:
33+
- 准备临时目录。
34+
- 相关文件:
35+
- `<tmp>/websocket.conf`
36+
37+
预期过程和结果:
38+
1. 执行 `chattool nginx websocket-proxy <tmp>/websocket.conf --set SERVER_NAME=ws.example.com --set PROXY_PASS=http://127.0.0.1:3000 --force`,预期输出文件被创建。
39+
2. 预期文件中包含 `proxy_http_version 1.1;``proxy_set_header Upgrade $http_upgrade;``proxy_set_header Connection "upgrade";`
40+
41+
参考执行脚本(伪代码):
42+
43+
```sh
44+
chattool nginx websocket-proxy /tmp/websocket.conf --set SERVER_NAME=ws.example.com --set PROXY_PASS=http://127.0.0.1:3000 --force
45+
```
46+
47+
## 用例 3:写入静态目录模板文件
48+
49+
- 初始环境准备:
50+
- 准备临时目录。
51+
- 相关文件:
52+
- `<tmp>/nas.conf`
53+
54+
预期过程和结果:
55+
1. 执行 `chattool nginx static-root <tmp>/nas.conf --set SERVER_NAME=share.example.com --set ROOT_DIR=/storage/nas --force`,预期输出文件被创建。
56+
2. 预期文件中包含 `root /storage/nas;``autoindex on;``charset utf-8;`
57+
58+
参考执行脚本(伪代码):
59+
60+
```sh
61+
chattool nginx static-root /tmp/nas.conf --set SERVER_NAME=share.example.com --set ROOT_DIR=/storage/nas --force
62+
```
63+
64+
## 用例 4:生成 HTTP 到 HTTPS 跳转模板
65+
66+
- 初始环境准备:
67+
-
68+
- 相关文件:
69+
-
70+
71+
预期过程和结果:
72+
1. 执行 `chattool nginx redirect --set SERVER_NAME="example.com www.example.com"`,预期 stdout 中包含 `return 301 https://$host$request_uri;`
73+
74+
参考执行脚本(伪代码):
75+
76+
```sh
77+
chattool nginx redirect --set SERVER_NAME="example.com www.example.com"
78+
```
79+
80+
## 清理 / 回滚
81+
82+
- 删除生成的配置文件。
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from pathlib import Path
2+
3+
import pytest
4+
from click.testing import CliRunner
5+
6+
from chattool.client.main import cli
7+
8+
9+
pytestmark = [pytest.mark.e2e]
10+
11+
12+
def test_chattool_nginx_basic(tmp_path: Path):
13+
runner = CliRunner()
14+
15+
result = runner.invoke(
16+
cli,
17+
[
18+
"nginx",
19+
"proxy-pass",
20+
"--set",
21+
"SERVER_NAME=app.example.com",
22+
"--set",
23+
"PROXY_PASS=http://127.0.0.1:8080",
24+
],
25+
)
26+
assert result.exit_code == 0
27+
assert "server_name app.example.com;" in result.output
28+
assert "proxy_pass http://127.0.0.1:8080;" in result.output
29+
30+
ws_file = tmp_path / "websocket.conf"
31+
result = runner.invoke(
32+
cli,
33+
[
34+
"nginx",
35+
"websocket-proxy",
36+
str(ws_file),
37+
"--set",
38+
"SERVER_NAME=ws.example.com",
39+
"--set",
40+
"PROXY_PASS=http://127.0.0.1:3000",
41+
"--force",
42+
],
43+
)
44+
assert result.exit_code == 0
45+
ws_content = ws_file.read_text(encoding="utf-8")
46+
assert 'proxy_set_header Connection "upgrade";' in ws_content
47+
assert "proxy_http_version 1.1;" in ws_content
48+
assert "proxy_set_header Upgrade $http_upgrade;" in ws_content
49+
50+
nas_file = tmp_path / "nas.conf"
51+
result = runner.invoke(
52+
cli,
53+
[
54+
"nginx",
55+
"static-root",
56+
str(nas_file),
57+
"--set",
58+
"SERVER_NAME=share.example.com",
59+
"--set",
60+
"ROOT_DIR=/storage/nas",
61+
"--force",
62+
],
63+
)
64+
assert result.exit_code == 0
65+
nas_content = nas_file.read_text(encoding="utf-8")
66+
assert "root /storage/nas;" in nas_content
67+
assert "autoindex on;" in nas_content
68+
assert "charset utf-8;" in nas_content
69+
70+
result = runner.invoke(
71+
cli,
72+
[
73+
"nginx",
74+
"redirect",
75+
"--set",
76+
"SERVER_NAME=example.com www.example.com",
77+
],
78+
)
79+
assert result.exit_code == 0
80+
assert "return 301 https://$host$request_uri;" in result.output

cli-tests/pypi/test_chattool_pypi_basic.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
预期过程和结果:
2222
1. 执行 `chattool pypi init mychat --project-dir <tmp>/mychat`,预期生成 `pyproject.toml``README.md``LICENSE``src/mychat/__init__.py``tests/conftest.py``tests/test_version.py`
2323
2. 生成的 `pyproject.toml` 默认写入 `requires-python = ">=3.9"`
24-
3. 当缺少包名时`chattool pypi init` 应直接报错提示需要传入包名或 `--project-dir`,不进入交互式补参
24+
3. 在交互终端里缺少包名时`chattool pypi init` 应自动进入交互式补参;显式 `-I` 关闭交互时才报错
2525

2626
参考执行脚本(伪代码):
2727

0 commit comments

Comments
 (0)