3x-ui/web/controller/server.go
Shishkevich D. 1ddfe4aba3
Some checks are pending
Build and Release 3X-UI / build (386) (push) Waiting to run
Build and Release 3X-UI / build (amd64) (push) Waiting to run
Build and Release 3X-UI / build (arm64) (push) Waiting to run
Build and Release 3X-UI / build (armv5) (push) Waiting to run
Build and Release 3X-UI / build (armv6) (push) Waiting to run
Build and Release 3X-UI / build (armv7) (push) Waiting to run
Build and Release 3X-UI / build (s390x) (push) Waiting to run
chore: toasts translation refactoring
2025-05-09 10:46:29 +07:00

200 lines
4.9 KiB
Go

package controller
import (
"fmt"
"net/http"
"regexp"
"time"
"x-ui/web/global"
"x-ui/web/service"
"github.com/gin-gonic/gin"
)
var filenameRegex = regexp.MustCompile(`^[a-zA-Z0-9_\-.]+$`)
type ServerController struct {
BaseController
serverService service.ServerService
lastStatus *service.Status
lastGetStatusTime time.Time
lastVersions []string
lastGetVersionsTime time.Time
}
func NewServerController(g *gin.RouterGroup) *ServerController {
a := &ServerController{
lastGetStatusTime: time.Now(),
}
a.initRouter(g)
a.startTask()
return a
}
func (a *ServerController) initRouter(g *gin.RouterGroup) {
g = g.Group("/server")
g.Use(a.checkLogin)
g.POST("/status", a.status)
g.POST("/getXrayVersion", a.getXrayVersion)
g.POST("/stopXrayService", a.stopXrayService)
g.POST("/restartXrayService", a.restartXrayService)
g.POST("/installXray/:version", a.installXray)
g.POST("/updateGeofile/:fileName", a.updateGeofile)
g.POST("/logs/:count", a.getLogs)
g.POST("/getConfigJson", a.getConfigJson)
g.GET("/getDb", a.getDb)
g.POST("/importDB", a.importDB)
g.POST("/getNewX25519Cert", a.getNewX25519Cert)
}
func (a *ServerController) refreshStatus() {
a.lastStatus = a.serverService.GetStatus(a.lastStatus)
}
func (a *ServerController) startTask() {
webServer := global.GetWebServer()
c := webServer.GetCron()
c.AddFunc("@every 2s", func() {
now := time.Now()
if now.Sub(a.lastGetStatusTime) > time.Minute*3 {
return
}
a.refreshStatus()
})
}
func (a *ServerController) status(c *gin.Context) {
a.lastGetStatusTime = time.Now()
jsonObj(c, a.lastStatus, nil)
}
func (a *ServerController) getXrayVersion(c *gin.Context) {
now := time.Now()
if now.Sub(a.lastGetVersionsTime) <= time.Minute {
jsonObj(c, a.lastVersions, nil)
return
}
versions, err := a.serverService.GetXrayVersions()
if err != nil {
jsonMsg(c, I18nWeb(c, "getVersion"), err)
return
}
a.lastVersions = versions
a.lastGetVersionsTime = time.Now()
jsonObj(c, versions, nil)
}
func (a *ServerController) installXray(c *gin.Context) {
version := c.Param("version")
err := a.serverService.UpdateXray(version)
jsonMsg(c, I18nWeb(c, "pages.index.xraySwitchVersionPopover"), err)
}
func (a *ServerController) updateGeofile(c *gin.Context) {
fileName := c.Param("fileName")
err := a.serverService.UpdateGeofile(fileName)
jsonMsg(c, I18nWeb(c, "pages.index.geofileUpdatePopover"), err)
}
func (a *ServerController) stopXrayService(c *gin.Context) {
a.lastGetStatusTime = time.Now()
err := a.serverService.StopXrayService()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.xray.stopError"), err)
return
}
jsonMsg(c, I18nWeb(c, "pages.xray.stopSuccess"), err)
}
func (a *ServerController) restartXrayService(c *gin.Context) {
err := a.serverService.RestartXrayService()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.xray.restartError"), err)
return
}
jsonMsg(c, I18nWeb(c, "pages.xray.restartSuccess"), err)
}
func (a *ServerController) getLogs(c *gin.Context) {
count := c.Param("count")
level := c.PostForm("level")
syslog := c.PostForm("syslog")
logs := a.serverService.GetLogs(count, level, syslog)
jsonObj(c, logs, nil)
}
func (a *ServerController) getConfigJson(c *gin.Context) {
configJson, err := a.serverService.GetConfigJson()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.index.getConfigError"), err)
return
}
jsonObj(c, configJson, nil)
}
func (a *ServerController) getDb(c *gin.Context) {
db, err := a.serverService.GetDb()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.index.getDatabaseError"), err)
return
}
filename := "x-ui.db"
if !isValidFilename(filename) {
c.AbortWithError(http.StatusBadRequest, fmt.Errorf("invalid filename"))
return
}
// Set the headers for the response
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Disposition", "attachment; filename="+filename)
// Write the file contents to the response
c.Writer.Write(db)
}
func isValidFilename(filename string) bool {
// Validate that the filename only contains allowed characters
return filenameRegex.MatchString(filename)
}
func (a *ServerController) importDB(c *gin.Context) {
// Get the file from the request body
file, _, err := c.Request.FormFile("db")
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.index.readDatabaseError"), err)
return
}
defer file.Close()
// Always restart Xray before return
defer a.serverService.RestartXrayService()
defer func() {
a.lastGetStatusTime = time.Now()
}()
// Import it
err = a.serverService.ImportDB(file)
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.index.importDatabaseError"), err)
return
}
jsonObj(c, I18nWeb(c, "pages.index.importDatabaseSuccess"), nil)
}
func (a *ServerController) getNewX25519Cert(c *gin.Context) {
cert, err := a.serverService.GetNewX25519Cert()
if err != nil {
jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.getNewX25519CertError"), err)
return
}
jsonObj(c, cert, nil)
}