feat: add compatible Trojan manager image

This commit is contained in:
chermack
2026-07-26 05:01:15 +08:00
parent 059be96536
commit cb17d56cae
26 changed files with 320 additions and 41 deletions
+10 -1
View File
@@ -123,6 +123,12 @@ func RequestUsername(c *gin.Context) string {
return claims[identityKey].(string)
}
func RequireAdmin(c *gin.Context) bool {
if RequestUsername(c) == "admin" { return true }
c.JSON(403, gin.H{"code": 403, "message": "administrator access required"})
return false
}
// Auth 权限router
func Auth(r *gin.Engine, timeout int) *jwt.GinJWTMiddleware {
jwtInit(timeout)
@@ -152,7 +158,10 @@ func Auth(r *gin.Engine, timeout int) *jwt.GinJWTMiddleware {
}
})
r.POST("/auth/login", authMiddleware.LoginHandler)
r.POST("/auth/register", updateUser)
r.POST("/auth/register", func(c *gin.Context) {
if result, _ := core.GetValue("admin_pass"); result != "" { c.JSON(403, gin.H{"code":403,"message":"administrator already initialized"}); return }
updateUser(c)
})
authO := r.Group("/auth")
authO.Use(authMiddleware.MiddlewareFunc())
{
+18
View File
@@ -0,0 +1,18 @@
package controller
import (
"crypto/x509"
"encoding/pem"
"fmt"
"os"
"time"
"trojan/managerconfig"
)
func SystemConfig() *ResponseBody { c, err := managerconfig.Load(); r := &ResponseBody{Msg:"success", Data:c}; if err != nil { r.Msg=err.Error() }; return r }
func ValidateSystemConfig(c managerconfig.Config) *ResponseBody { r:=&ResponseBody{Msg:"success"}; if err:=managerconfig.Validate(c); err != nil { r.Msg=err.Error() }; return r }
func ApplySystemConfig(c managerconfig.Config) *ResponseBody { r:=ValidateSystemConfig(c); if r.Msg!="success" { return r }; if err:=managerconfig.Save(c); err!=nil { r.Msg=err.Error(); return r }; r.Data=map[string]bool{"restartRequired":true,"nginxReloadRequired":false,"containerRecreateRequired":false}; return r }
func CertificateStatus() *ResponseBody {
r:=&ResponseBody{Msg:"success"}; b,err:=os.ReadFile("/etc/trojan/certs/fullchain.pem"); if err!=nil { r.Msg=err.Error(); return r }; block,_:=pem.Decode(b); if block==nil { r.Msg="invalid certificate PEM"; return r }; cert,err:=x509.ParseCertificate(block.Bytes); if err!=nil { r.Msg=err.Error(); return r }
r.Data=map[string]interface{}{"subject":cert.Subject.String(),"issuer":cert.Issuer.String(),"dnsNames":cert.DNSNames,"notBefore":cert.NotBefore,"notAfter":cert.NotAfter,"daysRemaining":int(time.Until(cert.NotAfter).Hours()/24),"serialNumber":fmt.Sprint(cert.SerialNumber)}; return r
}
+4 -4
View File
@@ -20,7 +20,7 @@ import (
func Start() *ResponseBody {
responseBody := ResponseBody{Msg: "success"}
defer TimeCost(time.Now(), &responseBody)
trojan.Start()
if err := trojan.Start(); err != nil { responseBody.Msg = err.Error() }
return &responseBody
}
@@ -28,7 +28,7 @@ func Start() *ResponseBody {
func Stop() *ResponseBody {
responseBody := ResponseBody{Msg: "success"}
defer TimeCost(time.Now(), &responseBody)
trojan.Stop()
if err := trojan.Stop(); err != nil { responseBody.Msg = err.Error() }
return &responseBody
}
@@ -36,7 +36,7 @@ func Stop() *ResponseBody {
func Restart() *ResponseBody {
responseBody := ResponseBody{Msg: "success"}
defer TimeCost(time.Now(), &responseBody)
trojan.Restart()
if err := trojan.Restart(); err != nil { responseBody.Msg = err.Error() }
return &responseBody
}
@@ -53,7 +53,7 @@ func SetLogLevel(level int) *ResponseBody {
responseBody := ResponseBody{Msg: "success"}
defer TimeCost(time.Now(), &responseBody)
core.WriteLogLevel(level)
trojan.Restart()
if err := trojan.Restart(); err != nil { responseBody.Msg = err.Error() }
return &responseBody
}
+4 -4
View File
@@ -36,8 +36,8 @@ func UserList(requestUser string) *ResponseBody {
}
domain, port := trojan.GetDomainAndPort()
responseBody.Data = map[string]interface{}{
"domain": domain,
"port": port,
"domain": domain, "port": port, "publicPort": port,
"listenPort": core.GetConfig().LocalPort,
"userList": userList,
}
return &responseBody
@@ -55,8 +55,8 @@ func PageUserList(curPage int, pageSize int) *ResponseBody {
}
domain, port := trojan.GetDomainAndPort()
responseBody.Data = map[string]interface{}{
"domain": domain,
"port": port,
"domain": domain, "port": port, "publicPort": port,
"listenPort": core.GetConfig().LocalPort,
"pageData": pageData,
}
return &responseBody
+12
View File
@@ -8,6 +8,7 @@ import (
"io/fs"
"net/http"
"strconv"
"trojan/managerconfig"
"trojan/core"
"trojan/util"
"trojan/web/controller"
@@ -171,6 +172,14 @@ func noTokenRouter(router *gin.Engine) {
})
}
func systemRouter(router *gin.Engine) {
system := router.Group("/system")
system.GET("/config", func(c *gin.Context) { if !RequireAdmin(c) { return }; c.JSON(200, controller.SystemConfig()) })
system.POST("/config/validate", func(c *gin.Context) { if !RequireAdmin(c) { return }; var cfg managerconfig.Config; if err:=c.ShouldBindJSON(&cfg); err!=nil { c.JSON(400, gin.H{"message":err.Error()}); return }; c.JSON(200, controller.ValidateSystemConfig(cfg)) })
system.POST("/config/apply", func(c *gin.Context) { if !RequireAdmin(c) { return }; var cfg managerconfig.Config; if err:=c.ShouldBindJSON(&cfg); err!=nil { c.JSON(400, gin.H{"message":err.Error()}); return }; c.JSON(200, controller.ApplySystemConfig(cfg)) })
system.GET("/certificate", func(c *gin.Context) { if !RequireAdmin(c) { return }; c.JSON(200, controller.CertificateStatus()) })
}
// Start web启动入口
func Start(host string, port, timeout int, isSSL bool) {
router := gin.Default()
@@ -178,7 +187,10 @@ func Start(host string, port, timeout int, isSSL bool) {
router.Use(gzip.Gzip(gzip.DefaultCompression))
staticRouter(router)
noTokenRouter(router)
router.GET("/healthz", func(c *gin.Context) { c.JSON(200, gin.H{"status":"ok"}) })
router.GET("/readyz", func(c *gin.Context) { if core.GetConfig()==nil { c.JSON(503, gin.H{"status":"not ready"}); return }; c.JSON(200, gin.H{"status":"ready"}) })
router.Use(Auth(router, timeout).MiddlewareFunc())
systemRouter(router)
trojanRouter(router)
userRouter(router)
dataRouter(router)