mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-20 00:47:57 +00:00
webui4frpc: 独立可用的可视化 frpc 控制器 (M0)
- 零 frp 源码依赖,单二进制 (Go + Vue3 + VueFlow + Element Plus) - 画布多对多连线,渲染 tcp/udp/http/https frpc 配置 - worker 进程管理:自愈、日志轮转、崩溃退避重启 - frpc 一键安装 (GitHub Releases) + 手动指定路径 - 三页 UI:状态(默认)/连接配置/设置 - 状态页实时节点/转发状态,节点可增删改启停 - 画布冲突检查:端口/域名冲突标红 + 弹窗拦截保存 - backend 单测覆盖 store/render/process/httpapi/install - plan.md + FRPC_FEATURES_AUDIT.md 文档
This commit is contained in:
104
internal/render/render.go
Normal file
104
internal/render/render.go
Normal file
@ -0,0 +1,104 @@
|
||||
// Package render generates frpc worker configuration (JSON) from canvas data.
|
||||
package render
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"webui4frpc/internal/store"
|
||||
)
|
||||
|
||||
// Proxy is a single proxy to render inside a worker config. It carries enough
|
||||
// info to build any supported frpc proxy type.
|
||||
type Proxy struct {
|
||||
// Name is the display name (the local service name).
|
||||
Name string
|
||||
// Type is the proxy type: tcp | udp | http | https.
|
||||
Type string
|
||||
// LocalIP and LocalPort are the backend address.
|
||||
LocalIP string
|
||||
LocalPort int
|
||||
// RemotePort is the exposed port on the frps server (tcp/udp only).
|
||||
RemotePort int
|
||||
// SubDomain is used by http/https proxies when set.
|
||||
SubDomain string
|
||||
// CustomDomains is used by http/https proxies when set.
|
||||
CustomDomains []string
|
||||
}
|
||||
|
||||
// frpcAuth mirrors frpc's auth section.
|
||||
type frpcAuth struct {
|
||||
Method string `json:"method"`
|
||||
Token string `json:"token,omitempty"`
|
||||
}
|
||||
|
||||
// frpcTransport is the transport section used by frpc.
|
||||
type frpcTransport struct {
|
||||
Protocol string `json:"protocol,omitempty"`
|
||||
}
|
||||
|
||||
// frpcProxy is the serialized proxy object. Different types use different
|
||||
// fields; empty ones are omitted so the JSON is accepted by frpc.
|
||||
type frpcProxy struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
LocalIP string `json:"localIP,omitempty"`
|
||||
LocalPort int `json:"localPort"`
|
||||
RemotePort int `json:"remotePort,omitempty"`
|
||||
CustomDomains []string `json:"customDomains,omitempty"`
|
||||
SubDomain string `json:"subdomain,omitempty"`
|
||||
}
|
||||
|
||||
// Config is a complete frpc configuration document.
|
||||
type Config struct {
|
||||
ServerAddr string `json:"serverAddr"`
|
||||
ServerPort int `json:"serverPort"`
|
||||
Auth frpcAuth `json:"auth"`
|
||||
Transport frpcTransport `json:"transport,omitempty"`
|
||||
LoginFailExit bool `json:"loginFailExit"`
|
||||
Proxies []frpcProxy `json:"proxies"`
|
||||
}
|
||||
|
||||
// Render builds the worker config JSON for a remote server from its setting
|
||||
// plus the list of proxies to forward.
|
||||
func Render(remote store.Remote, proxies []Proxy) ([]byte, error) {
|
||||
cfg := Config{
|
||||
ServerAddr: remote.IP,
|
||||
ServerPort: remote.Port,
|
||||
Auth: frpcAuth{Method: "token", Token: remote.Token},
|
||||
LoginFailExit: false,
|
||||
Proxies: make([]frpcProxy, 0, len(proxies)),
|
||||
}
|
||||
|
||||
nameCounts := make(map[string]int)
|
||||
for _, p := range proxies {
|
||||
frpcP := frpcProxy{
|
||||
Name: p.Name,
|
||||
Type: p.Type,
|
||||
LocalIP: p.LocalIP,
|
||||
LocalPort: p.LocalPort,
|
||||
SubDomain: p.SubDomain,
|
||||
}
|
||||
// http/https proxies use customDomains instead of remotePort. The
|
||||
// default domain is <name>.local; frps matches it by Host header.
|
||||
if p.Type == "http" || p.Type == "https" {
|
||||
frpcP.RemotePort = 0
|
||||
if frpcP.SubDomain == "" && len(frpcP.CustomDomains) == 0 {
|
||||
frpcP.CustomDomains = []string{p.Name + ".local"}
|
||||
}
|
||||
} else {
|
||||
frpcP.RemotePort = p.RemotePort
|
||||
}
|
||||
if len(p.CustomDomains) > 0 {
|
||||
frpcP.CustomDomains = p.CustomDomains
|
||||
}
|
||||
// Duplicate service name -> suffix with the remote port so frpc accepts
|
||||
// multiple proxies for the same local service.
|
||||
nameCounts[frpcP.Name]++
|
||||
if nameCounts[frpcP.Name] > 1 {
|
||||
frpcP.Name = fmt.Sprintf("%s-%d", frpcP.Name, frpcP.RemotePort)
|
||||
}
|
||||
cfg.Proxies = append(cfg.Proxies, frpcP)
|
||||
}
|
||||
return json.MarshalIndent(cfg, "", " ")
|
||||
}
|
||||
Reference in New Issue
Block a user