Files
raisfast/docs/plugin-system-assessment.md
T

311 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 插件系统评估
> 基于 WASM/JS/Lua 三引擎插件系统完成后的全面评估,分析当前水平与差距,确定改进方向。
---
## 1. 当前水平
**中等偏上** — 对博客系统而言优秀,对平台框架而言不够。
---
## 2. 已有的亮点
| 能力 | 说明 |
|------|------|
| **三引擎沙箱** | WASM (wasmtime) / JavaScript (rquickjs) / Lua (mlua),比大多数开源博客项目丰富 |
| **Hook 四类分派** | Filter(链式)/ Action(顺序)/ RenderOverride(首覆写)/ Route(自定义路由) |
| **热重载** | notify 文件监听 + mpsc channel + 1 秒防抖,支持 `.wasm` / `.js` / `.lua` |
| **优先级调度** | manifest 声明 priority,按数值排序执行 |
| **安全沙箱** | WASM fuel/memory 限制;JS interrupt handler 超时;Lua 禁用 io/os/debug 库 + 指令计数超时 |
| **Host API 跨引擎统一** | `Host.log()` + `Host.getConfig()` 在三引擎中行为一致 |
| **测试覆盖** | 117 单元测试 + 60 集成测试,含三引擎交叉测试 |
| **Feature flag 隔离** | `plugin-wasm` / `plugin-js` / `plugin-lua` / `plugin-all`,按需编译 |
---
## 3. 缺什么才算「先进」
### 3.1 P0 — 不补就做不了有用的插件
#### Host API 严重不足
当前仅 `log` + `getConfig` 两个 Host 函数。插件无法与外部世界交互,实际能做的事极其有限。
**需要补齐:**
| Host 函数 | 说明 | 引擎 |
|-----------|------|------|
| `host_http_get(url)` | HTTP GET 请求(受域名白名单限制) | 三引擎 |
| `host_http_post(url, body)` | HTTP POST 请求(受域名白名单限制) | 三引擎 |
| `host_get_post(slug)` | 获取文章内容(只读) | 三引擎 |
| `host_db_query(sql, params)` | 数据库只读查询(受表权限控制) | 三引擎 |
| `host_kv_get(key)` | 插件 KV 存储 — 读取 | 三引擎 |
| `host_kv_set(key, value, ttl?)` | 插件 KV 存储 — 写入 | 三引擎 |
**实现要点:**
- HTTP 请求需校验 manifest 中 `permissions.http` 白名单
- DB 查询需校验 `permissions.db-read` 表白名单,且强制只读(SELECT only
- KV store 可用 SQLite `plugin_storage` 表(`plugin_id TEXT, key TEXT, value TEXT, expires_at TEXT`
#### 权限声明未强制执行
manifest 中声明了 `permissions`http、config、db-read、db-write),但运行时没有任何检查逻辑。
**修复方案:**
```rust
// src/plugins/permissions.rs
impl Permissions {
pub fn is_url_allowed(&self, url: &str) -> bool {
// 解析 URL domain,检查是否匹配 permissions.http 白名单
}
pub fn is_config_key_allowed(&self, key: &str) -> bool {
// 检查 key 是否匹配 permissions.config 前缀
}
pub fn is_table_readable(&self, table: &str) -> bool {
self.db_read.iter().any(|t| t == table)
}
}
```
每个 Host 函数实现中必须在执行前检查权限,拒绝则返回错误。
---
### 3.2 P1 — 不补就做不好
#### 插件管理 API
运行时无法通过 API 查看插件状态、启用/禁用插件。
**需要:**
```
GET /api/v1/admin/plugins — 列出所有插件及状态
GET /api/v1/admin/plugins/:id — 插件详情
POST /api/v1/admin/plugins/:id/enable — 启用
POST /api/v1/admin/plugins/:id/disable — 禁用
POST /api/v1/admin/plugins/:id/reload — 重载
DELETE /api/v1/admin/plugins/:id — 卸载(从内存+磁盘移除)
```
#### 生命周期 Hook
插件无法在加载/卸载时执行初始化/清理逻辑。
**需要:**
| Hook | 触发时机 | 用途 |
|------|---------|------|
| `on_load` | 插件加载完成 | 初始化连接、注册定时任务、预加载数据 |
| `on_unload` | 插件卸载前 | 清理资源、关闭连接、保存状态 |
| `on_config_change` | 宿主配置变更时 | 热更新插件行为 |
#### 插件持久状态
插件没有地方存储自己的数据。例如 SEO 插件想缓存分析结果、通知插件想记录已发送的邮件。
**方案 A — SQLite plugin_storage 表:**
```sql
CREATE TABLE IF NOT EXISTS plugin_storage (
plugin_id TEXT NOT NULL,
key TEXT NOT NULL,
value TEXT NOT NULL,
expires_at TEXT,
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
PRIMARY KEY (plugin_id, key)
);
```
**方案 B — KV Host API**
```lua
-- Lua 插件中
Host.setData("last_notified", "2026-04-14T00:00:00Z")
local last = Host.getData("last_notified")
```
---
### 3.3 P2 — 做大时才需要
#### 错误恢复
当前插件抛异常后静默跳过,无法感知插件健康状态。
**需要:**
- 每个插件维护 `error_count``last_error`
- 连续 N 次错误(如 5 次)后自动禁用该插件
- 记录错误日志 + 可选告警通知
- 管理 API 中展示插件健康状态
```rust
struct PluginHealth {
error_count: u32,
last_error: Option<String>,
last_error_at: Option<String>,
auto_disabled: bool,
}
```
#### EventBus(事件总线)
当前 Hook 是同步链式调用,无法解耦。平台级系统需要事件驱动架构。
**差距:**
- 插件无法订阅内部事件(如"用户注册"不在 Hook 中则无法感知)
- 一个 Hook 的处理阻塞后续所有插件
- 无法异步响应(如"文章创建后异步触发邮件发送")
**方案:**
```rust
// src/eventbus/mod.rs
pub struct EventBus {
tx: broadcast::Sender<Arc<Event>>,
}
// 订阅者
EventBus.emit(PostCreated)
├── PluginManager.dispatch_action(PostCreated)
├── NotificationService.notify(author)
├── SearchIndexer.index(post)
└── AuditLogger.log("post.create", ...)
```
#### 插件间通信
当前插件之间完全隔离,无法互相发现或调用。
**可能的方案:**
- 注册表模式:插件 A 在宿主注册一个 named service,插件 B 通过 `Host.getService("name")` 调用
- 需要仔细设计以避免循环依赖和安全问题
#### 性能指标
缺少插件执行的可观测性。
**需要:**
| 指标 | 说明 |
|------|------|
| Hook 执行耗时 | 每个 Hook × 每个插件的 P50/P95/P99 |
| 内存占用 | 每个插件的实际内存使用 |
| 调用次数 | 每个 Hook 的调用频率 |
| 错误率 | 每个插件的错误次数/频率 |
可集成 Prometheus metrics 或通过管理 API 暴露。
#### 依赖管理
无插件版本冲突检测、无依赖声明。
**manifest 扩展:**
```toml
[plugin]
id = "com.example.seo-optimizer"
version = "1.0.0"
[dependencies]
"com.example.utils" = ">=1.0.0"
```
加载时检测依赖是否满足,缺失则拒绝加载。
---
## 4. 与成熟系统对比
| 能力 | WordPress | Ghost | **raisfast** |
|------|-----------|-------|---------------|
| 沙箱隔离 | 无(PHP 直接执行) | 无(Node 直接 require | **有(三引擎)** |
| 热重载 | 需插件自身支持 | 需重启 | **有** |
| Hook 数量 | ~2000 个 filter/action | ~30 个事件 | 11 个 Hook |
| Host API | 完整(DB/HTTP/FS/Option | 完整 | 2 个函数 |
| 权限控制 | Role + Capability | 细粒度 | 声明式但未执行 |
| 插件状态存储 | wp_options 表 | DB 表 | 无 |
| 插件市场 | 有 | 有 | 无 |
| 多语言插件 | PHP only | JS only | **Rust/JS/Lua/WASM** |
**raisfast 的独特优势**是三引擎沙箱隔离,这在博客系统中非常少见。但 Host API 和生态成熟度差距明显。
---
## 5. 改进路线图
### Phase 1 — 补齐基础能力(1 周)
```
Week 1:
├── 权限执行模块(permissions.rs
├── Host API: http_get/http_post(域名白名单)
├── Host API: get_post(文章只读访问)
├── Host API: KV storeplugin_storage 表 + get_data/set_data
├── 插件管理 API(列表/详情/启用/禁用/重载)
└── 集成测试覆盖新 Host API + 权限检查
```
### Phase 2 — 生命周期与健壮性(1 周)
```
Week 2:
├── 生命周期 Hookon_load/on_unload/on_config_change
├── 错误恢复(错误计数 + 自动禁用 + 健康状态)
├── 插件管理 API 增强(健康状态、错误日志)
└── Host API: db_query(只读查询 + 表权限校验)
```
### Phase 3 — 平台化能力(按需)
```
├── EventBus 事件总线
├── 插件间通信机制
├── 性能指标采集(Prometheus 或内部 metrics
├── 依赖管理与版本冲突检测
└── 插件脚手架 CLIcargo run -- generate-plugin
```
---
## 6. 依赖新增预估
| 阶段 | 依赖 | 用途 | 体积影响 |
|------|------|------|---------|
| Phase 1 | `reqwest`(可能已有) | Host HTTP 请求 | 零增长(已有) |
| Phase 1 | 无 | KV store 用 SQLite | 零增长 |
| Phase 2 | 无 | 错误恢复纯内存 | 零增长 |
| Phase 3 | `prometheus`(可选) | 指标采集 | +~1MB |
Phase 1-2 几乎不增加外部依赖。
---
## 7. 总结
```
当前状态:
✅ 三引擎沙箱 — 业界领先
✅ 热重载 — 生产可用
✅ Hook 四类分派 — 架构合理
✅ 安全模型 — 沙箱限制到位
✅ 测试覆盖 — 质量有保障
❌ Host API — 仅 2 个函数,插件做不了实事
❌ 权限执行 — 声明式但未运行时检查
❌ 管理接口 — 无法运行时管理插件
❌ 生命周期 — 无初始化/清理回调
❌ 持久状态 — 插件无法存数据
```
**核心判断:** 架构骨架扎实,三引擎沙箱是独特优势。但当前能扩展的点太少(11 Hook + 2 Host API),实际可编写的插件非常有限。补齐 Host API、权限执行、管理接口后,才算真正可用的插件平台。