|
| 1 | +# RedC 插件开发指南 |
| 2 | + |
| 3 | +本文档介绍如何编写一个 RedC 插件。插件可以在场景生命周期的关键节点(部署前/后、销毁前/后)执行自定义脚本,实现配置生成、文件上传、DNS 更新、Webhook 通知等扩展功能。 |
| 4 | + |
| 5 | +> 完整示例请参考 [redc-plugin-example](../plugins/redc-plugin-example/),生产级参考请看 [redc-plugin-clash-config](../plugins/redc-plugin-clash-config/)、[redc-plugin-upload-r2](../plugins/redc-plugin-upload-r2/)、[redc-plugin-cloudflare-dns](../plugins/redc-plugin-cloudflare-dns/)。 |
| 6 | +
|
| 7 | +## 目录结构 |
| 8 | + |
| 9 | +``` |
| 10 | +redc-plugin-your-name/ |
| 11 | +├── plugin.json # 必须 — 插件清单文件 |
| 12 | +├── README.md # 推荐 — 使用说明 |
| 13 | +├── hooks/ # 可选 — 生命周期钩子脚本 |
| 14 | +│ ├── pre-plan.sh |
| 15 | +│ ├── post-plan.sh |
| 16 | +│ ├── pre-apply.sh |
| 17 | +│ ├── post-apply.sh # 最常用:部署完成后执行 |
| 18 | +│ ├── pre-destroy.sh |
| 19 | +│ └── post-destroy.sh |
| 20 | +└── templates/ # 可选 — 附带的 Terraform 模板 |
| 21 | +``` |
| 22 | + |
| 23 | +插件安装后存放在 `~/.redc/plugins/<plugin-name>/` 目录下。 |
| 24 | + |
| 25 | +## plugin.json 完整字段说明 |
| 26 | + |
| 27 | +`plugin.json` 是插件的唯一必须文件,定义了插件的元信息、能力声明和配置参数。 |
| 28 | + |
| 29 | +```jsonc |
| 30 | +{ |
| 31 | + // ── 基础信息 ── |
| 32 | + "name": "redc-plugin-your-name", // 必填,插件名称,建议 redc-plugin- 前缀 |
| 33 | + "version": "1.0.0", // 必填,语义化版本号 |
| 34 | + "description": "插件功能的中文描述", // 必填,中文描述 |
| 35 | + "description_en": "English description", // 可选,英文描述 |
| 36 | + "author": "your-name", // 可选,作者 |
| 37 | + "homepage": "https://github.com/...", // 可选,项目主页 |
| 38 | + "category": "proxy", // 可选,分类(proxy/dns/storage/monitoring/example 等) |
| 39 | + "tags": ["clash", "proxy"], // 可选,标签数组,用于搜索和分类 |
| 40 | + "min_redc_version": "3.1.0", // 可选,要求的最低 RedC 版本 |
| 41 | + |
| 42 | + // ── 能力声明 ── |
| 43 | + "capabilities": { |
| 44 | + "hooks": { // 生命周期钩子,key 为钩子点,value 为脚本相对路径 |
| 45 | + "post-apply": "hooks/post-apply.sh", |
| 46 | + "pre-destroy": "hooks/pre-destroy.sh" |
| 47 | + }, |
| 48 | + "templates": ["templates/*.tf"], // 可选,附带的 Terraform 模板(glob 模式) |
| 49 | + "userdata": ["userdata/*.sh"] // 可选,附带的 Userdata 脚本(glob 模式) |
| 50 | + }, |
| 51 | + |
| 52 | + // ── 配置参数(可选)── |
| 53 | + // 声明后 GUI 会自动渲染配置表单,用户可在「插件管理」页面填写 |
| 54 | + "config_schema": { |
| 55 | + "notify_url": { |
| 56 | + "type": "string", // 类型:string / number / boolean |
| 57 | + "required": false, // 是否必填 |
| 58 | + "description": "Webhook 通知地址", // 参数说明 |
| 59 | + "default": "" // 默认值 |
| 60 | + }, |
| 61 | + "enable_log": { |
| 62 | + "type": "boolean", |
| 63 | + "required": false, |
| 64 | + "description": "是否记录详细日志", |
| 65 | + "default": "true" |
| 66 | + } |
| 67 | + } |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +## 生命周期钩子 |
| 72 | + |
| 73 | +### 6 个钩子点 |
| 74 | + |
| 75 | +| 钩子点 | 触发时机 | 典型用途 | |
| 76 | +|--------|---------|---------| |
| 77 | +| `pre-plan` | `terraform plan` 之前 | 预检查、参数验证 | |
| 78 | +| `post-plan` | `terraform plan` 之后 | 审查计划输出 | |
| 79 | +| `pre-apply` | `terraform apply` 之前 | 准备外部资源 | |
| 80 | +| `post-apply` | `terraform apply` 之后 | 生成配置、上传文件、更新 DNS、发通知 | |
| 81 | +| `pre-destroy` | `terraform destroy` 之前 | 清理外部资源(DNS 记录、文件等) | |
| 82 | +| `post-destroy` | `terraform destroy` 之后 | 发送销毁通知 | |
| 83 | + |
| 84 | +### 执行规则 |
| 85 | + |
| 86 | +- 每个钩子脚本最长执行 **5 分钟**,超时自动终止 |
| 87 | +- 钩子失败**不会阻断**后续流程,错误会记录到日志并继续执行下一个插件的钩子 |
| 88 | +- 同一场景绑定多个插件时,按 `case.json` 中 `redc_plugins` 的声明顺序执行 |
| 89 | +- 脚本通过 `bash` 执行,工作目录为插件目录 |
| 90 | + |
| 91 | +### 环境变量 |
| 92 | + |
| 93 | +钩子脚本可通过以下环境变量获取场景信息和插件配置: |
| 94 | + |
| 95 | +#### 场景信息 |
| 96 | + |
| 97 | +| 变量 | 说明 | 示例 | |
| 98 | +|------|------|------| |
| 99 | +| `REDC_HOOK_POINT` | 当前钩子点 | `post-apply` | |
| 100 | +| `REDC_PLUGIN_NAME` | 插件名称 | `redc-plugin-clash-config` | |
| 101 | +| `REDC_PLUGIN_DIR` | 插件目录绝对路径 | `/Users/xxx/.redc/plugins/redc-plugin-clash-config` | |
| 102 | +| `REDC_CASE_NAME` | 场景名称 | `proxy-01` | |
| 103 | +| `REDC_CASE_PATH` | 场景目录绝对路径 | `/Users/xxx/.redc/project/xxx/proxy-01` | |
| 104 | +| `REDC_CASE_TEMPLATE` | 场景模板类型 | `aliyun/proxy` | |
| 105 | +| `REDC_CASE_STATE` | 场景状态 | `running` | |
| 106 | +| `REDC_OUTPUT_JSON` | Terraform 输出(JSON 格式) | `{"public_ip":{"value":["1.2.3.4"],...}}` | |
| 107 | +| `REDC_CASE_VARS` | 场景参数(JSON 格式) | `{"region":"cn-beijing","node":"2"}` | |
| 108 | + |
| 109 | +#### 插件配置 |
| 110 | + |
| 111 | +| 变量 | 说明 | |
| 112 | +|------|------| |
| 113 | +| `REDC_PLUGIN_CONFIG` | 完整配置(JSON 格式),如 `{"notify_url":"https://...","enable_log":true}` | |
| 114 | +| `REDC_PLUGIN_CONFIG_<KEY>` | 单个配置值,key 转为大写并将 `-` 替换为 `_` | |
| 115 | + |
| 116 | +**配置 key 转换规则:** `notify_url` → `REDC_PLUGIN_CONFIG_NOTIFY_URL`,`enable-log` → `REDC_PLUGIN_CONFIG_ENABLE_LOG` |
| 117 | + |
| 118 | +### REDC_OUTPUT 协议 |
| 119 | + |
| 120 | +钩子脚本可以通过 stdout 输出特殊格式的行,将数据回传给 RedC: |
| 121 | + |
| 122 | +```bash |
| 123 | +echo "REDC_OUTPUT:clash_config_url=https://example.com/config.yaml" |
| 124 | +echo "REDC_OUTPUT:node_count=3" |
| 125 | +``` |
| 126 | + |
| 127 | +格式为 `REDC_OUTPUT:key=value`,每行一个键值对。这些输出会: |
| 128 | +1. 持久化到场景目录下的 `plugin_outputs.json` |
| 129 | +2. 合并到 GUI 的场景输出面板中展示 |
| 130 | +3. 可被后续插件和 AI Agent 读取 |
| 131 | + |
| 132 | +非 `REDC_OUTPUT:` 前缀的 stdout 内容会作为普通日志输出。 |
| 133 | + |
| 134 | +## 编写钩子脚本示例 |
| 135 | + |
| 136 | +一个典型的 `post-apply` 钩子: |
| 137 | + |
| 138 | +```bash |
| 139 | +#!/bin/bash |
| 140 | +set -euo pipefail |
| 141 | + |
| 142 | +echo "[my-plugin] post-apply hook triggered for ${REDC_CASE_NAME}" |
| 143 | + |
| 144 | +# 1. 从 Terraform 输出获取 IP |
| 145 | +IPS=$(echo "$REDC_OUTPUT_JSON" | jq -r '.public_ip.value[]' 2>/dev/null) |
| 146 | +if [ -z "$IPS" ]; then |
| 147 | + echo "[my-plugin] no IPs found in outputs, skipping" |
| 148 | + exit 0 |
| 149 | +fi |
| 150 | + |
| 151 | +# 2. 读取插件配置 |
| 152 | +PORT="${REDC_PLUGIN_CONFIG_PORT:-8388}" |
| 153 | + |
| 154 | +# 3. 执行业务逻辑(示例:生成配置文件) |
| 155 | +CONFIG_FILE="${REDC_CASE_PATH}/my-config.yaml" |
| 156 | +echo "servers:" > "$CONFIG_FILE" |
| 157 | +for ip in $IPS; do |
| 158 | + echo " - address: $ip:$PORT" >> "$CONFIG_FILE" |
| 159 | +done |
| 160 | + |
| 161 | +# 4. 通过 REDC_OUTPUT 回传结果到 GUI |
| 162 | +echo "REDC_OUTPUT:config_file=${CONFIG_FILE}" |
| 163 | +echo "REDC_OUTPUT:server_count=$(echo "$IPS" | wc -l | tr -d ' ')" |
| 164 | + |
| 165 | +# 5. 可选:Webhook 通知 |
| 166 | +if [ -n "${REDC_PLUGIN_CONFIG_NOTIFY_URL:-}" ]; then |
| 167 | + curl -s -X POST "$REDC_PLUGIN_CONFIG_NOTIFY_URL" \ |
| 168 | + -H "Content-Type: application/json" \ |
| 169 | + -d "{\"event\":\"deployed\",\"case\":\"${REDC_CASE_NAME}\",\"ips\":\"${IPS}\"}" \ |
| 170 | + --max-time 10 || echo "[my-plugin] webhook failed (non-fatal)" |
| 171 | +fi |
| 172 | + |
| 173 | +echo "[my-plugin] done" |
| 174 | +``` |
| 175 | + |
| 176 | +## 场景绑定插件 |
| 177 | + |
| 178 | +在模板的 `case.json` 中通过 `redc_plugins` 字段声明依赖的插件(逗号分隔): |
| 179 | + |
| 180 | +```json |
| 181 | +{ |
| 182 | + "name": "proxy", |
| 183 | + "description": "多节点代理场景", |
| 184 | + "redc_plugins": "redc-plugin-clash-config,redc-plugin-upload-r2", |
| 185 | + "template": "preset" |
| 186 | +} |
| 187 | +``` |
| 188 | + |
| 189 | +场景启动时,RedC 会按声明顺序执行这些插件的钩子。如果用户未安装声明的插件,GUI 会在启动前弹窗提醒。 |
| 190 | + |
| 191 | +## 安装与调试 |
| 192 | + |
| 193 | +### 安装方式 |
| 194 | + |
| 195 | +```bash |
| 196 | +# 从 Git 仓库安装 |
| 197 | +redc plugin install https://github.com/your-org/redc-plugin-your-name |
| 198 | + |
| 199 | +# 从本地路径安装(开发调试用) |
| 200 | +redc plugin install /path/to/redc-plugin-your-name |
| 201 | + |
| 202 | +# 从 ZIP URL 安装 |
| 203 | +redc plugin install https://example.com/redc-plugin-your-name.zip |
| 204 | +``` |
| 205 | + |
| 206 | +GUI 中可在「插件管理」页面输入上述地址安装。 |
| 207 | + |
| 208 | +### 常用命令 |
| 209 | + |
| 210 | +```bash |
| 211 | +redc plugin list # 列出已安装插件 |
| 212 | +redc plugin info <name> # 查看插件详情 |
| 213 | +redc plugin enable <name> # 启用插件 |
| 214 | +redc plugin disable <name> # 禁用插件(在插件目录创建 .disabled 标记文件) |
| 215 | +redc plugin update <name> # 更新插件(Git: pull,ZIP: 重新下载) |
| 216 | +redc plugin uninstall <name> # 卸载插件 |
| 217 | +``` |
| 218 | + |
| 219 | +### 调试技巧 |
| 220 | + |
| 221 | +1. **本地安装开发:** 开发时用 `redc plugin install /local/path` 安装,修改代码后 `redc plugin update <name>` 更新 |
| 222 | +2. **查看日志:** 钩子脚本的 stdout/stderr 会输出到 RedC 日志和 GUI 控制台 |
| 223 | +3. **手动测试钩子:** 设置好环境变量后直接运行脚本 |
| 224 | + ```bash |
| 225 | + REDC_CASE_NAME=test REDC_OUTPUT_JSON='{"public_ip":{"value":["1.2.3.4"]}}' \ |
| 226 | + bash hooks/post-apply.sh |
| 227 | + ``` |
| 228 | +4. **检查输出:** 查看场景目录下的 `plugin_outputs.json` 确认 REDC_OUTPUT 是否正确写入 |
| 229 | + |
| 230 | +## 发布到插件市场 |
| 231 | + |
| 232 | +1. 将插件推送到公开的 Git 仓库 |
| 233 | +2. 向 [redc-template](https://github.com/wgpsec/redc-template) 提交 PR,将插件目录放到 `plugins/` 下 |
| 234 | +3. 合并后 GitHub Actions 会自动更新插件市场索引,用户即可在 GUI 的插件市场中发现和安装 |
| 235 | + |
| 236 | +## 现有插件参考 |
| 237 | + |
| 238 | +| 插件 | 功能 | 钩子 | |
| 239 | +|------|------|------| |
| 240 | +| [redc-plugin-clash-config](../plugins/redc-plugin-clash-config/) | 部署后生成 Clash 代理配置 | post-apply | |
| 241 | +| [redc-plugin-upload-r2](../plugins/redc-plugin-upload-r2/) | 将生成的文件上传到 Cloudflare R2 | post-apply | |
| 242 | +| [redc-plugin-cloudflare-dns](../plugins/redc-plugin-cloudflare-dns/) | 部署后更新 Cloudflare DNS 记录 | post-apply, pre-destroy | |
| 243 | +| [redc-plugin-example](../plugins/redc-plugin-example/) | 示例插件(日志 + Webhook) | post-apply, pre-destroy | |
0 commit comments