This commit is contained in:
MHSanaei
2023-02-09 22:48:06 +03:30
commit b73e4173a3
133 changed files with 61908 additions and 0 deletions

48
web/controller/api.go Normal file
View File

@@ -0,0 +1,48 @@
package controller
import (
"github.com/gin-gonic/gin"
)
type APIController struct {
BaseController
inboundController *InboundController
settingController *SettingController
}
func NewAPIController(g *gin.RouterGroup) *APIController {
a := &APIController{}
a.initRouter(g)
return a
}
func (a *APIController) initRouter(g *gin.RouterGroup) {
g = g.Group("/xui/API/inbounds")
g.Use(a.checkLogin)
g.GET("/", a.inbounds)
g.GET("/get/:id", a.inbound)
g.POST("/add", a.addInbound)
g.POST("/del/:id", a.delInbound)
g.POST("/update/:id", a.updateInbound)
a.inboundController = NewInboundController(g)
}
func (a *APIController) inbounds(c *gin.Context) {
a.inboundController.getInbounds(c)
}
func (a *APIController) inbound(c *gin.Context) {
a.inboundController.getInbound(c)
}
func (a *APIController) addInbound(c *gin.Context) {
a.inboundController.addInbound(c)
}
func (a *APIController) delInbound(c *gin.Context) {
a.inboundController.delInbound(c)
}
func (a *APIController) updateInbound(c *gin.Context) {
a.inboundController.updateInbound(c)
}

33
web/controller/base.go Normal file
View File

@@ -0,0 +1,33 @@
package controller
import (
"github.com/gin-gonic/gin"
"net/http"
"x-ui/web/session"
)
type BaseController struct {
}
func (a *BaseController) checkLogin(c *gin.Context) {
if !session.IsLogin(c) {
if isAjax(c) {
pureJsonMsg(c, false, I18n(c , "pages.login.loginAgain"))
} else {
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
}
c.Abort()
} else {
c.Next()
}
}
func I18n(c *gin.Context , name string) string{
anyfunc, _ := c.Get("I18n")
i18n, _ := anyfunc.(func(key string, params ...string) (string, error))
message, _ := i18n(name)
return message;
}

136
web/controller/inbound.go Normal file
View File

@@ -0,0 +1,136 @@
package controller
import (
"fmt"
"github.com/gin-gonic/gin"
"strconv"
"x-ui/database/model"
"x-ui/logger"
"x-ui/web/global"
"x-ui/web/service"
"x-ui/web/session"
)
type InboundController struct {
inboundService service.InboundService
xrayService service.XrayService
}
func NewInboundController(g *gin.RouterGroup) *InboundController {
a := &InboundController{}
a.initRouter(g)
a.startTask()
return a
}
func (a *InboundController) initRouter(g *gin.RouterGroup) {
g = g.Group("/inbound")
g.POST("/list", a.getInbounds)
g.POST("/add", a.addInbound)
g.POST("/del/:id", a.delInbound)
g.POST("/update/:id", a.updateInbound)
g.POST("/resetClientTraffic/:email", a.resetClientTraffic)
}
func (a *InboundController) startTask() {
webServer := global.GetWebServer()
c := webServer.GetCron()
c.AddFunc("@every 10s", func() {
if a.xrayService.IsNeedRestartAndSetFalse() {
err := a.xrayService.RestartXray(false)
if err != nil {
logger.Error("restart xray failed:", err)
}
}
})
}
func (a *InboundController) getInbounds(c *gin.Context) {
user := session.GetLoginUser(c)
inbounds, err := a.inboundService.GetInbounds(user.Id)
if err != nil {
jsonMsg(c, I18n(c , "pages.inbounds.toasts.obtain"), err)
return
}
jsonObj(c, inbounds, nil)
}
func (a *InboundController) getInbound(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, I18n(c , "get"), err)
return
}
inbound, err := a.inboundService.GetInbound(id)
if err != nil {
jsonMsg(c, I18n(c , "pages.inbounds.toasts.obtain"), err)
return
}
jsonObj(c, inbound, nil)
}
func (a *InboundController) addInbound(c *gin.Context) {
inbound := &model.Inbound{}
err := c.ShouldBind(inbound)
if err != nil {
jsonMsg(c, I18n(c , "pages.inbounds.addTo"), err)
return
}
user := session.GetLoginUser(c)
inbound.UserId = user.Id
inbound.Enable = true
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
inbound, err = a.inboundService.AddInbound(inbound)
jsonMsgObj(c, I18n(c , "pages.inbounds.addTo"), inbound, err)
if err == nil {
a.xrayService.SetToNeedRestart()
}
}
func (a *InboundController) delInbound(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, I18n(c , "delete"), err)
return
}
err = a.inboundService.DelInbound(id)
jsonMsgObj(c, I18n(c , "delete"), id, err)
if err == nil {
a.xrayService.SetToNeedRestart()
}
}
func (a *InboundController) updateInbound(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
jsonMsg(c, I18n(c , "pages.inbounds.revise"), err)
return
}
inbound := &model.Inbound{
Id: id,
}
err = c.ShouldBind(inbound)
if err != nil {
jsonMsg(c, I18n(c , "pages.inbounds.revise"), err)
return
}
inbound, err = a.inboundService.UpdateInbound(inbound)
jsonMsgObj(c, I18n(c , "pages.inbounds.revise"), inbound, err)
if err == nil {
a.xrayService.SetToNeedRestart()
}
}
func (a *InboundController) resetClientTraffic(c *gin.Context) {
email := c.Param("email")
err := a.inboundService.ResetClientTraffic(email)
if err != nil {
jsonMsg(c, "something worng!", err)
return
}
jsonMsg(c, "traffic reseted", nil)
}

84
web/controller/index.go Normal file
View File

@@ -0,0 +1,84 @@
package controller
import (
"net/http"
"time"
"x-ui/logger"
"x-ui/web/job"
"x-ui/web/service"
"x-ui/web/session"
"github.com/gin-gonic/gin"
)
type LoginForm struct {
Username string `json:"username" form:"username"`
Password string `json:"password" form:"password"`
}
type IndexController struct {
BaseController
userService service.UserService
}
func NewIndexController(g *gin.RouterGroup) *IndexController {
a := &IndexController{}
a.initRouter(g)
return a
}
func (a *IndexController) initRouter(g *gin.RouterGroup) {
g.GET("/", a.index)
g.POST("/login", a.login)
g.GET("/logout", a.logout)
}
func (a *IndexController) index(c *gin.Context) {
if session.IsLogin(c) {
c.Redirect(http.StatusTemporaryRedirect, "xui/")
return
}
html(c, "login.html", "pages.login.title", nil)
}
func (a *IndexController) login(c *gin.Context) {
var form LoginForm
err := c.ShouldBind(&form)
if err != nil {
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.invalidFormData"))
return
}
if form.Username == "" {
pureJsonMsg(c, false, I18n(c, "pages.login.toasts.emptyUsername"))
return
}
if form.Password == "" {
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.emptyPassword"))
return
}
user := a.userService.CheckUser(form.Username, form.Password)
timeStr := time.Now().Format("2006-01-02 15:04:05")
if user == nil {
job.NewStatsNotifyJob().UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0)
logger.Infof("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password)
pureJsonMsg(c, false, I18n(c , "pages.login.toasts.wrongUsernameOrPassword"))
return
} else {
logger.Infof("%s login success,Ip Address:%s\n", form.Username, getRemoteIp(c))
job.NewStatsNotifyJob().UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 1)
}
err = session.SetLoginUser(c, user)
logger.Info("user", user.Id, "login success")
jsonMsg(c, I18n(c , "pages.login.toasts.successLogin"), err)
}
func (a *IndexController) logout(c *gin.Context) {
user := session.GetLoginUser(c)
if user != nil {
logger.Info("user", user.Id, "logout")
}
session.ClearSession(c)
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
}

85
web/controller/server.go Normal file
View File

@@ -0,0 +1,85 @@
package controller
import (
"github.com/gin-gonic/gin"
"time"
"x-ui/web/global"
"x-ui/web/service"
)
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("/installXray/:version", a.installXray)
}
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, I18n(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, I18n(c , "install") + " xray", err)
}

88
web/controller/setting.go Normal file
View File

@@ -0,0 +1,88 @@
package controller
import (
"errors"
"github.com/gin-gonic/gin"
"time"
"x-ui/web/entity"
"x-ui/web/service"
"x-ui/web/session"
)
type updateUserForm struct {
OldUsername string `json:"oldUsername" form:"oldUsername"`
OldPassword string `json:"oldPassword" form:"oldPassword"`
NewUsername string `json:"newUsername" form:"newUsername"`
NewPassword string `json:"newPassword" form:"newPassword"`
}
type SettingController struct {
settingService service.SettingService
userService service.UserService
panelService service.PanelService
}
func NewSettingController(g *gin.RouterGroup) *SettingController {
a := &SettingController{}
a.initRouter(g)
return a
}
func (a *SettingController) initRouter(g *gin.RouterGroup) {
g = g.Group("/setting")
g.POST("/all", a.getAllSetting)
g.POST("/update", a.updateSetting)
g.POST("/updateUser", a.updateUser)
g.POST("/restartPanel", a.restartPanel)
}
func (a *SettingController) getAllSetting(c *gin.Context) {
allSetting, err := a.settingService.GetAllSetting()
if err != nil {
jsonMsg(c, I18n(c , "pages.setting.toasts.getSetting"), err)
return
}
jsonObj(c, allSetting, nil)
}
func (a *SettingController) updateSetting(c *gin.Context) {
allSetting := &entity.AllSetting{}
err := c.ShouldBind(allSetting)
if err != nil {
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
return
}
err = a.settingService.UpdateAllSetting(allSetting)
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
}
func (a *SettingController) updateUser(c *gin.Context) {
form := &updateUserForm{}
err := c.ShouldBind(form)
if err != nil {
jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err)
return
}
user := session.GetLoginUser(c)
if user.Username != form.OldUsername || user.Password != form.OldPassword {
jsonMsg(c, I18n(c , "pages.setting.toasts.modifyUser"), errors.New(I18n(c , "pages.setting.toasts.originalUserPassIncorrect")))
return
}
if form.NewUsername == "" || form.NewPassword == "" {
jsonMsg(c,I18n(c , "pages.setting.toasts.modifyUser"), errors.New(I18n(c , "pages.setting.toasts.userPassMustBeNotEmpty")))
return
}
err = a.userService.UpdateUser(user.Id, form.NewUsername, form.NewPassword)
if err == nil {
user.Username = form.NewUsername
user.Password = form.NewPassword
session.SetLoginUser(c, user)
}
jsonMsg(c, I18n(c , "pages.setting.toasts.modifyUser"), err)
}
func (a *SettingController) restartPanel(c *gin.Context) {
err := a.panelService.RestartPanel(time.Second * 3)
jsonMsg(c, I18n(c , "pages.setting.restartPanel"), err)
}

97
web/controller/util.go Normal file
View File

@@ -0,0 +1,97 @@
package controller
import (
"github.com/gin-gonic/gin"
"net"
"net/http"
"strings"
"x-ui/config"
"x-ui/logger"
"x-ui/web/entity"
)
func getUriId(c *gin.Context) int64 {
s := struct {
Id int64 `uri:"id"`
}{}
_ = c.BindUri(&s)
return s.Id
}
func getRemoteIp(c *gin.Context) string {
value := c.GetHeader("X-Forwarded-For")
if value != "" {
ips := strings.Split(value, ",")
return ips[0]
} else {
addr := c.Request.RemoteAddr
ip, _, _ := net.SplitHostPort(addr)
return ip
}
}
func jsonMsg(c *gin.Context, msg string, err error) {
jsonMsgObj(c, msg, nil, err)
}
func jsonObj(c *gin.Context, obj interface{}, err error) {
jsonMsgObj(c, "", obj, err)
}
func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) {
m := entity.Msg{
Obj: obj,
}
if err == nil {
m.Success = true
if msg != "" {
m.Msg = msg + I18n(c , "success")
}
} else {
m.Success = false
m.Msg = msg + I18n(c , "fail") + ": " + err.Error()
logger.Warning(msg + I18n(c , "fail") + ": ", err)
}
c.JSON(http.StatusOK, m)
}
func pureJsonMsg(c *gin.Context, success bool, msg string) {
if success {
c.JSON(http.StatusOK, entity.Msg{
Success: true,
Msg: msg,
})
} else {
c.JSON(http.StatusOK, entity.Msg{
Success: false,
Msg: msg,
})
}
}
func html(c *gin.Context, name string, title string, data gin.H) {
if data == nil {
data = gin.H{}
}
data["title"] = title
data["request_uri"] = c.Request.RequestURI
data["base_path"] = c.GetString("base_path")
c.HTML(http.StatusOK, name, getContext(data))
}
func getContext(h gin.H) gin.H {
a := gin.H{
"cur_ver": config.GetVersion(),
}
if h != nil {
for key, value := range h {
a[key] = value
}
}
return a
}
func isAjax(c *gin.Context) bool {
return c.GetHeader("X-Requested-With") == "XMLHttpRequest"
}

42
web/controller/xui.go Normal file
View File

@@ -0,0 +1,42 @@
package controller
import (
"github.com/gin-gonic/gin"
)
type XUIController struct {
BaseController
inboundController *InboundController
settingController *SettingController
}
func NewXUIController(g *gin.RouterGroup) *XUIController {
a := &XUIController{}
a.initRouter(g)
return a
}
func (a *XUIController) initRouter(g *gin.RouterGroup) {
g = g.Group("/xui")
g.Use(a.checkLogin)
g.GET("/", a.index)
g.GET("/inbounds", a.inbounds)
g.GET("/setting", a.setting)
a.inboundController = NewInboundController(g)
a.settingController = NewSettingController(g)
}
func (a *XUIController) index(c *gin.Context) {
html(c, "index.html", "pages.index.title", nil)
}
func (a *XUIController) inbounds(c *gin.Context) {
html(c, "inbounds.html", "pages.inbounds.title", nil)
}
func (a *XUIController) setting(c *gin.Context) {
html(c, "setting.html", "pages.setting.title", nil)
}