Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMHSanaei <mc.sanaei@gmail.com>2023-02-09 22:18:06 +0300
committerMHSanaei <mc.sanaei@gmail.com>2023-02-09 22:18:06 +0300
commitb73e4173a3c1e69e02ad6b4e3b43e425e57a5be9 (patch)
treed95d2f5e903d97082e11eb9f9023c165b1bde388 /web/controller
3x-ui
Diffstat (limited to 'web/controller')
-rw-r--r--web/controller/api.go48
-rw-r--r--web/controller/base.go33
-rw-r--r--web/controller/inbound.go136
-rw-r--r--web/controller/index.go84
-rw-r--r--web/controller/server.go85
-rw-r--r--web/controller/setting.go88
-rw-r--r--web/controller/util.go97
-rw-r--r--web/controller/xui.go42
8 files changed, 613 insertions, 0 deletions
diff --git a/web/controller/api.go b/web/controller/api.go
new file mode 100644
index 00000000..84ac9c20
--- /dev/null
+++ b/web/controller/api.go
@@ -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)
+}
diff --git a/web/controller/base.go b/web/controller/base.go
new file mode 100644
index 00000000..6ed2f0ef
--- /dev/null
+++ b/web/controller/base.go
@@ -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;
+}
diff --git a/web/controller/inbound.go b/web/controller/inbound.go
new file mode 100644
index 00000000..7be877e3
--- /dev/null
+++ b/web/controller/inbound.go
@@ -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)
+}
diff --git a/web/controller/index.go b/web/controller/index.go
new file mode 100644
index 00000000..e0be6076
--- /dev/null
+++ b/web/controller/index.go
@@ -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"))
+}
diff --git a/web/controller/server.go b/web/controller/server.go
new file mode 100644
index 00000000..d59b3400
--- /dev/null
+++ b/web/controller/server.go
@@ -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)
+}
diff --git a/web/controller/setting.go b/web/controller/setting.go
new file mode 100644
index 00000000..f500c76d
--- /dev/null
+++ b/web/controller/setting.go
@@ -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)
+}
diff --git a/web/controller/util.go b/web/controller/util.go
new file mode 100644
index 00000000..2bd2fa55
--- /dev/null
+++ b/web/controller/util.go
@@ -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"
+}
diff --git a/web/controller/xui.go b/web/controller/xui.go
new file mode 100644
index 00000000..5832be84
--- /dev/null
+++ b/web/controller/xui.go
@@ -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)
+}