Skip to content

Plugin Packaging

worable edited this page Oct 12, 2026 · 1 revision

打包与签名

插件开发完成后打包为 ZIP 上传安装。生产环境使用 Ed25519 签名校验包来源与完整性。

打包

使用 pack 脚本

在仓库根目录执行:

pnpm plugin:pack <plugin-name>

该命令执行 apps/api/scripts/pack-plugin.ts:

  1. 运行插件 build。内置插件走 scripts/build-plugin.mjs(esbuild 打包 + tsc 输出声明)。
  2. 自动生成 manifest.json:读取 package.json,注入 schemaVersion 与 apiVersion,并从编译产物的 manifest 读取 requires/provides/consumes/permissions/roleTemplates。
  3. 产出 packages/plugins/<name>/dist-pack/<name>-<version>.zip,内含 dist/、可选的 assets/ 与可选的 frontend/。

手动打包

pack-plugin.ts 已自动排除 .d.ts、.d.ts.map、.ts、.tsx 等源码,只保留编译产物与前端包。如确需手动打包,只包含必要文件:

cd packages/plugins/my-plugin
zip -r dist-pack/my-plugin-1.0.0.zip \
  manifest.json \
  dist/index.js \
  dist/index.js.map \
  frontend/   # 如果有前端包

ZIP 结构

my-plugin-1.0.0.zip
├── manifest.json          # 必须
├── dist/
│   ├── index.js           # 必须(或 manifest.entry 指定的文件)
│   └── index.js.map       # 可选
├── assets/                # 可选:静态资源
├── frontend/              # 可选
│   ├── manifest.json
│   └── dist/
│       ├── index.js
│       └── admin.js       # 有客户端入口时
└── signature.json         # 可选

上传校验规则

上传 ZIP 时,内核执行以下校验:

规则 限制
最大文件数 128
单文件最大 1 MB
解压总大小 8 MB
入口文件最大 512 KB
禁止源码 .ts/.tsx/.jsx/.mts/.cts/.d.ts.map
禁止隐藏文件 .git*、.htaccess
路径安全 禁止 ..、绝对路径
ID 格式 /^[a-z0-9_-]{1,64}$/
保留 ID 不可使用内置插件 ID、kernel、default

依赖检查

上传时检查 requires 声明的依赖:

  • 非 optional 依赖必须已安装,否则返回 409
  • 依赖版本必须满足 range(如有)
  • 内置插件不可通过上传覆盖

签名

生成密钥对

openssl genpkey -algorithm ed25519 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem

签名

使用 scripts/sign-package.mjs:

pnpm package:sign my-plugin-1.0.0.zip private.pem

该命令计算 ZIP 内所有文件(除 signature.json)的 SHA-256 哈希,生成 signature.json 并写入 ZIP:

{
  "algorithm": "ed25519",
  "files": {
    "manifest.json": "sha256...",
    "dist/index.js": "sha256..."
  },
  "signature": "base64..."
}

配置公钥

公钥可通过两种方式配置:

  1. 环境变量(优先级高,不可变):STACKPANEL_SIGNING_PUBLIC_KEY
  2. 管理后台:PATCH /admin/signing(存储在 settings 表)
PATCH /admin/signing
{ "publicKey": "-----BEGIN PUBLIC KEY-----\n..." }

签名策略

PACKAGE_SIGNATURE_REQUIRED 公钥状态 行为
false(开发默认) 未配置 跳过签名校验
true(生产默认) 未配置 拒绝所有上传(启动时也会因缺公钥而报错)
任意 已配置 必须有有效签名;缺 signature.json、哈希不符或验签失败均拒绝

只要配置了公钥,签名就是强制的——PACKAGE_SIGNATURE_REQUIRED=false 不会放宽「已配置公钥」场景。

签名验证流程

  1. 读取 ZIP 中的 signature.json
  2. 计算所有非签名文件的 SHA-256 哈希(排序后)
  3. 与 signature.json 中的 files 比对
  4. 用公钥验证签名

上传安装

管理后台

访问 /admin/plugins,点击上传按钮选择 ZIP 文件。

API

curl -X POST http://127.0.0.1:3001/admin/plugins/upload \
  -H "Authorization: Bearer <token>" \
  -F "file=@my-plugin-1.0.0.zip"

响应(HTTP 201):

{
  "installed": true,
  "live": true,
  "upgraded": false,
  "id": "my-plugin",
  "frontendApply": { "requestedAt": "2026-01-01T00:00:00.000Z" }
}
  • upgraded: false — 新安装;true — 升级已存在的插件
  • frontendApply — 含前端产物时返回,表示已请求 web 容器重建 registry 并重启;无前端时为 null

启用插件

上传后插件处于 registered 状态,需启用:

curl -X PATCH http://127.0.0.1:3001/admin/plugins/my-plugin \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

删除插件

curl -X DELETE http://127.0.0.1:3001/admin/plugins/my-plugin \
  -H "Authorization: Bearer <token>"

内置插件不可删除。

模板脚手架

packages/plugins/template 是最小可发布脚手架,复制改名即可开发:

cp -r packages/plugins/template packages/plugins/my-plugin

Clone this wiki locally