Postman 风格的团队 API 协作平台(Monorepo)。
| 模块 | 技术 |
|---|---|
| Monorepo | Nx + pnpm workspaces |
前端 apps/web |
React 19 + Vite + antd 5 + zustand |
后端 apps/api |
Next.js (Route Handlers) + Drizzle ORM(含实时桥:SSE 下行 + POST 上行) |
CLI apps/cli |
Rust 单二进制 rabbitpost(资源 CRUD / 用例执行 / 报告上传 / lint / rt 会话) |
Runner apps/runner |
Rust 常驻进程 rabbitpost-runner(派发任务 + 长连接协议客户端:WS/Socket.IO/MQTT/MCP/gRPC/SSE) |
桌面端 apps/desktop |
Tauri 2 壳(Win/macOS/Linux,WebView 加载远程 Web,前后端零改动) |
共享库 crates/rp-core |
Rust 执行引擎 + QuickJS 脚本沙箱(CLI 与 Runner 同源) |
| 数据库 | PostgreSQL(开发环境用 embedded-postgres,免安装) |
| 认证 | Casdoor (OIDC,私有化部署) + 个人 API Key(CLI) |
RabbitPost/
├── apps/
│ ├── web/ # React 前端(Postman 风格 UI)
│ ├── api/ # Next.js API(团队/Workspace/Collection/Env/History/Runs/请求执行代理/实时桥)
│ ├── cli/ # rabbitpost CLI(Rust,本地/CI 用)
│ ├── runner/ # rabbitpost-runner(Rust,服务器常驻:派发任务 + 长连接协议会话)
│ └── desktop/ # 桌面客户端(Tauri 2 壳,加载远程 Web)
├── crates/
│ └── rp-core/ # CLI 与 Runner 共用的执行引擎 / QuickJS 沙箱 / API 客户端
├── packages/
│ └── shared/ # 前后端共享的领域模型与 API 契约
├── nx.json
├── pnpm-workspace.yaml
└── tsconfig.base.json
- 团队管理:团队 CRUD、成员邀请(按邮箱)、角色(owner/admin/editor/viewer)
- Workspace 管理:团队下多 Workspace,CRUD
- Collection 管理:Collection + 自引用树(folder/request 无限层级),拖拽排序字段预留
- 环境变量:多环境、Key-Value 变量、secret 标记、请求中
{{var}}引用 - HTTP 请求:服务端代理执行(规避 CORS),支持 params/headers/body(raw/form-data/urlencoded/binary/graphql)/auth(bearer/basic/api-key)
- 多协议请求:
- GraphQL:独立协议编辑器(Query/Variables/Headers/Auth),introspection 拉取 Schema 文档 + 编辑器补全(走
/api/v1/execute,GraphQL-over-HTTP) - WebSocket / Socket.IO / MQTT / MCP / gRPC / SSE / GraphQL Subscription:长连接协议经 Runner 上的 Rust 客户端执行,链路为 浏览器 ⇄(SSE+POST)⇄ api 实时桥 ⇄(NDJSON 流)⇄ Runner ⇄ 目标(浏览器不直连目标,无 CORS/混合内容限制;Runner 部署到内网即可达内网目标);消息时间线 + 连接状态实时展示;gRPC 支持 unary 与全部流式调用,服务发现优先 server reflection、可退化为 .proto 文本
- 协议在新建请求(草稿)时选择,保存后不可修改;各协议配置随请求持久化,连接状态与消息记录不持久化
- 长连接协议不走
/api/v1/execute一次性执行模型(该接口会明确拒绝),由/api/v1/rt/sessions会话接口承载
- GraphQL:独立协议编辑器(Query/Variables/Headers/Auth),introspection 拉取 Schema 文档 + 编辑器补全(走
- Scripts:Pre-request 与 Tests 脚本(服务端 node:vm、CLI/Runner 内嵌 QuickJS,
rp.*API + 极简rp.expect断言,pm为兼容别名) - History:每次请求(含失败)自动落库,可回放为新草稿
- Runs:派发任务(Runner 执行)与 CLI 本机执行统一落库,Collection 的 Runs tab 可查看记录与逐请求断言结果
- RabbitPost CLI:接口/Collection/文件夹/环境增删改查(JSON 输出,面向 AI),本机执行用例(Postman 文件直跑 / 迭代 / globals / Cookie Jar),生成 JSON/HTML/JUnit 报告并上传;另含 lint、团队/Workspace/组织/Runner/文档/Spec/场景管理与 rt 长连接会话
- 错误透传:上游/网络错误原文返回,不做封装改写
# 1. 安装依赖
pnpm install
# 2. 配置环境变量
cp .env.example .env
# 编辑 .env,填入你的 Casdoor 配置(见下文)
# 3. 启动嵌入式 PostgreSQL(保持前台运行,首次会下载 PG 二进制)
pnpm db:up
# 4. 另开终端,同步数据库表结构
pnpm db:push
# 5. 启动前后端(web: http://localhost:5173, api: http://localhost:4000)
pnpm dev- 在你的 Casdoor 实例创建一个 Application,
Redirect URLs添加:http://localhost:5173/auth/callback - 将以下信息填入根目录
.env:CASDOOR_ENDPOINT:Casdoor 服务地址,如https://casdoor.example.comCASDOOR_CLIENT_ID/CASDOOR_CLIENT_SECRETCASDOOR_ORGANIZATION/CASDOOR_APPLICATIONCASDOOR_CERT:Application 关联证书的公钥(用于验签 id_token)
APP_SESSION_SECRET改为随机串:openssl rand -hex 32
未配置 Casdoor 时,登录接口会返回
503 CASDOOR_NOT_CONFIGURED及明确提示。
pnpm dev # 并行启动 web + api(nx run-many;api 会自动拉起内嵌 Runner)
pnpm dev:web # 仅前端
pnpm dev:api # 仅后端
pnpm db:up # 启动嵌入式 PG(开发)
pnpm db:push # drizzle-kit 同步 schema 到数据库
pnpm build # 全部构建
pnpm typecheck # 全部类型检查
pnpm runner:build # 构建 Runner(apps/runner/target/release/rabbitpost-runner)
pnpm cli:build # 构建 CLI(apps/cli/target/release/rabbitpost)
pnpm dev:desktop # 启动桌面壳(并行起 web dev server + Tauri 窗口)
pnpm desktop:build# 打包桌面安装包(本机平台,产物在 apps/desktop/src-tauri/target/release/bundle)
pnpm cli:package # 交叉编译全平台 CLI 并输出到 apps/api/public/cli(供面板下载)
pnpm runner:test # Runner + rp-core 测试(单元 + 契约)
pnpm cli:test # CLI 测试(单元 + assert_cmd/wiremock 功能测试)crates/rp-core:脚本沙箱语义(对齐服务端 pm-sandbox)、{{var}} 替换、执行引擎(变量/Auth/脚本改写/断言翻转/错误透传)、信封与 Runner 契约,均用 wiremock 本地模拟apps/cli:config/crud/report 单元测试 +tests/e2e.rs功能套件(assert_cmd 跑真实二进制、wiremock 模拟服务端,覆盖 auth/CRUD/run/报告/上传/退出码与用例展开)- GitHub Actions:
runner-test.yml/cli-test.yml在相关路径变更时跑 cargo test + clippy(-D warnings);runner-release.yml/cli-release.yml发布预 编译包(cli-release在 main 上 CLI 相关代码变更时自动触发并刷新 cli-latest,PR 只做跨平台构建验证)
apps/mock-server 的 Postman 全协议测试集(80 个请求:HTTP 方法 / Auth / Body 类型 / 状态码 / Advanced / SSE / GraphQL / MCP),源文件在
apps/mock-server/postman/rabbitpost-mock-server.postman_collection.json,也已发布到 Postman 团队空间:
RabbitPost Mock Server — 全协议测试集
# 先启动 mock-server(apps/mock-server 下 pnpm run start),然后:
postman collection run apps/mock-server/postman/rabbitpost-mock-server.postman_collection.jsonWebSocket / Socket.IO / gRPC / MQTT 为长连接协议,无法用 Collection JSON 表达,连接参数见 Collection 描述。
rabbitpost 是本地/CI 用的 Rust 单二进制(与服务端 API Key 认证配合)。
安装方式(三选一):
- 在「CLI 中心 → RabbitPost CLI」下载预编译包(推荐):由 API 的
/api/v1/cli/artifacts提供,覆盖 macOS(arm64/x64)、Linux(x64/arm64)、Windows(x64) - GitHub Releases:
cli-release工作流发布到cli-latest(main 上apps/cli/crates/rp-core变更自动触发,也可 tagcli-v*或手动触发) - 本地打包:
pnpm cli:package一次构建全部平台(需 zig + cargo-zigbuild + cargo-xwin), 产物与 manifest 输出到apps/api/public/cli/v<version>/供本实例直接下载
# 1. 在 Web「CLI 中心 → RabbitPost CLI」创建 API Key(rpk_...,只展示一次)
# 2. 配置凭证(无需登录,三选一):
# a) 命令行参数 --server / --api-key
# b) 环境变量(CI 推荐)
export RABBITPOST_SERVER=http://localhost:4000
export RABBITPOST_API_KEY=<API_KEY>
# c) 配置文件(之后所有命令免带参数)
# echo '{"server":"http://localhost:4000","apiKey":"<API_KEY>"}' > ~/.rabbitpost/config.json
# 3. 增删改查(默认 JSON 输出到 stdout,--table 可切换表格)
rabbitpost team list
rabbitpost request list --collection <COLLECTION_ID>
rabbitpost request create --collection <COLLECTION_ID> --name "获取用户" --method GET --url "{{host}}/users/1"
rabbitpost request update <ITEM_ID> --data @request.json # @文件 / - stdin / 字面量
rabbitpost env update <ENV_ID> --set host=https://api.example.com
# 4. Collection 导入 / 导出(rabbitpost.collection 与 Postman v2.1 文件互认)
rabbitpost collection export <COLLECTION_ID> --file backup.json
rabbitpost collection import --workspace <WORKSPACE_ID> --file collection.json
# 5. 本机执行用例(含 rp.* 断言),生成报告并上传(Runs tab 可见)
rabbitpost run --collection <COLLECTION_ID> --env <ENV_ID> \
--report json,html,junit --report-dir ./reports --upload
# 离线执行(newman 用法):直接跑本地 Collection 文件,环境也可用本地文件
rabbitpost run --file collection.json --env-file env.json \
-d data.csv -n 3 --folder "用户模块" --env-var host=https://api.example.com
# globals / 脚本变量回传 / Cookie 会话(跨请求共享,可导出)
rabbitpost run --file collection.json --globals globals.json \
--export-environment env-out.json --export-globals globals-out.json \
--cookie-jar cookies.json --export-cookie-jar cookies-out.json
# 静态检查(有 error 级 issue 时退出码 1,可直接做 CI 门禁)
rabbitpost collection lint <COLLECTION_ID> # 或 --file collection.json
rabbitpost spec lint --file openapi.yaml # 或 spec lint <SPEC_ID>
# 执行记录与历史(服务端数据)
rabbitpost runs list --collection <COLLECTION_ID>
rabbitpost runs get <JOB_ID>
rabbitpost runs report <JOB_ID> --format junit --file report.xml
rabbitpost history list --workspace <WORKSPACE_ID>
# 团队 / Workspace / 组织 / Runner / 文档 / Spec / 场景
rabbitpost team create --name "后端组"
rabbitpost team member-add <TEAM_ID> --email a@b.c --role viewer
rabbitpost workspace create --team <TEAM_ID> --name "支付"
rabbitpost org list
rabbitpost runner create --team <TEAM_ID> --name ci-1 # 返回一次性明文 token(rpr_...)
rabbitpost doc create --workspace <WORKSPACE_ID> --name "接入说明" --content @README.md
rabbitpost spec create --workspace <WORKSPACE_ID> --name "订单 API" --type openapi-3.0 --content @openapi.yaml
rabbitpost scenario steps <SCENARIO_ID>
rabbitpost scenario step-add <SCENARIO_ID> --source-item <ITEM_ID>
# 长连接协议(经 Runner 执行;事件以 JSON Lines 输出)
rabbitpost rt --workspace <WORKSPACE_ID> --protocol websocket --url ws://localhost:8080/ws \
--send "ping" --listen 15
# 退出码:0 全部通过 / 1 存在失败用例 / 2 操作错误(CI 可直接作门禁;-x 可强制恒 0)rabbitpost run 完整选项(对齐 Postman CLI / newman):
| 选项 | 说明 |
|---|---|
--collection / --request / --file |
执行目标三选一:服务端 Collection / 单请求 / 本地 Collection 文件 |
--env / --env-file |
环境二选一:服务端环境 id / 本地环境文件(Postman 环境导出、RabbitPost 环境、扁平 kv 均可) |
--globals <file> |
本地 globals 文件(Postman globals 导出 / 扁平 kv),优先级最低 |
--env-var KEY=VALUE |
覆盖变量,可多次;优先级最高 |
--global-var KEY=VALUE |
全局变量(globals 作用域),可多次;替换优先级同 --env-var |
--folder <名或路径> |
只跑指定文件夹(名称或 "A / B" 路径),可多次 |
-n, --iteration-count |
迭代轮数 |
-d, --iteration-data |
迭代数据(JSON 对象数组或 CSV,首行表头),每行注入一轮变量 |
--request-name <名> |
只跑指定名称的请求(请求名,可多次;其用例一并执行) |
--bail |
首个失败即停(隐含顺序执行) |
-x, --suppress-exit-code |
用例失败退出码也为 0 |
--delay-request <ms> |
每个请求前的固定延迟 |
--timeout-request <ms> |
覆盖请求超时(0 为不超时) |
--timeout-script <ms> |
覆盖脚本超时(0/缺省为引擎默认 5s) |
-k, --insecure |
跳过 TLS 证书校验 |
--silent / --verbose |
不输出逐请求日志 / 输出逐请求详情(状态、响应头、响应体截断、断言明细) |
--color auto/always/never |
日志着色(默认按终端探测) |
--working-dir <dir> |
相对输入文件的解析基准目录 |
--no-insecure-file-read |
禁止读取工作目录之外的输入文件 |
--cookie-jar <file> |
加载 Cookie Jar(Postman cookie 导出 / 极简数组),run 内跨请求共享 |
--export-cookie-jar <file> |
运行结束导出 Cookie Jar |
--export-environment <file> |
导出最终环境变量(含 rp.environment.set 改动,Postman 环境格式) |
--export-globals <file> |
导出最终 globals(含 rp.globals.set 改动,Postman globals 格式) |
--concurrency |
并发数(默认 4;--bail 时忽略) |
--report json,html,junit / --report-dir |
本地报告 |
--reporter-json-export <file> 等 |
显式报告导出路径(json/html/junit 三个,与 --report 并存) |
--upload |
上传报告到 Runs tab(--file 离线运行时自动跳过) |
变量优先级(低 → 高):globals(--globals)< Collection 变量 < 环境(--env / --env-file)< 迭代数据行 < --env-var / --global-var。
脚本改动传递:同一 run 内,rp.environment.set / rp.variables.set / rp.globals.set 的改动对后续请求可见(并发 >1 时按完成顺序合并,同键后完成者覆盖;需要严格顺序时用 --concurrency 1)。已知限制:沙箱的 rp.environment 作用域在执行替换时并入了 globals 与 Collection 变量,rp.environment.get 可能读到 globals 同名键;Cookie Jar 不跟随跨域重定向。
rabbitpost collection lint 内置规则:empty-name / empty-url / invalid-variable-syntax / unresolved-variable(error/warning)与 insecure-url / no-tests / duplicate-name(warning);--env-file / --globals 可补充可见变量。rabbitpost spec lint 规则与 Web 端 Issues 面板一致(Spectral 规则名,YAML/JSON 均可),差异:CLI 不输出行列号,以 path 定位。
凭证解析优先级:--server/--api-key > RABBITPOST_SERVER/RABBITPOST_API_KEY > 配置文件。
Runner(rabbitpost-runner serve)使用团队级 Runner Token(rpr_...),与 CLI 的个人 API Key 互不通用。
Windows ARM64 暂缺:rquickjs-sys 未附带 aarch64-pc-windows-msvc 预生成 bindings, 需 bindgen 现场生成,后续补齐;ARM 平台已由 macOS arm64 / Linux arm64 覆盖。
apps/desktop 是 Tauri 2 壳:窗口直接加载 Web 地址(默认 http://localhost:5173),
登录与所有功能与浏览器一致。要指向线上实例,改
apps/desktop/src-tauri/tauri.conf.json 中 app.windows[0].url 一处即可。
本地执行(local-agent):桌面壳启动时自动拉起随包分发的
rabbitpost-runner local-agent(只监听 127.0.0.1:17337+,不注册、不连接服务器)。
前端探测到它后,"执行"类请求改道本机,不再经过服务器代理:
- HTTP 请求(Send/用例 Run):本机执行(rp-core 引擎 + QuickJS 脚本,语义与服务端一致), 结果自动回传服务器写入 History;agent 不可用时自动回退服务器执行
- 长连接协议(WebSocket/MQTT/gRPC/MCP/SSE 等):session 由本机 agent 托管, 可直接触达本机/内网目标
pnpm dev:desktop # 本地调试(自动并行启动 web dev server;会先构建 sidecar)
pnpm desktop:build # 打出本机平台安装包(含内嵌 runner sidecar)Windows(.msi/.exe)与 Linux(.deb/.AppImage)安装包由 desktop-release 工作流
跨平台构建:推 desktop-v* tag 产出带版本号的 Release,手动触发则刷新
desktop-latest 滚动 Release,PR 上只做构建验证。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/auth/login |
获取 Casdoor 授权地址 |
| POST | /api/v1/auth/callback |
授权码换会话 |
| GET | /api/v1/auth/me |
当前用户(会话或 API Key) |
| GET/POST | /api/v1/auth/api-keys |
API Key 列表 / 创建(仅会话) |
| DELETE | /api/v1/auth/api-keys/:id |
吊销 API Key(仅会话) |
| GET/POST | /api/v1/teams |
团队列表 / 创建 |
| GET/PATCH/DELETE | /api/v1/teams/:id |
团队详情/更新/删除 |
| GET/POST/PATCH/DELETE | /api/v1/teams/:id/members |
成员管理 |
| GET/POST | /api/v1/workspaces |
Workspace 列表(?teamId=) / 创建 |
| GET/POST | /api/v1/workspaces/:id/collections |
Collection 列表 / 创建 |
| GET | /api/v1/collections/:id/tree |
Collection 树(folder/request) |
| POST | /api/v1/collections/:id/items |
新建条目 |
| GET/PATCH/DELETE | /api/v1/items/:id |
条目详情/更新/删除 |
| GET/POST | /api/v1/workspaces/:id/environments |
环境列表 / 创建 |
| GET/PATCH/DELETE | /api/v1/environments/:id |
环境详情/更新/删除 |
| GET/DELETE | /api/v1/workspaces/:id/history |
历史列表 / 清空 |
| POST | /api/v1/workspaces/:id/history |
客户端上报一条历史(桌面端本地执行后回传) |
| POST | /api/v1/execute |
服务端代理执行 HTTP 请求 |
| GET | /api/v1/collections/:id/runs |
该 Collection 的执行记录(Runs tab) |
| POST | /api/v1/collections/:id/runs |
CLI 上传执行报告(rabbitpost.run-report 格式) |
// Pre-request:发送前执行
pm.environment.set("ts", Date.now());
pm.variables.set("token", "xxx");
console.log(pm.environment.get("baseUrl"));
// Tests:响应返回后执行
pm.test("status is 200", () => {
pm.response.to.have.status(200);
});
pm.test("body has id", () => {
const body = pm.response.json();
pm.expect(body.id).to.exist();
});rp.globals(get/set/unset/toObject)为 globals 作用域:CLI rabbitpost run 内跨请求、跨迭代持久(配合 --globals / --export-globals);服务端一次性执行(Send)不持久化,仅保证 API 可用。
Workspace 级数据库连接(Apifox 风格),在侧边栏 Databases 面板管理:支持 MySQL / PostgreSQL / SQLite / Redis,可按环境做字段覆盖(envOverrides);密码与覆盖密文用 DB_SECRET_KEY(AES-256-GCM,.env 中配置)加密存储,API 只写不读(永不回传明文)。
每个请求可配置声明式数据库操作(请求编辑器的「前置/后置数据库操作」):pre 在发送前、post 在响应返回后按序执行;SQL 操作支持 ? 参数绑定与 extract 变量提取(rows 全部行 JSON / row 首行 JSON / row.<col> 首行某列 / value Redis 返回值),提取结果写入变量表,对后续操作的 {{var}} 与脚本 rp.environment.get 可见。单条操作失败不中断请求,错误记入 Console([db:pre] / [db:post] 前缀)。
脚本内可用 rp.db API(顶层 await,两个沙箱语义一致):
// Pre-request / Tests 脚本中:
const res = await rp.db.query("main", "SELECT name FROM users WHERE id = ?", [1]);
rp.environment.set("userName", res.rows[0].name); // res: { rows, rowCount, truncated? }
const wr = await rp.db.exec("main", "UPDATE users SET seen = 1 WHERE id = ?", [1]); // { affectedRows }
const v = await rp.db.redis("cache", "GET", ["token:1"]);CLI 本机执行用 --db-connection NAME=URL(可多次,scheme 推导类型:mysql:// / postgres:// / sqlite://<path> / redis://,密码写在 URL 里)或 --db-connections-file conns.json(JSON 数组 [{ "name", "config": { "type", ... }, "password"? }],同名被命令行覆盖);因服务端不回传密码,CLI 不支持在线拉取连接。
护栏:连接超时 5s(connectTimeoutMs 可覆盖)、单条语句 10s、查询结果最多 1000 行(超出截断并标记 truncated)、连接可设 readOnly(拒绝非 SELECT 语句)。执行结束统一关闭本次建立的连接。
DATABASE_URL指向真实 PostgreSQL,db:push(或db:generate生成迁移)同步表结构WEB_ORIGIN改为前端正式域名,并在 Casdoor 添加对应 redirect URLpnpm build后,apps/api以next start运行,apps/web产物为静态文件