后端已跑通

This commit is contained in:
2026-07-23 13:02:51 +08:00
parent 17b9af9184
commit b692e88bfc
2 changed files with 119 additions and 43 deletions
Binary file not shown.
+107 -31
View File
@@ -2,13 +2,51 @@ package main
import ( import (
"bytes" "bytes"
"embed"
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"runtime"
"strings" "strings"
"syscall"
"text/template" "text/template"
) )
//go:embed bin/*
var embeddedFrpc embed.FS
// 获取当前平台对应的 frpc 路径(从 embed 解压到临时目录)
func getFrpcPath() (string, error) {
var fileName string
if runtime.GOOS == "windows" {
fileName = "frpc.exe"
} else {
fileName = "frpc"
}
// 从 embed 读取
data, err := embeddedFrpc.ReadFile("bin/" + fileName)
if err != nil {
// 如果 embed 中没有对应文件,降级到系统 PATH 查找
path, err := exec.LookPath(fileName)
if err != nil {
return "", fmt.Errorf("未找到 frpc 可执行文件(embed 和 PATH 均无): %w", err)
}
return path, nil
}
// 写入临时目录
tmpPath := os.TempDir() + "/frpc"
if runtime.GOOS == "windows" {
tmpPath += ".exe"
}
err = os.WriteFile(tmpPath, data, 0755)
if err != nil {
return "", fmt.Errorf("写入临时 frpc 文件失败: %w", err)
}
return tmpPath, nil
}
// frpc.toml 模板 - 完全复制你的配置文件结构 // frpc.toml 模板 - 完全复制你的配置文件结构
const frpcTemplate = `serverAddr = "{{.ServerAddr}}" const frpcTemplate = `serverAddr = "{{.ServerAddr}}"
serverPort = {{.ServerPort}} serverPort = {{.ServerPort}}
@@ -37,15 +75,13 @@ localPort = {{.LocalPort}}
remotePort = {{.RemotePort}} remotePort = {{.RemotePort}}
{{end}}` {{end}}`
// 生成 frpc.toml 文件 // GenerateFrpcConfig 生成 frpc.toml 配置文件
func GenerateFrpcConfig() error { func GenerateFrpcConfig() error {
// 1. 读取全局配置
cfg, err := GetGlobalConfig() cfg, err := GetGlobalConfig()
if err != nil { if err != nil {
return fmt.Errorf("读取全局配置失败: %w", err) return fmt.Errorf("读取全局配置失败: %w", err)
} }
// 2. 读取所有启用的隧道
proxies, err := GetProxies() proxies, err := GetProxies()
if err != nil { if err != nil {
return fmt.Errorf("读取隧道列表失败: %w", err) return fmt.Errorf("读取隧道列表失败: %w", err)
@@ -58,7 +94,6 @@ func GenerateFrpcConfig() error {
} }
} }
// 3. 组合数据
data := struct { data := struct {
*GlobalConfig *GlobalConfig
Proxies []Proxy Proxies []Proxy
@@ -67,7 +102,6 @@ func GenerateFrpcConfig() error {
Proxies: activeProxies, Proxies: activeProxies,
} }
// 4. 渲染模板
tmpl, err := template.New("frpc").Parse(frpcTemplate) tmpl, err := template.New("frpc").Parse(frpcTemplate)
if err != nil { if err != nil {
return fmt.Errorf("解析模板失败: %w", err) return fmt.Errorf("解析模板失败: %w", err)
@@ -79,7 +113,6 @@ func GenerateFrpcConfig() error {
return fmt.Errorf("渲染模板失败: %w", err) return fmt.Errorf("渲染模板失败: %w", err)
} }
// 5. 写入文件
err = os.WriteFile("./frpc.toml", buf.Bytes(), 0644) err = os.WriteFile("./frpc.toml", buf.Bytes(), 0644)
if err != nil { if err != nil {
return fmt.Errorf("写入配置文件失败: %w", err) return fmt.Errorf("写入配置文件失败: %w", err)
@@ -88,59 +121,102 @@ func GenerateFrpcConfig() error {
return nil return nil
} }
// 热加载 frpc // StartFrpc 启动 frpc(后台运行)
func ReloadFrpc() error {
// 先检查 frpc 进程是否在运行
cmd := exec.Command("pgrep", "-f", "frpc")
output, err := cmd.Output()
if err != nil || len(output) == 0 {
// frpc 没在运行,尝试启动
return StartFrpc()
}
// 执行 reload
reloadCmd := exec.Command("frpc", "reload", "-c", "./frpc.toml")
output, err = reloadCmd.CombinedOutput()
if err != nil {
return fmt.Errorf("reload 失败: %s, %w", string(output), err)
}
return nil
}
// 启动 frpc
func StartFrpc() error { func StartFrpc() error {
// 先确保配置文件存在 frpcPath, err := getFrpcPath()
if err != nil {
return fmt.Errorf("获取 frpc 路径失败: %w", err)
}
// 检查配置文件是否存在
if _, err := os.Stat("./frpc.toml"); os.IsNotExist(err) { if _, err := os.Stat("./frpc.toml"); os.IsNotExist(err) {
if err := GenerateFrpcConfig(); err != nil { if err := GenerateFrpcConfig(); err != nil {
return fmt.Errorf("生成配置文件失败: %w", err) return fmt.Errorf("生成配置文件失败: %w", err)
} }
} }
cmd := exec.Command("frpc", "-c", "./frpc.toml") // 先检查是否已在运行
// 后台运行,不阻塞 running, _ := GetFrpcStatus()
err := cmd.Start() if running {
return nil // 已经在运行,不重复启动
}
cmd := exec.Command(frpcPath, "-c", "./frpc.toml")
// Windows 下需要隐藏窗口(可选)
if runtime.GOOS == "windows" {
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
}
err = cmd.Start()
if err != nil { if err != nil {
return fmt.Errorf("启动 frpc 失败: %w", err) return fmt.Errorf("启动 frpc 失败: %w", err)
} }
return nil return nil
} }
// ReloadFrpc 热加载 frpc 配置
func ReloadFrpc() error {
frpcPath, err := getFrpcPath()
if err != nil {
return fmt.Errorf("获取 frpc 路径失败: %w", err)
}
// 检查 frpc 是否在运行
running, err := GetFrpcStatus()
if err != nil {
return fmt.Errorf("检查 frpc 状态失败: %w", err)
}
if !running {
// 没运行就直接启动
return StartFrpc()
}
// 执行 reload
cmd := exec.Command(frpcPath, "reload", "-c", "./frpc.toml")
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("reload 失败: %s, %w", string(output), err)
}
return nil
}
// StopFrpc 停止 frpc // StopFrpc 停止 frpc
func StopFrpc() error { func StopFrpc() error {
if runtime.GOOS == "windows" {
// Windows 用 taskkill
cmd := exec.Command("taskkill", "/F", "/IM", "frpc.exe")
err := cmd.Run()
if err != nil && !strings.Contains(err.Error(), "not found") {
return fmt.Errorf("停止 frpc 失败: %w", err)
}
return nil
} else {
// Linux/Unix 用 pkill
cmd := exec.Command("pkill", "-f", "frpc") cmd := exec.Command("pkill", "-f", "frpc")
err := cmd.Run() err := cmd.Run()
if err != nil && !strings.Contains(err.Error(), "no process") { if err != nil && !strings.Contains(err.Error(), "no process") {
return fmt.Errorf("停止 frpc 失败: %w", err) return fmt.Errorf("停止 frpc 失败: %w", err)
} }
return nil return nil
}
} }
// 检查 frpc 状态 // GetFrpcStatus 检查 frpc 是否在运行
func GetFrpcStatus() (bool, error) { func GetFrpcStatus() (bool, error) {
if runtime.GOOS == "windows" {
// Windows 用 tasklist 检查
cmd := exec.Command("tasklist", "/FI", "IMAGENAME eq frpc.exe")
output, err := cmd.CombinedOutput()
if err != nil {
return false, nil
}
return strings.Contains(string(output), "frpc.exe"), nil
} else {
// Linux/Unix 用 pgrep
cmd := exec.Command("pgrep", "-f", "frpc") cmd := exec.Command("pgrep", "-f", "frpc")
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
return false, nil return false, nil
} }
return len(output) > 0, nil return len(output) > 0, nil
}
} }