mirror of
https://github.com/RaisFast/raisfast.git
synced 2026-09-24 08:02:36 +00:00
8.0 KiB
8.0 KiB
插件开发指南
rust-blog 支持两种插件运行时:WASM(Rust 编写)和 JavaScript(QuickJS)。 本文档聚焦 JS 插件开发。
快速开始
1. 创建插件目录
plugins/my-plugin/
├── plugin.toml # 插件清单
└── index.js # JS 入口
2. 编写 plugin.toml
[plugin]
id = "com.example.my-plugin" # 全局唯一 ID(反向域名格式)
name = "My Plugin" # 显示名称
version = "1.0.0" # 语义化版本
description = "插件描述"
author = "Your Name"
license = "MIT"
runtime = "js" # 必须设为 "js"
language = "javascript" # 或 "typescript"
entry = "index.js" # JS 入口文件名(默认 index.js)
[permissions]
max_memory_mb = 8 # 内存限制(默认 32MB)
timeout_ms = 2000 # 单次 Hook 执行超时(默认 5000ms)
[hooks.on_post_creating] # 注册的 Hook,key 用下划线或短横线均可
priority = 10 # 优先级,数字越小越先执行
[hooks.filter_html]
priority = 20
3. 编写 index.js
var Plugin = {
on_post_creating: function(inputJson) {
var input = JSON.parse(inputJson);
// 修改输入数据
input.title = input.title.trim();
return JSON.stringify(input);
},
filter_html: function(html) {
// 在 <head> 后注入 OG 标签
return html.replace("<head>", '<head><meta property="og:type" content="article">');
}
};
4. 部署
# 手动复制
cp -r plugins-examples-js/my-plugin/ plugins/my-plugin/
# 或用 justfile(需添加到 justfile)
just plugins-js-build
Hook 类型
JSON Filter — 修改数据
接收 JSON 字符串,返回修改后的 JSON 字符串。
| Hook | 触发时机 | 数据内容 |
|---|---|---|
on_post_creating |
文章创建前 | { title, content, excerpt, category_id, ... } |
on_post_updating |
文章更新前 | 同上 + id |
on_comment_creating |
评论创建前 | { content, post_id, parent_id, ... } |
on_post_creating: function(inputJson) {
var input = JSON.parse(inputJson);
input.excerpt = input.content.substring(0, 200);
return JSON.stringify(input); // 必须返回 JSON 字符串
}
JSON Action — 通知/副作用
接收 JSON 字符串,无返回值。适合日志、通知、缓存清理等。
| Hook | 触发时机 | 数据内容 |
|---|---|---|
on_post_created |
文章创建后 | { id, title, slug, ... } |
on_post_updated |
文章更新后 | 同上 |
on_post_deleted |
文章删除后 | { id } |
on_comment_created |
评论创建后 | { id, content, post_id, ... } |
on_login |
用户登录后 | { email, success } |
on_post_created: function(dataJson) {
var data = JSON.parse(dataJson);
Host.log("info", "New post: " + data.title);
}
String Filter — 修改原始字符串
| Hook | 触发时机 | 输入 | 返回 |
|---|---|---|---|
render_markdown |
Markdown 渲染(替代默认渲染器) | Markdown 原文 | HTML 字符串 |
filter_html |
HTML 后处理 | HTML 字符串 | 修改后的 HTML |
render_markdown: function(content) {
// 自定义渲染逻辑
return "<p>" + content + "</p>";
}
Route Handler — 自定义路由
handle_route: function(routeJson) {
var route = JSON.parse(routeJson);
// route.path, route.method
return JSON.stringify({
status: 200,
body: JSON.stringify({ message: "Hello from plugin!" })
});
}
需要在 plugin.toml 中配置 match 模式:
[hooks.handle_route]
match = "/api/v1/custom/*" # glob 风格,* 匹配单段路径
priority = 5
宿主 API
插件通过全局 Host 对象与宿主交互。
Host.log(level, message)
写入宿主日志。
Host.log("info", "这条消息会出现在宿主日志中");
Host.log("warn", "警告信息");
Host.log("error", "错误信息");
Host.getConfig(key)
读取宿主配置。返回字符串或 null。
var env = Host.getConfig("app.env"); // "development" / "production"
var port = Host.getConfig("app.port"); // "3000"
var baseUrl = Host.getConfig("app.base_url"); // "http://localhost:3000"
var maxSize = Host.getConfig("upload.max_size");
支持的 key:
| Key | 说明 |
|---|---|
app.host |
监听地址 |
app.port |
监听端口 |
app.env |
运行环境 |
app.base_url |
站点 URL |
jwt.access_expires |
Access Token 过期时间(秒) |
jwt.refresh_expires |
Refresh Token 过期时间(秒) |
upload.dir |
上传目录 |
upload.max_size |
上传大小限制(字节) |
plugin.max_memory_mb |
插件内存限制 |
plugin.default_timeout_ms |
插件超时时间 |
注意:
jwt.secret和database_url等敏感配置不暴露给插件。
TypeScript 开发
1. 安装 SDK 类型定义
在插件目录创建 tsconfig.json:
{
"compilerOptions": {
"target": "ES2021",
"module": "ES2020",
"strict": true,
"noEmit": true,
"baseUrl": "..",
"paths": {
"rust-blog-plugin-sdk": ["plugins-sdk-js"]
}
}
}
2. 编写 TypeScript
/// <reference path="../../plugins-sdk-js/index.d.ts" />
var Plugin: PluginHooks = {
on_post_creating(inputJson: string): string {
var input = JSON.parse(inputJson);
input.excerpt = input.content.substring(0, 200);
return JSON.stringify(input);
}
};
3. 编译为 JS
# 用 esbuild 编译
npx esbuild plugins-examples-js/my-plugin/src/index.ts \
--outfile=plugins-examples-js/my-plugin/index.js \
--bundle --format=iife --target=es2021
# 或用 justfile
just plugins-ts-build
安全限制
JS 插件运行在 QuickJS 沙箱中:
| 限制 | 说明 |
|---|---|
| 内存 | 默认 32MB,可在 [permissions] 中配置 |
| 超时 | 默认 5000ms,超时自动中断 |
| 无文件系统 | 不能读写文件 |
| 无网络 | 不能发起 HTTP 请求 |
| 隔离作用域 | 每个插件独立的全局对象,互不干扰 |
热重载
当 PLUGIN_HOT_RELOAD=true 时,修改 plugins/ 目录下的 .js 或 .wasm 文件会自动触发插件重载:
PLUGIN_HOT_RELOAD=true cargo run
热重载仅监听文件变化,不会自动编译 TypeScript。开发 TS 插件时需手动或用 watch 模式运行 esbuild。
完整示例
Welcome Email(JS)
// plugins/welcome-email/index.js
var Plugin = {
on_login: function(dataJson) {
var data = JSON.parse(dataJson);
if (data.success) {
Host.log("info", "User logged in: " + data.email);
}
}
};
SEO Optimizer(JS)
// plugins/seo-optimizer-js/index.js
var Plugin = {
on_post_creating: function(inputJson) {
var input = JSON.parse(inputJson);
if (!input.excerpt || input.excerpt === "") {
var plain = input.content
.replace(/```[\s\S]*?```/g, "")
.replace(/[#*_`]/g, "")
.replace(/\s+/g, " ")
.trim();
input.excerpt = plain.substring(0, 200);
if (plain.length > 200) input.excerpt += "...";
}
return JSON.stringify(input);
},
filter_html: function(html) {
var meta = '<meta property="og:type" content="article">';
return html.replace("<head>", "<head>" + meta);
}
};
调试技巧
- 查看日志 —
Host.log()输出到宿主 tracing 日志,开发环境默认打印到终端 - 错误不崩溃 — 插件 Hook 抛异常时,宿主会跳过该插件继续执行,不影响请求
- 禁用插件 — 在
.env中设置PLUGIN_DISABLED=com.example.bad-plugin - 超时测试 — 设置短超时
[permissions] timeout_ms = 100来验证中断机制