realm/web/main.go

343 lines
8.1 KiB
Go
Raw Permalink Normal View History

2024-12-23 01:59:08 -05:00
package main
import (
2024-12-28 09:47:31 -05:00
"bytes"
2024-12-23 01:59:08 -05:00
"fmt"
"io/ioutil"
"log"
"net/http"
"os/exec"
"sync"
"github.com/BurntSushi/toml"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)
type ForwardingRule struct {
2024-12-28 09:47:31 -05:00
Listen string `toml:"listen" json:"listen"`
Remote string `toml:"remote" json:"remote"`
2024-12-23 01:59:08 -05:00
}
type Config struct {
Network struct {
NoTCP bool `toml:"no_tcp"`
UseUDP bool `toml:"use_udp"`
} `toml:"network"`
Endpoints []ForwardingRule `toml:"endpoints"`
}
type PanelConfig struct {
Auth struct {
Password string `toml:"password"`
} `toml:"auth"`
Server struct {
Port int `toml:"port"`
} `toml:"server"`
HTTPS struct {
Enabled bool `toml:"enabled"`
CertFile string `toml:"cert_file"`
KeyFile string `toml:"key_file"`
} `toml:"https"`
}
var (
mu sync.Mutex
config Config
panelConfig PanelConfig
2024-12-28 09:47:31 -05:00
httpsWarningShown = false
2024-12-23 01:59:08 -05:00
)
func LoadConfig() error {
data, err := ioutil.ReadFile("/root/.realm/config.toml")
if err != nil {
return err
}
if _, err := toml.Decode(string(data), &config); err != nil {
return err
}
return nil
}
func LoadPanelConfig() error {
data, err := ioutil.ReadFile("./config.toml")
if err != nil {
return err
}
if _, err := toml.Decode(string(data), &panelConfig); err != nil {
return err
}
return nil
}
2024-12-28 09:47:31 -05:00
func SaveConfig() error {
2024-12-23 01:59:08 -05:00
mu.Lock()
defer mu.Unlock()
2024-12-28 09:47:31 -05:00
var buf bytes.Buffer
encoder := toml.NewEncoder(&buf)
// 编码 network 部分
if err := encoder.Encode(map[string]interface{}{"network": config.Network}); err != nil {
2024-12-23 01:59:08 -05:00
return err
}
2024-12-28 09:47:31 -05:00
// 只有在有规则时才添加 endpoints 部分
if len(config.Endpoints) > 0 {
buf.WriteString("\n")
for _, endpoint := range config.Endpoints {
buf.WriteString("[[endpoints]]\n")
if err := encoder.Encode(endpoint); err != nil {
return err
}
buf.WriteString("\n")
}
}
// 写入文件
return ioutil.WriteFile("/root/.realm/config.toml", buf.Bytes(), 0644)
2024-12-23 01:59:08 -05:00
}
func AuthRequired() gin.HandlerFunc {
return func(c *gin.Context) {
session := sessions.Default(c)
user := session.Get("user")
if user == nil {
c.Redirect(http.StatusFound, "/login")
c.Abort()
return
}
c.Next()
}
}
2024-12-28 09:47:31 -05:00
func HTTPSRedirect() gin.HandlerFunc {
return func(c *gin.Context) {
if panelConfig.HTTPS.Enabled && c.Request.TLS == nil {
target := "https://" + c.Request.Host + c.Request.URL.Path
if c.Request.URL.RawQuery != "" {
target += "?" + c.Request.URL.RawQuery
}
c.Redirect(http.StatusMovedPermanently, target)
c.Abort()
return
}
c.Next()
}
}
2024-12-23 01:59:08 -05:00
func main() {
if err := LoadConfig(); err != nil {
log.Fatalf("无法加载 realm 配置: %v", err)
}
if err := LoadPanelConfig(); err != nil {
log.Fatalf("无法加载面板配置: %v", err)
}
r := gin.Default()
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("realm_session", store))
2024-12-28 09:47:31 -05:00
r.Use(HTTPSRedirect())
2024-12-23 01:59:08 -05:00
r.Static("/static", "./static")
r.GET("/login", func(c *gin.Context) {
session := sessions.Default(c)
if session.Get("user") != nil {
c.Redirect(http.StatusFound, "/")
return
}
c.File("./templates/login.html")
})
r.POST("/login", func(c *gin.Context) {
var loginData struct {
Password string `json:"password"`
}
if err := c.ShouldBindJSON(&loginData); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求"})
return
}
if loginData.Password == panelConfig.Auth.Password {
session := sessions.Default(c)
session.Set("user", true)
session.Options(sessions.Options{
2025-01-05 02:03:47 -05:00
MaxAge: 3600 * 2, // 2小时
2024-12-23 01:59:08 -05:00
})
if err := session.Save(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Session保存失败"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "登录成功"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"error": "密码错误"})
}
})
authorized := r.Group("/")
authorized.Use(AuthRequired())
{
authorized.GET("/", func(c *gin.Context) {
2024-12-28 09:47:31 -05:00
if !panelConfig.HTTPS.Enabled && !httpsWarningShown {
c.Header("X-HTTPS-Warning", "当前未启用HTTPS强烈建议启用HTTPS")
httpsWarningShown = true
}
2024-12-23 01:59:08 -05:00
c.File("./templates/index.html")
})
authorized.GET("/get_rules", func(c *gin.Context) {
mu.Lock()
2024-12-28 09:47:31 -05:00
rules := config.Endpoints
mu.Unlock()
2024-12-23 01:59:08 -05:00
c.JSON(200, rules)
})
authorized.POST("/add_rule", func(c *gin.Context) {
2024-12-28 09:47:31 -05:00
var input ForwardingRule
2024-12-23 01:59:08 -05:00
if err := c.ShouldBindJSON(&input); err != nil {
2024-12-28 09:47:31 -05:00
c.JSON(400, gin.H{"error": "无效的输入"})
2024-12-23 01:59:08 -05:00
return
}
mu.Lock()
2024-12-28 09:47:31 -05:00
config.Endpoints = append(config.Endpoints, input)
2024-12-23 01:59:08 -05:00
mu.Unlock()
2024-12-28 09:47:31 -05:00
if err := SaveConfig(); err != nil {
c.JSON(500, gin.H{"error": "保存配置失败"})
2024-12-23 01:59:08 -05:00
return
}
c.JSON(201, input)
})
authorized.DELETE("/delete_rule", func(c *gin.Context) {
listen := c.Query("listen")
mu.Lock()
2024-12-28 09:47:31 -05:00
found := false
for i, rule := range config.Endpoints {
2024-12-23 01:59:08 -05:00
if rule.Listen == listen {
2024-12-28 09:47:31 -05:00
config.Endpoints = append(config.Endpoints[:i], config.Endpoints[i+1:]...)
found = true
2024-12-23 01:59:08 -05:00
break
}
}
mu.Unlock()
2024-12-28 09:47:31 -05:00
if err := SaveConfig(); err != nil {
c.JSON(500, gin.H{"error": "保存转发规则失败"})
2024-12-23 01:59:08 -05:00
return
}
2024-12-28 09:47:31 -05:00
if found {
c.JSON(200, gin.H{"message": "保存转发规则成功"})
} else {
c.JSON(404, gin.H{"error": "未找到转发规则"})
}
2024-12-23 01:59:08 -05:00
})
authorized.POST("/start_service", func(c *gin.Context) {
cmd := exec.Command("systemctl", "start", "realm")
if err := cmd.Run(); err != nil {
2024-12-28 09:47:31 -05:00
c.JSON(500, gin.H{"error": "服务启动失败"})
2024-12-23 01:59:08 -05:00
return
}
2024-12-28 09:47:31 -05:00
c.JSON(200, gin.H{"message": "服务启动成功"})
2024-12-23 01:59:08 -05:00
})
authorized.POST("/stop_service", func(c *gin.Context) {
cmd := exec.Command("systemctl", "stop", "realm")
if err := cmd.Run(); err != nil {
2024-12-28 09:47:31 -05:00
c.JSON(500, gin.H{"error": "服务停止失败"})
2024-12-23 01:59:08 -05:00
return
}
2024-12-28 09:47:31 -05:00
c.JSON(200, gin.H{"message": "服务停止成功"})
2024-12-23 01:59:08 -05:00
})
2025-01-05 02:03:47 -05:00
authorized.POST("/restart_service", func(c *gin.Context) {
cmd := exec.Command("systemctl", "restart", "realm")
if err := cmd.Run(); err != nil {
c.JSON(500, gin.H{"error": "服务重启失败"})
return
}
c.JSON(200, gin.H{"message": "服务重启成功"})
})
2024-12-23 01:59:08 -05:00
authorized.GET("/check_status", func(c *gin.Context) {
cmd := exec.Command("systemctl", "is-active", "--quiet", "realm")
err := cmd.Run()
var status string
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
if exitError.ExitCode() == 3 {
status = "未启用"
} else {
status = "未知状态"
}
} else {
status = "检查失败"
}
} else {
status = "启用"
}
c.JSON(200, gin.H{"status": status})
})
2024-12-28 09:47:31 -05:00
authorized.POST("/logout", func(c *gin.Context) {
session := sessions.Default(c)
session.Clear()
session.Save()
c.JSON(http.StatusOK, gin.H{"message": "登出成功"})
})
2024-12-23 01:59:08 -05:00
}
port := panelConfig.Server.Port
if port == 0 {
port = 8081 // 默认端口
}
if panelConfig.HTTPS.Enabled {
if panelConfig.HTTPS.CertFile == "" || panelConfig.HTTPS.KeyFile == "" {
log.Println("警告HTTPS 已启用,但证书或密钥文件路径未指定。将使用 HTTP 继续。")
log.Printf("服务器正在使用 HTTP 运行,端口:%d\n", port)
r.Run(fmt.Sprintf(":%d", port))
} else {
log.Printf("服务器正在使用 HTTPS 运行,端口:%d\n", port)
2024-12-28 09:47:31 -05:00
go func() {
log.Printf("HTTP 服务器正在运行端口8082用于重定向到 HTTPS\n")
if err := http.ListenAndServe(":8082", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
target := "https://" + r.Host + r.URL.Path
if r.URL.RawQuery != "" {
target += "?" + r.URL.RawQuery
}
http.Redirect(w, r, target, http.StatusMovedPermanently)
})); err != nil {
log.Fatalf("HTTP 服务器错误: %v", err)
}
}()
if err := r.RunTLS(fmt.Sprintf(":%d", port), panelConfig.HTTPS.CertFile, panelConfig.HTTPS.KeyFile); err != nil {
log.Fatalf("HTTPS 服务器错误: %v", err)
}
2024-12-23 01:59:08 -05:00
}
} else {
2024-12-28 09:47:31 -05:00
log.Println("警告:未启用 HTTPS强烈建议启用 HTTPS。")
2024-12-23 01:59:08 -05:00
log.Printf("服务器正在使用 HTTP 运行,端口:%d\n", port)
r.Run(fmt.Sprintf(":%d", port))
}
}