核心功能设计已完成,包含frpc热重启冷重启两种设计逻辑
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -9,13 +10,24 @@ import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// SetupRouter 配置所有路由
|
||||
func SetupRouter() *gin.Engine {
|
||||
r := gin.Default()
|
||||
|
||||
// 静态文件(前端)
|
||||
r.Static("/static", "./static")
|
||||
|
||||
// API 路由
|
||||
// 健康检查
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
c.String(200, "frpc-console 后端已启动 🎉")
|
||||
})
|
||||
|
||||
// 前端入口
|
||||
r.GET("/", func(c *gin.Context) {
|
||||
c.File("./static/index.html")
|
||||
})
|
||||
|
||||
// API 路由组
|
||||
api := r.Group("/api")
|
||||
{
|
||||
// 登录(无需鉴权)
|
||||
@@ -41,14 +53,12 @@ func SetupRouter() *gin.Engine {
|
||||
auth.POST("/frpc/start", startFrpcHandler)
|
||||
auth.POST("/frpc/stop", stopFrpcHandler)
|
||||
auth.GET("/frpc/status", getFrpcStatusHandler)
|
||||
|
||||
// 🟢 导入 TOML(新增)
|
||||
auth.POST("/import/toml", importTomlHandler)
|
||||
}
|
||||
}
|
||||
|
||||
// 前端入口
|
||||
r.GET("/", func(c *gin.Context) {
|
||||
c.File("./static/index.html")
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -69,8 +79,7 @@ func loginHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password))
|
||||
if err != nil {
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)); err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"code": 1, "msg": "用户名或密码错误"})
|
||||
return
|
||||
}
|
||||
@@ -81,7 +90,11 @@ func loginHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "登录成功", "data": gin.H{"token": token}})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "登录成功",
|
||||
"data": gin.H{"token": token},
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 全局配置 ==========
|
||||
@@ -101,29 +114,23 @@ func updateConfigHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("收到配置更新: %+v", cfg)
|
||||
// 强制 tcpMux 为 true
|
||||
cfg.TcpMux = true
|
||||
|
||||
if err := UpdateGlobalConfig(&cfg); err != nil {
|
||||
log.Printf("❌ 更新数据库失败: %v", err) // 新增日志
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "更新配置失败"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "更新配置失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
log.Printf("✅ 数据库更新成功")
|
||||
|
||||
// 自动生成配置文件并热加载
|
||||
if err := GenerateFrpcConfig(); err != nil {
|
||||
log.Printf("❌ 生成配置文件失败: %v", err) // 新增日志
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "生成配置文件失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
log.Printf("✅ 配置文件生成成功")
|
||||
|
||||
if err := ReloadFrpc(); err != nil {
|
||||
log.Printf("❌ 热加载失败: %v", err) // 新增日志
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "热加载失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
log.Printf("✅ 热加载成功")
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "配置更新成功"})
|
||||
}
|
||||
@@ -139,7 +146,11 @@ func getProxiesHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func getProxyHandler(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "无效的ID"})
|
||||
return
|
||||
}
|
||||
p, err := GetProxy(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"code": 1, "msg": "隧道不存在"})
|
||||
@@ -154,14 +165,13 @@ func createProxyHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "请求参数错误"})
|
||||
return
|
||||
}
|
||||
p.Enabled = true // 默认启用
|
||||
p.Enabled = true
|
||||
|
||||
if err := CreateProxy(&p); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "创建隧道失败"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "创建隧道失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 重新生成配置并热加载
|
||||
if err := generateAndReload(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "配置生效失败: " + err.Error()})
|
||||
return
|
||||
@@ -171,7 +181,12 @@ func createProxyHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func updateProxyHandler(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "无效的ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var p Proxy
|
||||
if err := c.ShouldBindJSON(&p); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "请求参数错误"})
|
||||
@@ -180,11 +195,10 @@ func updateProxyHandler(c *gin.Context) {
|
||||
p.ID = id
|
||||
|
||||
if err := UpdateProxy(&p); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "更新隧道失败"})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "更新隧道失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 重新生成配置并热加载
|
||||
if err := generateAndReload(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "配置生效失败: " + err.Error()})
|
||||
return
|
||||
@@ -194,13 +208,17 @@ func updateProxyHandler(c *gin.Context) {
|
||||
}
|
||||
|
||||
func deleteProxyHandler(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
if err := DeleteProxy(id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "删除隧道失败"})
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "无效的ID"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := DeleteProxy(id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "删除隧道失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 重新生成配置并热加载
|
||||
if err := generateAndReload(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "配置生效失败: " + err.Error()})
|
||||
return
|
||||
@@ -247,6 +265,80 @@ func getFrpcStatusHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "data": gin.H{"running": running}})
|
||||
}
|
||||
|
||||
// ========== 🟢 导入 TOML(新增) ==========
|
||||
func importTomlHandler(c *gin.Context) {
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "请选择文件"})
|
||||
return
|
||||
}
|
||||
|
||||
f, err := file.Open()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "读取文件失败"})
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := buf.ReadFrom(f); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "读取文件失败"})
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := ParseToml(buf.String())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 1, "msg": "解析 TOML 失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 更新全局配置(保持当前 tcpMux 值)
|
||||
cfg := parsed.ToGlobalConfig()
|
||||
currentCfg, _ := GetGlobalConfig()
|
||||
if currentCfg != nil {
|
||||
cfg.TcpMux = currentCfg.TcpMux // 保持当前值不变
|
||||
}
|
||||
cfg.TcpMux = true // 强制开启
|
||||
|
||||
if err := UpdateGlobalConfig(cfg); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "更新配置失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 清空并导入隧道
|
||||
if _, err := DB.Exec("DELETE FROM proxies"); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "清空隧道失败"})
|
||||
return
|
||||
}
|
||||
|
||||
proxies := parsed.ToProxies()
|
||||
for _, p := range proxies {
|
||||
if err := CreateProxy(&p); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "导入隧道失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 生成配置并热加载
|
||||
if err := GenerateFrpcConfig(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 2, "msg": "生成配置失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
if err := ReloadFrpc(); err != nil {
|
||||
// 导入成功但热加载失败,不影响整体结果
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": fmt.Sprintf("导入成功!共 %d 条隧道,但热加载失败: %s", len(proxies), err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": fmt.Sprintf("导入成功!共 %d 条隧道", len(proxies)),
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 辅助函数 ==========
|
||||
func generateAndReload() error {
|
||||
if err := GenerateFrpcConfig(); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user