diff --git a/CLAUDE.md b/CLAUDE.md index c40ebc4..7483cd9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,6 +20,12 @@ go build -o client ./cmd/client # Web UI development (in web/ directory) cd web && npm install && npm run dev # development server cd web && npm run build # production build (outputs to web/dist/) + +# Cross-platform build (Windows PowerShell) +.\scripts\build.ps1 + +# Cross-platform build (Linux/Mac) +./scripts/build.sh all ``` ## Architecture Overview @@ -32,6 +38,7 @@ GoTunnel is an intranet penetration tool (similar to frp) with **server-centric - **Binary Protocol**: `[Type(1 byte)][Length(4 bytes)][Payload(JSON)]` - see `pkg/protocol/message.go` - **TLS by Default**: Auto-generated self-signed ECDSA P-256 certificates, no manual setup required - **Embedded Web UI**: Vue.js SPA embedded in server binary via `//go:embed` +- **JS Plugin System**: Extensible plugin system using goja JavaScript runtime ### Package Structure @@ -41,34 +48,36 @@ cmd/client/ # Client entry point internal/server/ ├── tunnel/ # Core tunnel server, client session management ├── config/ # YAML configuration loading - ├── db/ # SQLite storage (ClientStore interface) + ├── db/ # SQLite storage (ClientStore, JSPluginStore interfaces) ├── app/ # Web server, SPA handler - ├── router/ # REST API endpoints - └── plugin/ # Server-side plugin manager + ├── router/ # REST API endpoints (Swagger documented) + └── plugin/ # Server-side JS plugin manager internal/client/ - ├── tunnel/ # Client tunnel logic, auto-reconnect - └── plugin/ # Client-side plugin manager and cache + └── tunnel/ # Client tunnel logic, auto-reconnect, plugin execution pkg/ ├── protocol/ # Message types and serialization ├── crypto/ # TLS certificate generation - ├── proxy/ # Legacy proxy implementations ├── relay/ # Bidirectional data relay (32KB buffers) + ├── auth/ # JWT authentication ├── utils/ # Port availability checking + ├── version/ # Version info and update checking (Gitea API) + ├── update/ # Shared update logic (download, extract tar.gz/zip) └── plugin/ # Plugin system core - ├── types.go # ProxyHandler interface, PluginMetadata + ├── types.go # Plugin interfaces ├── registry.go # Plugin registry - ├── builtin/ # Built-in plugins (socks5, http) - ├── wasm/ # WASM runtime (wazero) + ├── script/ # JS plugin runtime (goja) + ├── sign/ # Plugin signature verification └── store/ # Plugin persistence (SQLite) -web/ # Vue 3 + TypeScript frontend (Vite) +web/ # Vue 3 + TypeScript frontend (Vite + naive-ui) +scripts/ # Build scripts (build.sh, build.ps1) ``` ### Key Interfaces - `ClientStore` (internal/server/db/): Database abstraction for client rules storage -- `ServerInterface` (internal/server/router/): API handler interface -- `ProxyHandler` (pkg/plugin/): Plugin interface for proxy handlers -- `PluginStore` (pkg/plugin/store/): Plugin persistence interface +- `JSPluginStore` (internal/server/db/): JS plugin persistence +- `ServerInterface` (internal/server/router/handler/): API handler interface +- `ClientPlugin` (pkg/plugin/): Plugin interface for client-side plugins ### Proxy Types @@ -77,9 +86,11 @@ web/ # Vue 3 + TypeScript frontend (Vite) 2. **UDP**: UDP port forwarding 3. **HTTP**: HTTP proxy through client network 4. **HTTPS**: HTTPS proxy through client network +5. **SOCKS5**: SOCKS5 proxy through client network -**插件类型** (通过 plugin 系统提供): -- **SOCKS5**: Full SOCKS5 protocol (official plugin) +**JS 插件类型** (通过 goja 运行时): +- Custom application plugins (file-server, api-server, etc.) +- Runs on client side with sandbox restrictions ### Data Flow @@ -87,32 +98,68 @@ External User → Server Port → Yamux Stream → Client → Local Service ### Configuration -- Server: YAML config + SQLite database for client rules +- Server: YAML config + SQLite database for client rules and JS plugins - Client: Command-line flags only (server address, token, client ID) - Default ports: 7000 (tunnel), 7500 (web console) ## Plugin System -GoTunnel supports a WASM-based plugin system for extensible proxy handlers. +GoTunnel supports a JavaScript-based plugin system using the goja runtime. ### Plugin Architecture -- **内置类型**: tcp, udp, http, https 直接在 tunnel 代码中处理 -- **Official Plugin**: SOCKS5 作为官方 plugin 提供 -- **WASM Plugins**: 自定义 plugins 可通过 wazero 运行时动态加载 -- **Hybrid Distribution**: 内置 plugins 离线可用;WASM plugins 可从服务端下载 +- **内置协议**: tcp, udp, http, https, socks5 直接在 tunnel 代码中处理 +- **JS Plugins**: 自定义应用插件通过 goja 运行时在客户端执行 +- **Plugin Store**: 从官方商店浏览和安装插件 +- **Signature Verification**: 插件需要签名验证才能运行 -### ProxyHandler Interface +### JS Plugin Lifecycle -```go -type ProxyHandler interface { - Metadata() PluginMetadata - Init(config map[string]string) error - HandleConn(conn net.Conn, dialer Dialer) error - Close() error +```javascript +function metadata() { + return { + name: "plugin-name", + version: "1.0.0", + type: "app", + description: "Plugin description", + author: "Author" + }; } + +function start() { /* called on plugin start */ } +function handleConn(conn) { /* handle each connection */ } +function stop() { /* called on plugin stop */ } ``` -### Creating a Built-in Plugin +### Plugin APIs -See `pkg/plugin/builtin/socks5.go` as reference implementation. +- **Basic**: `log()`, `config()` +- **Connection**: `conn.Read()`, `conn.Write()`, `conn.Close()` +- **File System**: `fs.readFile()`, `fs.writeFile()`, `fs.readDir()`, `fs.stat()`, etc. +- **HTTP**: `http.serve()`, `http.json()`, `http.sendFile()` + +See `PLUGINS.md` for detailed plugin development documentation. + +## API Documentation + +The server provides Swagger-documented REST APIs at `/api/`. + +### Key Endpoints + +- `POST /api/auth/login` - JWT authentication +- `GET /api/clients` - List all clients +- `GET /api/client/{id}` - Get client details +- `PUT /api/client/{id}` - Update client config +- `POST /api/client/{id}/push` - Push config to online client +- `POST /api/client/{id}/plugin/{name}/{action}` - Plugin actions (start/stop/restart/delete) +- `GET /api/plugins` - List registered plugins +- `GET /api/update/check/server` - Check server updates +- `POST /api/update/apply/server` - Apply server update + +## Update System + +Both server and client support self-update from Gitea releases. + +- Release assets are compressed archives (`.tar.gz` for Linux/Mac, `.zip` for Windows) +- The `pkg/update/` package handles download, extraction, and binary replacement +- Updates can be triggered from the Web UI at `/update` page diff --git a/PLUGINS.md b/PLUGINS.md index 9745520..b8327b6 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -1,6 +1,6 @@ # GoTunnel 插件开发指南 -本文档介绍如何为 GoTunnel 开发 JS 插件。 +本文档介绍如何为 GoTunnel 开发 JS 插件。JS 插件基于 [goja](https://github.com/dop251/goja) 运行时,运行在客户端上。 ## 目录 @@ -66,8 +66,7 @@ function metadata() { return { name: "plugin-name", // 插件名称 version: "1.0.0", // 版本号 - type: "app", // 类型: "app" 或 "proxy" - run_at: "client", // 运行位置: "client" 或 "server" + type: "app", // 类型: "app" (应用插件) description: "描述", // 插件描述 author: "作者" // 作者名称 }; @@ -519,16 +518,20 @@ if (result.error) { ### 配置测试 -在服务端配置中测试插件: +在 Web 控制台的插件管理页面安装并配置插件,或通过 API 安装: -```yaml -js_plugins: - - name: my-plugin - path: /path/to/my-plugin.js - sig_path: /path/to/my-plugin.js.sig - config: - debug: "true" - port: "8080" +```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 +} ``` --- diff --git a/README.md b/README.md index 030d669..7911b30 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ GoTunnel 是一个类似 frp 的内网穿透解决方案,核心特点是**服 - **多客户端支持** - 支持多个客户端同时连接,每个客户端独立的映射规则 - **端口冲突检测** - 自动检测系统端口占用和客户端间端口冲突 - **SOCKS5/HTTP 代理** - 支持通过客户端网络访问任意网站 +- **自动更新** - 服务端和客户端支持从 Web 界面一键更新 ### 安全性 @@ -192,11 +193,7 @@ web: | `http` | HTTP 代理 | 通过客户端网络访问 HTTP/HTTPS | | `https` | HTTPS 代理 | 同 HTTP,支持 CONNECT 方法 | -### 插件类型 - -| 类型 | 说明 | 示例用途 | -|------|------|----------| -| `socks5` | SOCKS5 代理(官方插件) | 通过客户端网络访问任意地址 | +| `socks5` | SOCKS5 代理 | 通过客户端网络访问任意地址 | **规则配置示例(通过 Web API):** @@ -226,9 +223,11 @@ GoTunnel/ │ │ ├── config/ # 配置管理 │ │ ├── db/ # 数据库存储 │ │ ├── app/ # Web 服务 -│ │ └── router/ # API 路由 +│ │ ├── router/ # API 路由 +│ │ └── plugin/ # 服务端插件管理 │ └── client/ -│ └── tunnel/ # 客户端隧道 +│ ├── tunnel/ # 客户端隧道 +│ └── plugin/ # 客户端插件管理和缓存 ├── pkg/ │ ├── protocol/ # 通信协议 │ ├── crypto/ # TLS 加密 @@ -236,32 +235,31 @@ GoTunnel/ │ ├── relay/ # 数据转发 │ ├── auth/ # JWT 认证 │ ├── utils/ # 工具函数 -│ └── plugin/ # 插件系统核心 -│ ├── builtin/ # 内置插件 (socks5) -│ ├── wasm/ # WASM 运行时 (wazero) +│ ├── version/ # 版本信息和更新检查 +│ ├── update/ # 共享更新逻辑 (下载、解压) +│ └── plugin/ # JS 插件系统核心 (goja) │ └── store/ # 插件持久化 (SQLite) ├── web/ # Vue 3 + naive-ui 前端 ├── scripts/ # 构建脚本 -│ └── build.sh # 跨平台构建脚本 +│ ├── build.sh # Linux/macOS 构建脚本 +│ └── build.ps1 # Windows 构建脚本 └── go.mod ``` ## 插件系统 -GoTunnel 支持灵活的插件系统,可扩展代理协议和应用功能。 +GoTunnel 支持灵活的 JS 插件系统,可扩展代理协议和应用功能。 ### 插件类型 | 类型 | 说明 | 运行位置 | |------|------|----------| -| `proxy` | 代理协议插件 (如 SOCKS5) | 服务端 | -| `app` | 应用插件 (如 HTTP 文件服务) | 客户端 | +| `app` | 应用插件 (如 HTTP 文件服务、Echo 服务) | 客户端 | ### 插件来源 -- **内置插件**: 编译在二进制中,离线可用 - **JS 插件**: 基于 goja 运行时,支持动态加载和热更新 -- **扩展商店**: 从官方商店浏览和安装插件 +- **插件商店**: 从服务端管理的插件商店浏览和安装 ### 开发 JS 插件 @@ -423,10 +421,16 @@ A: 服务端会自动检测端口冲突,请检查日志并更换端口。 A: 如果客户端未指定 `-id` 参数,服务端会自动生成 16 位随机 ID。 +**Q: 如何更新服务端/客户端?** + +A: 在 Web 控制台的"更新"页面,可以检查并应用更新。服务端/客户端会自动从 Release 下载压缩包、解压并重启。 + ## 构建 使用构建脚本可以一键构建前后端: +**Linux/macOS:** + ```bash # 构建当前平台 ./scripts/build.sh current @@ -444,6 +448,25 @@ A: 如果客户端未指定 `-id` 参数,服务端会自动生成 16 位随机 VERSION=1.0.0 ./scripts/build.sh all ``` +**Windows (PowerShell):** + +```powershell +# 构建当前平台 +.\scripts\build.ps1 current + +# 构建所有平台 +.\scripts\build.ps1 all + +# 仅构建 Web UI +.\scripts\build.ps1 web + +# 清理构建产物 +.\scripts\build.ps1 clean + +# 指定版本号 +$env:VERSION="1.0.0"; .\scripts\build.ps1 all +``` + 构建产物输出到 `build/_/` 目录。 ## 架构时序图 diff --git a/plan.md b/plan.md deleted file mode 100644 index fb69f78..0000000 --- a/plan.md +++ /dev/null @@ -1,319 +0,0 @@ -# GoTunnel 重构计划 - -## 概述 - -本次重构包含三个主要目标: -1. 移除 WASM 支持,只保留 JS 插件系统 -2. 优化 Web 界面,支持协议动态配置和 JS 插件管理 -3. 实现动态重启客户端和插件功能 - ---- - -## 第一部分:移除 WASM,简化插件系统 - -### 1.1 需要删除的文件/目录 -- `pkg/plugin/wasm/` - WASM 运行时目录(如果存在) - -### 1.2 需要修改的文件 - -#### 数据库层 (`internal/server/db/`) -- **interface.go**: 移除 `PluginStore` 接口中的 `GetPluginWASM` 方法 -- **sqlite.go**: - - 移除 `plugins` 表(WASM 插件表) - - 移除相关的 CRUD 方法 - - 保留 `js_plugins` 表 - -#### 插件类型 (`pkg/plugin/types.go`) -- 移除 `PluginSource` 中的 `"wasm"` 选项,只保留 `"builtin"` 和 `"script"` - -#### 依赖清理 -- 检查 `go.mod` 是否有 wazero 依赖,如有则移除 - ---- - -## 第二部分:优化 Web 界面 - -### 2.1 协议动态配置 - -#### 后端修改 - -##### A. 扩展 ConfigField 类型 (`pkg/plugin/types.go`) -```go -type ConfigField struct { - Key string `json:"key"` - Label string `json:"label"` - Type string `json:"type"` // string, number, bool, select, password - Default string `json:"default,omitempty"` - Required bool `json:"required,omitempty"` - Options []string `json:"options,omitempty"` - Description string `json:"description,omitempty"` -} - -type RuleSchema struct { - NeedsLocalAddr bool `json:"needs_local_addr"` - ExtraFields []ConfigField `json:"extra_fields"` -} -``` - -##### B. 内置协议配置模式 -为 SOCKS5 和 HTTP 代理添加认证配置字段: - -```go -// SOCKS5 配置模式 -var Socks5Schema = RuleSchema{ - NeedsLocalAddr: false, - ExtraFields: []ConfigField{ - {Key: "auth_enabled", Label: "启用认证", Type: "bool", Default: "false"}, - {Key: "username", Label: "用户名", Type: "string"}, - {Key: "password", Label: "密码", Type: "password"}, - }, -} - -// HTTP 代理配置模式 -var HTTPProxySchema = RuleSchema{ - NeedsLocalAddr: false, - ExtraFields: []ConfigField{ - {Key: "auth_enabled", Label: "启用认证", Type: "bool", Default: "false"}, - {Key: "username", Label: "用户名", Type: "string"}, - {Key: "password", Label: "密码", Type: "password"}, - }, -} -``` - -##### C. API 端点 (`internal/server/router/api.go`) -- **GET `/api/rule-schemas`**: 返回所有协议类型的配置模式 - - 内置类型 (tcp, udp, http, https) 的模式 - - 已注册插件的模式(从插件 Metadata 获取) - -#### 前端修改 (`web/src/views/ClientView.vue`) -- 页面加载时获取 rule-schemas -- 根据选择的协议类型动态渲染额外配置字段 -- 支持的字段类型:string, number, bool, select, password - -### 2.2 JS 插件管理界面优化 - -#### 后端修改 - -##### A. 扩展 JSPlugin 结构 (`internal/server/db/interface.go`) -```go -type JSPlugin struct { - Name string `json:"name"` - Source string `json:"source"` // JS 源码 - Signature string `json:"signature"` // 官方签名 - Description string `json:"description"` - Author string `json:"author"` - Version string `json:"version"` - AutoPush []string `json:"auto_push"` // 自动推送客户端列表 - Config map[string]string `json:"config"` // 插件运行时配置 - ConfigSchema []ConfigField `json:"config_schema"` // 配置字段定义 - AutoStart bool `json:"auto_start"` - Enabled bool `json:"enabled"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` -} -``` - -##### B. 新增/修改 API 端点 -- **GET `/api/js-plugins`**: 获取所有 JS 插件列表(包含配置模式) -- **GET `/api/js-plugin/{name}`**: 获取单个插件详情 -- **PUT `/api/js-plugin/{name}/config`**: 更新插件运行时配置 -- **POST `/api/js-plugin/{name}/push/{clientId}`**: 推送插件到指定客户端 -- **POST `/api/js-plugin/{name}/reload`**: 重新加载插件(重新解析源码) - -#### 前端修改 (`web/src/views/PluginsView.vue`) - -##### A. 重新设计 JS 插件 Tab -``` -┌─────────────────────────────────────────────────────────────┐ -│ 已安装的 JS 插件 │ -├─────────────────────────────────────────────────────────────┤ -│ ┌─────────────────────────────────────────────────────────┐ │ -│ │ 📦 socks5-auth v1.0.0 │ │ -│ │ 带认证的 SOCKS5 代理 │ │ -│ │ 作者: official │ │ -│ │ ────────────────────────────────────────────────────── │ │ -│ │ 状态: ✅ 启用 自动启动: ✅ │ │ -│ │ 推送目标: 所有客户端 │ │ -│ │ ────────────────────────────────────────────────────── │ │ -│ │ [⚙️ 配置] [🔄 重载] [📤 推送] [🗑️ 删除] │ │ -│ └─────────────────────────────────────────────────────────┘ │ -└─────────────────────────────────────────────────────────────┘ -``` - -##### B. 插件配置模态框 -- 动态渲染 ConfigSchema 定义的字段 -- 支持 string, number, bool, select, password 类型 -- 保存后可选择是否同步到已连接的客户端 - -##### C. 推送配置 -- 选择目标客户端(支持多选) -- 选择是否自动启动 -- 显示推送结果 - ---- - -## 第三部分:动态重启功能 - -### 3.1 协议消息定义 (`pkg/protocol/message.go`) - -```go -// 已有消息类型 -const ( - MsgTypeClientPluginStart = 40 // 启动插件 - MsgTypeClientPluginStop = 41 // 停止插件 (需实现) - MsgTypeClientPluginStatus = 42 // 插件状态 - MsgTypeClientPluginConn = 43 // 插件连接 - - // 新增消息类型 - MsgTypeClientRestart = 44 // 客户端重启 - MsgTypePluginConfigUpdate = 45 // 插件配置更新 -) - -// 新增请求/响应结构 -type ClientPluginStopRequest struct { - PluginName string `json:"plugin_name"` - RuleName string `json:"rule_name"` -} - -type ClientRestartRequest struct { - Reason string `json:"reason,omitempty"` -} - -type PluginConfigUpdateRequest struct { - PluginName string `json:"plugin_name"` - RuleName string `json:"rule_name"` - Config map[string]string `json:"config"` -} -``` - -### 3.2 客户端实现 (`internal/client/tunnel/client.go`) - -#### A. 实现插件停止处理 -```go -func (c *Client) handleClientPluginStop(stream net.Conn, msg *protocol.Message) { - defer stream.Close() - - var req protocol.ClientPluginStopRequest - if err := msg.ParsePayload(&req); err != nil { - return - } - - key := req.PluginName + ":" + req.RuleName - - c.pluginMu.Lock() - if handler, ok := c.runningPlugins[key]; ok { - handler.Stop() - delete(c.runningPlugins, key) - } - c.pluginMu.Unlock() - - // 发送确认 - resp := protocol.ClientPluginStatusResponse{ - PluginName: req.PluginName, - RuleName: req.RuleName, - Running: false, - } - respMsg, _ := protocol.NewMessage(protocol.MsgTypeClientPluginStatus, resp) - protocol.WriteMessage(stream, respMsg) -} -``` - -#### B. 实现配置热更新 -```go -func (c *Client) handlePluginConfigUpdate(stream net.Conn, msg *protocol.Message) { - // 更新运行中插件的配置(如果插件支持热更新) -} -``` - -#### C. 实现客户端优雅重启 -```go -func (c *Client) handleClientRestart(stream net.Conn, msg *protocol.Message) { - // 1. 停止所有运行中的插件 - // 2. 关闭当前会话 - // 3. 触发重连(Run() 循环会自动处理) -} -``` - -### 3.3 服务端实现 (`internal/server/tunnel/server.go`) - -```go -// 停止客户端插件 -func (s *Server) StopClientPlugin(clientID, pluginName, ruleName string) error - -// 重启客户端插件 -func (s *Server) RestartClientPlugin(clientID, pluginName, ruleName string) error - -// 更新插件配置 -func (s *Server) UpdateClientPluginConfig(clientID, pluginName, ruleName string, config map[string]string) error - -// 重启整个客户端 -func (s *Server) RestartClient(clientID string) error -``` - -### 3.4 REST API (`internal/server/router/api.go`) - -```go -// 客户端控制 -POST /api/client/{id}/restart // 重启整个客户端 - -// 插件控制 -POST /api/client/{id}/plugin/{name}/stop // 停止插件 -POST /api/client/{id}/plugin/{name}/restart // 重启插件 -PUT /api/client/{id}/plugin/{name}/config // 更新配置并可选重启 -``` - -### 3.5 前端界面 (`web/src/views/ClientView.vue`) - -在客户端详情页添加控制按钮: -- **客户端级别**: "重启客户端" 按钮 -- **插件级别**: 每个运行中的插件显示 "停止"、"重启"、"配置" 按钮 - ---- - -## 实施顺序 - -### 阶段 1: 清理 WASM 代码 -1. 删除 WASM 相关文件和代码 -2. 更新数据库 schema -3. 清理依赖 - -### 阶段 2: 协议动态配置 -1. 定义 ConfigField 和 RuleSchema 类型 -2. 实现内置协议的配置模式 -3. 添加 `/api/rule-schemas` 端点 -4. 更新前端规则编辑界面 - -### 阶段 3: JS 插件管理优化 -1. 扩展 JSPlugin 结构和数据库 -2. 实现插件配置 API -3. 重新设计前端插件管理界面 - -### 阶段 4: 动态重启功能 -1. 实现客户端 pluginStop 处理 -2. 实现服务端重启方法 -3. 添加 REST API 端点 -4. 更新前端添加控制按钮 - -### 阶段 5: 测试和文档 -1. 端到端测试 -2. 更新 CLAUDE.md - ---- - -## 文件变更清单 - -### 后端 Go 文件 -- `pkg/plugin/types.go` - 添加 ConfigField, RuleSchema -- `pkg/plugin/schema.go` (新建) - 内置协议配置模式 -- `pkg/protocol/message.go` - 新增消息类型 -- `internal/server/db/interface.go` - 更新接口 -- `internal/server/db/sqlite.go` - 更新数据库操作 -- `internal/server/router/api.go` - 新增 API 端点 -- `internal/server/tunnel/server.go` - 重启控制方法 -- `internal/client/tunnel/client.go` - 插件停止/重启处理 - -### 前端文件 -- `web/src/types/index.ts` - 类型定义 -- `web/src/api/index.ts` - API 调用 -- `web/src/views/ClientView.vue` - 规则配置和重启控制 -- `web/src/views/PluginsView.vue` - 插件管理界面