Plugins
概述
用 JS、Lua、Rhai 或 WASM 扩展 RaisFast。几分钟写出一个可运行的插件。
插件是 RaisFast 的扩展机制。一个插件就是一个目录,包含 manifest.toml 和一个入口文件。就这么简单。
你的第一个插件
raisfast plugin new my-plugin --runtime js自动生成:
extensions/plugins/my-plugin/
├── manifest.toml
└── main.js在 main.js 中写你的第一个 Hook:
import { logInfo } from 'sdk';
export function on_content_created(input) {
const data = JSON.parse(input);
logInfo("new content: " + data.id);
}启动服务 — 插件自动加载。每次创建内容时,你都会看到日志输出。
架构
manifest.toml 入口文件 (.js / .lua / .rhai / .wasm)
│ │
└─────────┬───────────────┘
▼
PluginManager(Arc 共享)
├─ 拓扑排序(依赖顺序)
├─ JS / Lua: per-request(每次调用新建 VM)
├─ Rhai: per-request(预编译 AST,新建 Scope)
├─ WASM: 实例池(round-robin)
└─ 热重载(文件系统监听)
│
▼
Host API(沙箱权限控制)
├─ 数据库(查询 / 插入 / 更新 / 删除)
├─ HTTP 客户端(GET / POST)
├─ 虚拟文件系统(读 / 写 / 列表)
├─ KV 存储(按插件隔离)
├─ 配置读取
└─ 事件、日志、ID 生成四种运行时
| JavaScript | Lua | Rhai | WASM | |
|---|---|---|---|---|
| 引擎 | QuickJS | mlua (Lua 5.4) | Rhai | wasmtime |
| 入口 | main.js | init.lua | init.rhai | plugin.wasm |
| SDK 导入 | import { ... } from 'sdk' | local sdk = require("sdk") | 无(全局注入) | WIT 接口 |
| 并发模型 | Per-request | Per-request | Per-request | 实例池 |
| 语言 | ES2024(async/await) | Lua 5.4 | Rust 风格 DSL | Rust、Go、C、Zig |
| 适合 | 快速集成 | 游戏 / 嵌入式开发者 | 简单自动化 | 重计算 |
三种脚本运行时可同时编译、同时加载。WASM 通过 Cargo Feature 独立编译。
JavaScript
import { dbQuery, ok, logInfo } from 'sdk';
export function on_content_created(input) {
const data = JSON.parse(input);
logInfo("created: " + data.id);
}
export function getProducts() {
const rows = dbQuery("SELECT * FROM products");
return ok(rows);
}- 运行时:QuickJS(ES2024,支持 async/await、可选链)
- 入口:
main.js - 导入 SDK:
import { ... } from 'sdk'
Lua
local sdk = require("sdk")
Plugin = {}
Plugin.on_content_created = function(input)
local data = sdk.extractJson(input, "body")
sdk.logInfo("created: " .. tostring(data.id))
end
Plugin.getProducts = function(input)
local rows = sdk.dbQuery("SELECT * FROM products")
return sdk.ok(rows)
end- 运行时:Lua 5.4(沙箱化:无 IO/OS/debug)
- 入口:
init.lua - 导入 SDK:
local sdk = require("sdk")
Rhai
fn on_content_created(input) {
log("info", "created: " + input.id);
}
fn get_products(input) {
let rows = db_query("SELECT * FROM products", "");
rows
}- 运行时:Rhai(Rust 原生脚本,无需导入 SDK)
- 入口:
init.rhai - 宿主函数直接注入全局作用域
插件能做什么
| 功能 | 说明 |
|---|---|
| Hooks | 响应事件 — 内容创建、用户登录、定时触发等 |
| 路由 | 提供自定义 REST API 端点 |
| 定时任务 | 用 Cron 表达式调度后台任务 |
| 数据库 | 查询和修改数据(受权限控制) |
| HTTP | 发起外部 HTTP 请求(白名单控制) |
| VFS | 按插件隔离的虚拟文件系统 |
| KV 存储 | 按插件隔离的键值存储 |
| 事件 | 发射和监听自定义事件 |
插件结构
my-plugin/
├── manifest.toml ← 元数据、权限、钩子、路由、定时任务
└── main.js ← 入口文件(.js / .lua / .rhai / .wasm)最小 manifest:
[plugin]
id = "com.example.my-plugin"
name = "My Plugin"
version = "0.1.0"
runtime = "js"
entry = "main.js"热重载
PLUGIN_HOT_RELOAD=true(默认开启)时,编辑插件文件后自动重新加载,无需重启服务。1 秒防抖,2 秒轮询。
管理
# CLI
raisfast plugin check # 校验所有插件
raisfast plugin check ./plugins/my-plugin # 校验指定插件
# Admin API
GET /api/v1/admin/plugins # 列出所有插件
GET /api/v1/admin/plugins/{id} # 插件详情
POST /api/v1/admin/plugins/{id}/reload # 重载插件
POST /api/v1/admin/plugins/{id}/disable # 禁用
POST /api/v1/admin/plugins/{id}/enable # 启用
DELETE /api/v1/admin/plugins/{id} # 卸载