Files
GoTunnel/PLUGINS.md
Flik 2f98e1ac7d
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 30s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m16s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 1m4s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 2m29s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 54s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 2m35s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 55s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 2m21s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m35s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m1s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m55s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m39s
feat(logging): implement client log streaming and management
- Added log streaming functionality for clients, allowing real-time log access via SSE.
- Introduced LogHandler to manage log streaming requests and responses.
- Implemented LogSessionManager to handle active log sessions and listeners.
- Enhanced protocol with log-related message types and structures.
- Created Logger for client-side logging, supporting various log levels and file output.
- Developed LogViewer component for the web interface to display and filter logs.
- Updated API to support log stream creation and management.
- Added support for querying logs by level and searching through log messages.
2026-01-03 16:19:52 +08:00

646 lines
12 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.
# GoTunnel 插件开发指南
本文档介绍如何为 GoTunnel 开发 JS 插件。JS 插件基于 [goja](https://github.com/dop251/goja) 运行时,运行在客户端上。
## 目录
- [快速开始](#快速开始)
- [插件结构](#插件结构)
- [API 参考](#api-参考)
- [示例插件](#示例插件)
- [插件签名](#插件签名)
- [发布到商店](#发布到商店)
---
## 快速开始
### 最小插件示例
```javascript
// 必须:定义插件元数据
function metadata() {
return {
name: "my-plugin",
version: "1.0.0",
type: "app",
description: "My first plugin",
author: "Your Name"
};
}
// 可选:插件启动时调用
function start() {
log("Plugin started");
}
// 必须:处理连接
function handleConn(conn) {
// 处理连接逻辑
conn.Close();
}
// 可选:插件停止时调用
function stop() {
log("Plugin stopped");
}
```
---
## 插件结构
### 生命周期函数
| 函数 | 必须 | 说明 |
|------|------|------|
| `metadata()` | 否 | 返回插件元数据,不定义则使用默认值 |
| `start()` | 否 | 插件启动时调用 |
| `handleConn(conn)` | 是 | 处理每个连接 |
| `stop()` | 否 | 插件停止时调用 |
### 元数据字段
```javascript
function metadata() {
return {
name: "plugin-name", // 插件名称
version: "1.0.0", // 版本号
type: "app", // 类型: "app" (应用插件)
description: "描述", // 插件描述
author: "作者" // 作者名称
};
}
```
---
## API 参考
### 基础 API
#### `log(message)`
输出日志信息。
```javascript
log("Hello, World!");
// 输出: [JS:plugin-name] Hello, World!
```
#### `config(key)`
获取插件配置值。
```javascript
var port = config("port");
var host = config("host") || "127.0.0.1";
```
---
### 连接 API (conn)
`handleConn` 函数接收的 `conn` 对象提供以下方法:
#### `conn.Read(size)`
读取数据,返回字节数组,失败返回 `null`
```javascript
var data = conn.Read(1024);
if (data) {
log("Received " + data.length + " bytes");
}
```
#### `conn.Write(data)`
写入数据,返回写入的字节数。
```javascript
var written = conn.Write(data);
log("Wrote " + written + " bytes");
```
#### `conn.Close()`
关闭连接。
```javascript
conn.Close();
```
---
### 文件系统 API (fs)
所有文件操作都在沙箱中执行,有路径和大小限制。
#### `fs.readFile(path)`
读取文件内容。
```javascript
var result = fs.readFile("/path/to/file.txt");
if (result.error) {
log("Error: " + result.error);
} else {
log("Content: " + result.data);
}
```
#### `fs.writeFile(path, content)`
写入文件内容。
```javascript
var result = fs.writeFile("/path/to/file.txt", "Hello");
if (result.ok) {
log("File written");
} else {
log("Error: " + result.error);
}
```
#### `fs.readDir(path)`
读取目录内容。
```javascript
var result = fs.readDir("/path/to/dir");
if (!result.error) {
for (var i = 0; i < result.entries.length; i++) {
var entry = result.entries[i];
log(entry.name + " - " + (entry.isDir ? "DIR" : entry.size + " bytes"));
}
}
```
#### `fs.stat(path)`
获取文件信息。
```javascript
var result = fs.stat("/path/to/file");
if (!result.error) {
log("Name: " + result.name);
log("Size: " + result.size);
log("IsDir: " + result.isDir);
log("ModTime: " + result.modTime);
}
```
#### `fs.exists(path)`
检查文件是否存在。
```javascript
var result = fs.exists("/path/to/file");
if (result.exists) {
log("File exists");
}
```
#### `fs.mkdir(path)`
创建目录。
```javascript
var result = fs.mkdir("/path/to/new/dir");
if (result.ok) {
log("Directory created");
}
```
#### `fs.remove(path)`
删除文件或目录。
```javascript
var result = fs.remove("/path/to/file");
if (result.ok) {
log("Removed");
}
```
---
### HTTP API (http)
用于构建简单的 HTTP 服务。
#### `http.serve(conn, handler)`
处理 HTTP 请求。
```javascript
function handleConn(conn) {
http.serve(conn, function(req) {
return {
status: 200,
contentType: "application/json",
body: http.json({ message: "Hello", path: req.path })
};
});
}
```
**请求对象 (req):**
| 字段 | 类型 | 说明 |
|------|------|------|
| `method` | string | HTTP 方法 (GET, POST, etc.) |
| `path` | string | 请求路径 |
| `body` | string | 请求体 |
**响应对象:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `status` | number | HTTP 状态码 (默认 200) |
| `contentType` | string | Content-Type (默认 application/json) |
| `body` | string | 响应体 |
#### `http.json(data)`
将对象序列化为 JSON 字符串。
```javascript
var jsonStr = http.json({ name: "test", value: 123 });
// 返回: '{"name":"test","value":123}'
```
#### `http.sendFile(conn, filePath)`
发送文件作为 HTTP 响应。
```javascript
function handleConn(conn) {
http.sendFile(conn, "/path/to/index.html");
}
```
---
### 增强 API (Enhanced APIs)
GoTunnel v2.0+ 提供了更多强大的 API 能力。
#### `logger` (日志)
推荐使用结构化日志替代简单的 `log()`
- `logger.info(msg)`
- `logger.warn(msg)`
- `logger.error(msg)`
```javascript
logger.info("Server started");
logger.error("Connection failed");
```
#### `config` (配置)
增强的配置获取方式。
- `config.get(key)`: 获取配置值
- `config.getAll()`: 获取所有配置
```javascript
var all = config.getAll();
var port = config.get("port");
```
#### `storage` (持久化存储)
简单的 Key-Value 存储,数据保存在客户端本地。
- `storage.get(key, default)`
- `storage.set(key, value)`
- `storage.delete(key)`
- `storage.keys()`
```javascript
storage.set("last_run", Date.now());
var last = storage.get("last_run", 0);
```
#### `event` (事件总线)
插件内部或插件间的事件通信。
- `event.on(name, callback)`
- `event.emit(name, data)`
- `event.off(name)`
```javascript
event.on("user_login", function(user) {
logger.info("User logged in: " + user);
});
event.emit("user_login", "admin");
```
#### `request` (HTTP 请求)
发起外部 HTTP 请求。
- `request.get(url)`
- `request.post(url, contentType, body)`
```javascript
var res = request.get("https://api.ipify.org");
logger.info("My IP: " + res.body);
```
#### `notify` (通知)
发送系统通知。
- `notify.send(title, message)`
```javascript
notify.send("Download Complete", "File saved to disk");
```
---
## 示例插件
### Echo 服务
```javascript
function metadata() {
return {
name: "echo",
version: "1.0.0",
type: "app",
description: "Echo back received data"
};
}
function handleConn(conn) {
while (true) {
var data = conn.Read(4096);
if (!data || data.length === 0) {
break;
}
conn.Write(data);
}
conn.Close();
}
```
### HTTP 文件服务器
```javascript
function metadata() {
return {
name: "file-server",
version: "1.0.0",
type: "app",
description: "Simple HTTP file server"
};
}
var rootDir = "";
function start() {
rootDir = config("root") || "/tmp";
log("Serving files from: " + rootDir);
}
function handleConn(conn) {
http.serve(conn, function(req) {
if (req.method === "GET") {
var filePath = rootDir + req.path;
if (req.path === "/") {
filePath = rootDir + "/index.html";
}
var stat = fs.stat(filePath);
if (stat.error) {
return { status: 404, body: "Not Found" };
}
if (stat.isDir) {
return listDirectory(filePath);
}
var file = fs.readFile(filePath);
if (file.error) {
return { status: 500, body: file.error };
}
return {
status: 200,
contentType: "text/html",
body: file.data
};
}
return { status: 405, body: "Method Not Allowed" };
});
}
function listDirectory(path) {
var result = fs.readDir(path);
if (result.error) {
return { status: 500, body: result.error };
}
var html = "<html><body><h1>Directory Listing</h1><ul>";
for (var i = 0; i < result.entries.length; i++) {
var e = result.entries[i];
html += "<li><a href='" + e.name + "'>" + e.name + "</a></li>";
}
html += "</ul></body></html>";
return { status: 200, contentType: "text/html", body: html };
}
```
### JSON API 服务
```javascript
function metadata() {
return {
name: "api-server",
version: "1.0.0",
type: "app",
description: "JSON API server"
};
}
var counter = 0;
function handleConn(conn) {
http.serve(conn, function(req) {
if (req.path === "/api/status") {
return {
status: 200,
body: http.json({
status: "ok",
counter: counter++,
timestamp: Date.now()
})
};
}
if (req.path === "/api/echo" && req.method === "POST") {
return {
status: 200,
body: http.json({
received: req.body
})
};
}
return {
status: 404,
body: http.json({ error: "Not Found" })
};
});
}
```
---
## 插件签名
为了安全JS 插件需要官方签名才能运行。
### 签名格式
签名文件 (`.sig`) 包含 Base64 编码的签名数据:
```json
{
"payload": {
"name": "plugin-name",
"version": "1.0.0",
"checksum": "sha256-hash",
"key_id": "official-v1"
},
"signature": "base64-signature"
}
```
### 获取签名
1. 提交插件到官方仓库
2. 通过审核后获得签名
3.`.js``.sig` 文件一起分发
---
## 发布到商店
### 商店 JSON 格式
插件商店使用 `store.json` 文件索引所有插件:
```json
[
{
"name": "echo",
"version": "1.0.0",
"type": "app",
"description": "Echo service plugin",
"author": "GoTunnel",
"icon": "https://example.com/icon.png",
"download_url": "https://example.com/plugins/echo.js"
}
]
```
### 提交流程
1. Fork 官方插件仓库
2. 添加插件文件到 `plugins/` 目录
3. 更新 `store.json`
4. 提交 Pull Request
5. 等待审核和签名
---
## 沙箱限制
为了安全JS 插件运行在沙箱环境中:
| 限制项 | 默认值 |
|--------|--------|
| 最大读取文件大小 | 10 MB |
| 最大写入文件大小 | 10 MB |
| 允许读取路径 | 插件数据目录 |
| 允许写入路径 | 插件数据目录 |
---
## 调试技巧
### 日志输出
使用 `log()` 函数输出调试信息:
```javascript
log("Debug: variable = " + JSON.stringify(variable));
```
### 错误处理
始终检查 API 返回的错误:
```javascript
var result = fs.readFile(path);
if (result.error) {
log("Error reading file: " + result.error);
return;
}
```
### 配置测试
在 Web 控制台的插件管理页面安装并配置插件,或通过 API 安装:
```bash
# 安装 JS 插件到客户端
POST /api/client/{id}/plugin/js/install
Content-Type: application/json
{
"plugin_name": "my-plugin",
"source": "function metadata() {...}",
"rule_name": "my-rule",
"remote_port": 8080,
"config": {"debug": "true"},
"auto_start": true
}
```
---
## 常见问题
**Q: 插件无法加载?**
A: 检查签名文件是否存在且有效。
**Q: 文件操作失败?**
A: 确认路径在沙箱允许范围内。
**Q: 如何获取客户端 IP**
A: 目前 API 不支持,计划在后续版本添加。
---
## 更新日志
### v1.0.0
- 初始版本
- 支持基础 API: log, config
- 支持连接 API: Read, Write, Close
- 支持文件系统 API: fs.*
- 支持 HTTP API: http.*