mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-22 00:35:48 +00:00
流量统计,修复问题
This commit is contained in:
112
web/controller/inbound.go
Normal file
112
web/controller/inbound.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/atomic"
|
||||
"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
|
||||
|
||||
isNeedXrayRestart atomic.Bool
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (a *InboundController) startTask() {
|
||||
webServer := global.GetWebServer()
|
||||
c := webServer.GetCron()
|
||||
c.AddFunc("@every 10s", func() {
|
||||
if a.isNeedXrayRestart.Load() {
|
||||
err := a.xrayService.RestartXray()
|
||||
if err != nil {
|
||||
logger.Error("restart xray failed:", err)
|
||||
}
|
||||
a.isNeedXrayRestart.Store(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (a *InboundController) getInbounds(c *gin.Context) {
|
||||
user := session.GetLoginUser(c)
|
||||
inbounds, err := a.inboundService.GetInbounds(user.Id)
|
||||
if err != nil {
|
||||
jsonMsg(c, "获取", err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, inbounds, nil)
|
||||
}
|
||||
|
||||
func (a *InboundController) addInbound(c *gin.Context) {
|
||||
inbound := &model.Inbound{}
|
||||
err := c.ShouldBind(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, "添加", err)
|
||||
return
|
||||
}
|
||||
user := session.GetLoginUser(c)
|
||||
inbound.UserId = user.Id
|
||||
inbound.Enable = true
|
||||
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
|
||||
err = a.inboundService.AddInbound(inbound)
|
||||
jsonMsg(c, "添加", err)
|
||||
if err == nil {
|
||||
a.isNeedXrayRestart.Store(true)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *InboundController) delInbound(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
jsonMsg(c, "删除", err)
|
||||
return
|
||||
}
|
||||
err = a.inboundService.DelInbound(id)
|
||||
jsonMsg(c, "删除", err)
|
||||
if err == nil {
|
||||
a.isNeedXrayRestart.Store(true)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *InboundController) updateInbound(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
jsonMsg(c, "修改", err)
|
||||
return
|
||||
}
|
||||
inbound := &model.Inbound{
|
||||
Id: id,
|
||||
}
|
||||
err = c.ShouldBind(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, "修改", err)
|
||||
return
|
||||
}
|
||||
err = a.inboundService.UpdateInbound(inbound)
|
||||
jsonMsg(c, "修改", err)
|
||||
if err == nil {
|
||||
a.isNeedXrayRestart.Store(true)
|
||||
}
|
||||
}
|
||||
44
web/controller/setting.go
Normal file
44
web/controller/setting.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"x-ui/web/entity"
|
||||
"x-ui/web/service"
|
||||
)
|
||||
|
||||
type SettingController struct {
|
||||
settingService service.SettingService
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (a *SettingController) getAllSetting(c *gin.Context) {
|
||||
allSetting, err := a.settingService.GetAllSetting()
|
||||
if err != nil {
|
||||
jsonMsg(c, "获取设置", 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, "修改设置", err)
|
||||
return
|
||||
}
|
||||
err = a.settingService.UpdateAllSetting(allSetting)
|
||||
jsonMsg(c, "修改设置", err)
|
||||
}
|
||||
@@ -2,31 +2,18 @@ package controller
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/atomic"
|
||||
"log"
|
||||
"strconv"
|
||||
"x-ui/database/model"
|
||||
"x-ui/logger"
|
||||
"x-ui/web/entity"
|
||||
"x-ui/web/global"
|
||||
"x-ui/web/service"
|
||||
"x-ui/web/session"
|
||||
)
|
||||
|
||||
type XUIController struct {
|
||||
BaseController
|
||||
|
||||
inboundService service.InboundService
|
||||
xrayService service.XrayService
|
||||
settingService service.SettingService
|
||||
|
||||
isNeedXrayRestart atomic.Bool
|
||||
inboundController *InboundController
|
||||
settingController *SettingController
|
||||
}
|
||||
|
||||
func NewXUIController(g *gin.RouterGroup) *XUIController {
|
||||
a := &XUIController{}
|
||||
a.initRouter(g)
|
||||
a.startTask()
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -36,27 +23,10 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) {
|
||||
|
||||
g.GET("/", a.index)
|
||||
g.GET("/inbounds", a.inbounds)
|
||||
g.POST("/inbounds", a.getInbounds)
|
||||
g.POST("/inbound/add", a.addInbound)
|
||||
g.POST("/inbound/del/:id", a.delInbound)
|
||||
g.POST("/inbound/update/:id", a.updateInbound)
|
||||
g.GET("/setting", a.setting)
|
||||
g.POST("/setting/all", a.getAllSetting)
|
||||
g.POST("/setting/update", a.updateSetting)
|
||||
}
|
||||
|
||||
func (a *XUIController) startTask() {
|
||||
webServer := global.GetWebServer()
|
||||
c := webServer.GetCron()
|
||||
c.AddFunc("@every 10s", func() {
|
||||
if a.isNeedXrayRestart.Load() {
|
||||
err := a.xrayService.RestartXray()
|
||||
if err != nil {
|
||||
logger.Error("restart xray failed:", err)
|
||||
}
|
||||
a.isNeedXrayRestart.Store(false)
|
||||
}
|
||||
})
|
||||
a.inboundController = NewInboundController(g)
|
||||
a.settingController = NewSettingController(g)
|
||||
}
|
||||
|
||||
func (a *XUIController) index(c *gin.Context) {
|
||||
@@ -70,85 +40,3 @@ func (a *XUIController) inbounds(c *gin.Context) {
|
||||
func (a *XUIController) setting(c *gin.Context) {
|
||||
html(c, "setting.html", "设置", nil)
|
||||
}
|
||||
|
||||
func (a *XUIController) getInbounds(c *gin.Context) {
|
||||
user := session.GetLoginUser(c)
|
||||
inbounds, err := a.inboundService.GetInbounds(user.Id)
|
||||
if err != nil {
|
||||
jsonMsg(c, "获取", err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, inbounds, nil)
|
||||
}
|
||||
|
||||
func (a *XUIController) addInbound(c *gin.Context) {
|
||||
inbound := &model.Inbound{}
|
||||
err := c.ShouldBind(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, "添加", err)
|
||||
return
|
||||
}
|
||||
user := session.GetLoginUser(c)
|
||||
inbound.UserId = user.Id
|
||||
inbound.Enable = true
|
||||
log.Println(inbound)
|
||||
err = a.inboundService.AddInbound(inbound)
|
||||
jsonMsg(c, "添加", err)
|
||||
if err == nil {
|
||||
a.isNeedXrayRestart.Store(true)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *XUIController) delInbound(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
jsonMsg(c, "删除", err)
|
||||
return
|
||||
}
|
||||
err = a.inboundService.DelInbound(id)
|
||||
jsonMsg(c, "删除", err)
|
||||
if err == nil {
|
||||
a.isNeedXrayRestart.Store(true)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *XUIController) updateInbound(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
jsonMsg(c, "修改", err)
|
||||
return
|
||||
}
|
||||
inbound := &model.Inbound{
|
||||
Id: id,
|
||||
}
|
||||
err = c.ShouldBind(inbound)
|
||||
if err != nil {
|
||||
jsonMsg(c, "修改", err)
|
||||
return
|
||||
}
|
||||
err = a.inboundService.UpdateInbound(inbound)
|
||||
jsonMsg(c, "修改", err)
|
||||
if err == nil {
|
||||
a.isNeedXrayRestart.Store(true)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *XUIController) getAllSetting(c *gin.Context) {
|
||||
allSetting, err := a.settingService.GetAllSetting()
|
||||
if err != nil {
|
||||
jsonMsg(c, "获取设置", err)
|
||||
return
|
||||
}
|
||||
jsonObj(c, allSetting, nil)
|
||||
}
|
||||
|
||||
func (a *XUIController) updateSetting(c *gin.Context) {
|
||||
allSetting := &entity.AllSetting{}
|
||||
err := c.ShouldBind(allSetting)
|
||||
if err != nil {
|
||||
jsonMsg(c, "修改设置", err)
|
||||
return
|
||||
}
|
||||
err = a.settingService.UpdateAllSetting(allSetting)
|
||||
jsonMsg(c, "修改设置", err)
|
||||
}
|
||||
|
||||
@@ -60,6 +60,10 @@
|
||||
<template slot="protocol" slot-scope="text, dbInbound">
|
||||
<a-tag color="blue">[[ dbInbound.protocol ]]</a-tag>
|
||||
</template>
|
||||
<template slot="traffic" slot-scope="text, dbInbound">
|
||||
<a-tag color="blue">[[ sizeFormat(dbInbound.up) ]]</a-tag>
|
||||
<a-tag color="green">[[ sizeFormat(dbInbound.down) ]]</a-tag>
|
||||
</template>
|
||||
<template slot="settings" slot-scope="text, dbInbound">
|
||||
<a-button type="link">查看</a-button>
|
||||
</template>
|
||||
@@ -67,8 +71,7 @@
|
||||
<a-button type="link">查看</a-button>
|
||||
</template>
|
||||
<template slot="enable" slot-scope="text, dbInbound">
|
||||
<a-tag v-if="dbInbound.enable" color="green">启用</a-tag>
|
||||
<a-tag v-else color="red">禁用</a-tag>
|
||||
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound)"></a-switch>
|
||||
</template>
|
||||
<template slot="expiryTime" slot-scope="text, dbInbound">
|
||||
<span v-if="dbInbound.expiryTime > 0" color="red">[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]</span>
|
||||
@@ -104,6 +107,11 @@
|
||||
align: 'center',
|
||||
dataIndex: "port",
|
||||
width: 60,
|
||||
}, {
|
||||
title: "traffic",
|
||||
align: 'center',
|
||||
width: 60,
|
||||
scopedSlots: { customRender: 'traffic' },
|
||||
}, {
|
||||
title: "settings",
|
||||
align: 'center',
|
||||
@@ -146,7 +154,7 @@
|
||||
},
|
||||
async getDBInbounds() {
|
||||
this.loading();
|
||||
const msg = await HttpUtil.post('/xui/inbounds');
|
||||
const msg = await HttpUtil.post('/xui/inbound/list');
|
||||
this.loading(false);
|
||||
if (!msg.success) {
|
||||
return;
|
||||
@@ -247,9 +255,19 @@
|
||||
resetAllTraffic() {
|
||||
this.submit('/xui/reset_all_traffic');
|
||||
},
|
||||
setEnable(inbound, enable) {
|
||||
let data = {enable: enable};
|
||||
this.submit(`/xui/inbound/update/${inbound.id}`, data);
|
||||
switchEnable(dbInbound) {
|
||||
const data = {
|
||||
remark: dbInbound.remark,
|
||||
enable: dbInbound.enable,
|
||||
|
||||
listen: dbInbound.listen,
|
||||
port: dbInbound.port,
|
||||
protocol: dbInbound.protocol,
|
||||
settings: dbInbound.settings,
|
||||
stream_settings: dbInbound.stream,
|
||||
sniffing: dbInbound.sniffing,
|
||||
};
|
||||
this.submit(`/xui/inbound/update/${dbInbound.id}`, data);
|
||||
},
|
||||
async submit(url, data, modal) {
|
||||
const msg = await HttpUtil.postWithModal(url, data, modal);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
"x-ui/database"
|
||||
"x-ui/database/model"
|
||||
"x-ui/xray"
|
||||
)
|
||||
|
||||
type InboundService struct {
|
||||
@@ -63,7 +65,36 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) error {
|
||||
oldInbound.Settings = inbound.Settings
|
||||
oldInbound.StreamSettings = inbound.StreamSettings
|
||||
oldInbound.Sniffing = inbound.Sniffing
|
||||
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
|
||||
|
||||
db := database.GetDB()
|
||||
return db.Save(oldInbound).Error
|
||||
}
|
||||
|
||||
func (s *InboundService) AddTraffic(traffics []*xray.Traffic) (err error) {
|
||||
if len(traffics) == 0 {
|
||||
return nil
|
||||
}
|
||||
db := database.GetDB()
|
||||
db = db.Model(model.Inbound{})
|
||||
tx := db.Begin()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
} else {
|
||||
tx.Commit()
|
||||
}
|
||||
}()
|
||||
for _, traffic := range traffics {
|
||||
if traffic.IsInbound {
|
||||
err = tx.Where("tag = ?", traffic.Tag).
|
||||
UpdateColumn("up", gorm.Expr("up + ?", traffic.Up)).
|
||||
UpdateColumn("down", gorm.Expr("down + ?", traffic.Down)).
|
||||
Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ func (s *ServerService) UpdateXray(version string) error {
|
||||
|
||||
s.xrayService.StopXray()
|
||||
defer func() {
|
||||
err := s.xrayService.StartXray()
|
||||
err := s.xrayService.RestartXray()
|
||||
if err != nil {
|
||||
logger.Error("start xray failed:", err)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ package service
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"x-ui/util/common"
|
||||
"sync"
|
||||
"x-ui/xray"
|
||||
)
|
||||
|
||||
var p *xray.Process
|
||||
var lock sync.Mutex
|
||||
var result string
|
||||
|
||||
type XrayService struct {
|
||||
@@ -64,15 +65,27 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
for _, inbound := range inbounds {
|
||||
if !inbound.Enable {
|
||||
continue
|
||||
}
|
||||
inboundConfig := inbound.GenXrayInboundConfig()
|
||||
xrayConfig.InboundConfigs = append(xrayConfig.InboundConfigs, *inboundConfig)
|
||||
}
|
||||
return xrayConfig, nil
|
||||
}
|
||||
|
||||
func (s *XrayService) StartXray() error {
|
||||
if s.IsXrayRunning() {
|
||||
return nil
|
||||
func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, error) {
|
||||
if !s.IsXrayRunning() {
|
||||
return nil, errors.New("xray is not running")
|
||||
}
|
||||
return p.GetTraffic(true)
|
||||
}
|
||||
|
||||
func (s *XrayService) RestartXray() error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if p != nil {
|
||||
p.Stop()
|
||||
}
|
||||
|
||||
xrayConfig, err := s.GetXrayConfig()
|
||||
@@ -81,20 +94,15 @@ func (s *XrayService) StartXray() error {
|
||||
}
|
||||
|
||||
p = xray.NewProcess(xrayConfig)
|
||||
err = p.Start()
|
||||
result = ""
|
||||
return err
|
||||
return p.Start()
|
||||
}
|
||||
|
||||
func (s *XrayService) StopXray() error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if s.IsXrayRunning() {
|
||||
return p.Stop()
|
||||
}
|
||||
return errors.New("xray is not running")
|
||||
}
|
||||
|
||||
func (s *XrayService) RestartXray() error {
|
||||
err1 := s.StopXray()
|
||||
err2 := s.StartXray()
|
||||
return common.Combine(err1, err2)
|
||||
}
|
||||
|
||||
35
web/web.go
35
web/web.go
@@ -18,8 +18,10 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
"x-ui/config"
|
||||
"x-ui/logger"
|
||||
"x-ui/util"
|
||||
"x-ui/util/common"
|
||||
"x-ui/web/controller"
|
||||
"x-ui/web/service"
|
||||
@@ -51,6 +53,7 @@ type Server struct {
|
||||
|
||||
xrayService service.XrayService
|
||||
settingService service.SettingService
|
||||
inboundService service.InboundService
|
||||
|
||||
cron *cron.Cron
|
||||
|
||||
@@ -236,19 +239,43 @@ func (s *Server) initI18n(engine *gin.Engine) error {
|
||||
}
|
||||
|
||||
func (s *Server) startTask() {
|
||||
err := s.xrayService.StartXray()
|
||||
err := s.xrayService.RestartXray()
|
||||
if err != nil {
|
||||
logger.Warning("start xray failed:", err)
|
||||
}
|
||||
var checkTime = 0
|
||||
s.cron.AddFunc("@every 30s", func() {
|
||||
if s.xrayService.IsXrayRunning() {
|
||||
checkTime = 0
|
||||
return
|
||||
}
|
||||
err := s.xrayService.StartXray()
|
||||
checkTime++
|
||||
if checkTime < 2 {
|
||||
return
|
||||
}
|
||||
err := s.xrayService.RestartXray()
|
||||
if err != nil {
|
||||
logger.Warning("start xray failed:", err)
|
||||
}
|
||||
})
|
||||
go func() {
|
||||
time.Sleep(time.Second * 5)
|
||||
// 与重启 xray 的时间错开
|
||||
s.cron.AddFunc("@every 10s", func() {
|
||||
if !s.xrayService.IsXrayRunning() {
|
||||
return
|
||||
}
|
||||
traffics, err := s.xrayService.GetXrayTraffic()
|
||||
if err != nil {
|
||||
logger.Warning("get xray traffic failed:", err)
|
||||
return
|
||||
}
|
||||
err = s.inboundService.AddTraffic(traffics)
|
||||
if err != nil {
|
||||
logger.Warning("add traffic failed:", err)
|
||||
}
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Server) Start() (err error) {
|
||||
@@ -318,6 +345,10 @@ func (s *Server) Start() (err error) {
|
||||
}
|
||||
|
||||
func (s *Server) Stop() error {
|
||||
if util.IsDone(s.ctx) {
|
||||
// 防止 gc 后调用第二次 Stop
|
||||
s.xrayService.StopXray()
|
||||
}
|
||||
s.cancel()
|
||||
if s.cron != nil {
|
||||
s.cron.Stop()
|
||||
|
||||
Reference in New Issue
Block a user