mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-09-05 09:46:21 +00:00

checkPortExist Function: Simplified the query by combining the conditions into a single Where clause. Removed unnecessary variable assignment for db. checkEmailExistForInbound Function: Introduced a map seenEmails to track already seen emails, which eliminates the need for the contains function call for each client email, thus reducing the complexity from O(n^2) to O(n). UpdateInbound Function: Instead of manually assigning each field, a map updates is used to update only the changed fields in the database. This reduces the verbosity of the code and makes it easier to maintain. Removed the unnecessary API calls for adding and deleting inbounds, as the function should only update the database record. addClientTraffic Function: Streamlined the update process by directly updating the traffic records in the database using a loop, which avoids the need to first load all records into a slice and then adjust them.
1565 lines
38 KiB
Go
1565 lines
38 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
"x-ui/database"
|
|
"x-ui/database/model"
|
|
"x-ui/logger"
|
|
"x-ui/util/common"
|
|
"x-ui/xray"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type InboundService struct {
|
|
xrayApi xray.XrayAPI
|
|
}
|
|
|
|
func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
|
|
db := database.GetDB()
|
|
var inbounds []*model.Inbound
|
|
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Find(&inbounds).Error
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
return nil, err
|
|
}
|
|
return inbounds, nil
|
|
}
|
|
|
|
func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
|
|
db := database.GetDB()
|
|
var inbounds []*model.Inbound
|
|
err := db.Model(model.Inbound{}).Preload("ClientStats").Find(&inbounds).Error
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
return nil, err
|
|
}
|
|
return inbounds, nil
|
|
}
|
|
|
|
func (s *InboundService) checkPortExist(port int, ignoreId int) (bool, error) {
|
|
db := database.GetDB()
|
|
var count int64
|
|
err := db.Model(model.Inbound{}).Where("port = ? AND id <> ?", port, ignoreId).Count(&count).Error
|
|
return count > 0, err
|
|
}
|
|
|
|
func (s *InboundService) GetClients(inbound *model.Inbound) ([]model.Client, error) {
|
|
settings := map[string][]model.Client{}
|
|
json.Unmarshal([]byte(inbound.Settings), &settings)
|
|
if settings == nil {
|
|
return nil, fmt.Errorf("setting is null")
|
|
}
|
|
|
|
clients := settings["clients"]
|
|
if clients == nil {
|
|
return nil, nil
|
|
}
|
|
return clients, nil
|
|
}
|
|
|
|
func (s *InboundService) getAllEmails() ([]string, error) {
|
|
db := database.GetDB()
|
|
var emails []string
|
|
err := db.Raw(`
|
|
SELECT JSON_EXTRACT(client.value, '$.email')
|
|
FROM inbounds,
|
|
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
|
`).Scan(&emails).Error
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return emails, nil
|
|
}
|
|
|
|
func (s *InboundService) contains(slice []string, str string) bool {
|
|
for _, s := range slice {
|
|
if s == str {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *InboundService) checkEmailsExistForClients(clients []model.Client) (string, error) {
|
|
allEmails, err := s.getAllEmails()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
var emails []string
|
|
for _, client := range clients {
|
|
if client.Email != "" {
|
|
if s.contains(emails, client.Email) {
|
|
return client.Email, nil
|
|
}
|
|
if s.contains(allEmails, client.Email) {
|
|
return client.Email, nil
|
|
}
|
|
emails = append(emails, client.Email)
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (string, error) {
|
|
clients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
allEmails, err := s.getAllEmails()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
seenEmails := make(map[string]bool)
|
|
for _, client := range clients {
|
|
if client.Email != "" {
|
|
if seenEmails[client.Email] {
|
|
return client.Email, nil
|
|
}
|
|
if s.contains(allEmails, client.Email) {
|
|
return client.Email, nil
|
|
}
|
|
seenEmails[client.Email] = true
|
|
}
|
|
}
|
|
return "", nil
|
|
}
|
|
|
|
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
|
|
exist, err := s.checkPortExist(inbound.Port, 0)
|
|
if err != nil {
|
|
return inbound, false, err
|
|
}
|
|
if exist {
|
|
return inbound, false, common.NewError("Port already exists:", inbound.Port)
|
|
}
|
|
|
|
existEmail, err := s.checkEmailExistForInbound(inbound)
|
|
if err != nil {
|
|
return inbound, false, err
|
|
}
|
|
if existEmail != "" {
|
|
return inbound, false, common.NewError("Duplicate email:", existEmail)
|
|
}
|
|
|
|
clients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return inbound, false, err
|
|
}
|
|
|
|
db := database.GetDB()
|
|
tx := db.Begin()
|
|
defer func() {
|
|
if err == nil {
|
|
tx.Commit()
|
|
} else {
|
|
tx.Rollback()
|
|
}
|
|
}()
|
|
|
|
err = tx.Save(inbound).Error
|
|
if err == nil {
|
|
for _, client := range clients {
|
|
s.AddClientStat(tx, inbound.Id, &client)
|
|
}
|
|
}
|
|
|
|
needRestart := false
|
|
if inbound.Enable {
|
|
s.xrayApi.Init(p.GetAPIPort())
|
|
inboundJson, err1 := json.MarshalIndent(inbound.GenXrayInboundConfig(), "", " ")
|
|
if err1 != nil {
|
|
logger.Debug("Unable to marshal inbound config:", err1)
|
|
}
|
|
|
|
err1 = s.xrayApi.AddInbound(inboundJson)
|
|
if err1 == nil {
|
|
logger.Debug("New inbound added by api:", inbound.Tag)
|
|
} else {
|
|
logger.Debug("Unable to add inbound by api:", err1)
|
|
needRestart = true
|
|
}
|
|
s.xrayApi.Close()
|
|
}
|
|
|
|
return inbound, needRestart, err
|
|
}
|
|
|
|
func (s *InboundService) DelInbound(id int) (bool, error) {
|
|
db := database.GetDB()
|
|
|
|
var tag string
|
|
needRestart := false
|
|
result := db.Model(model.Inbound{}).Select("tag").Where("id = ? and enable = ?", id, true).First(&tag)
|
|
if result.Error == nil {
|
|
s.xrayApi.Init(p.GetAPIPort())
|
|
err1 := s.xrayApi.DelInbound(tag)
|
|
if err1 == nil {
|
|
logger.Debug("Inbound deleted by api:", tag)
|
|
} else {
|
|
logger.Debug("Unable to delete inbound by api:", err1)
|
|
needRestart = true
|
|
}
|
|
s.xrayApi.Close()
|
|
} else {
|
|
logger.Debug("No enabled inbound founded to removing by api", tag)
|
|
}
|
|
|
|
// Delete client traffics of inbounds
|
|
err := db.Where("inbound_id = ?", id).Delete(xray.ClientTraffic{}).Error
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
inbound, err := s.GetInbound(id)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
clients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
for _, client := range clients {
|
|
err := s.DelClientIPs(db, client.Email)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
return needRestart, db.Delete(model.Inbound{}, id).Error
|
|
}
|
|
|
|
func (s *InboundService) GetInbound(id int) (*model.Inbound, error) {
|
|
db := database.GetDB()
|
|
inbound := &model.Inbound{}
|
|
err := db.Model(model.Inbound{}).First(inbound, id).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return inbound, nil
|
|
}
|
|
|
|
func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
|
|
exist, err := s.checkPortExist(inbound.Port, inbound.Id)
|
|
if err != nil {
|
|
return inbound, false, err
|
|
}
|
|
if exist {
|
|
return inbound, false, common.NewError("Port already exists:", inbound.Port)
|
|
}
|
|
|
|
oldInbound, err := s.GetInbound(inbound.Id)
|
|
if err != nil {
|
|
return inbound, false, err
|
|
}
|
|
|
|
err = s.updateClientTraffics(oldInbound, inbound)
|
|
if err != nil {
|
|
return inbound, false, err
|
|
}
|
|
|
|
// Update only changed fields
|
|
updates := map[string]interface{}{
|
|
"Up": inbound.Up,
|
|
"Down": inbound.Down,
|
|
"Total": inbound.Total,
|
|
"Remark": inbound.Remark,
|
|
"Enable": inbound.Enable,
|
|
"ExpiryTime": inbound.ExpiryTime,
|
|
"Listen": inbound.Listen,
|
|
"Port": inbound.Port,
|
|
"Protocol": inbound.Protocol,
|
|
"Settings": inbound.Settings,
|
|
"StreamSettings": inbound.StreamSettings,
|
|
"Sniffing": inbound.Sniffing,
|
|
"Tag": fmt.Sprintf("inbound-%v", inbound.Port),
|
|
}
|
|
|
|
db := database.GetDB()
|
|
return inbound, false, db.Model(oldInbound).Updates(updates).Error
|
|
}
|
|
|
|
func (s *InboundService) updateClientTraffics(oldInbound *model.Inbound, newInbound *model.Inbound) error {
|
|
oldClients, err := s.GetClients(oldInbound)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newClients, err := s.GetClients(newInbound)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
db := database.GetDB()
|
|
tx := db.Begin()
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
tx.Rollback()
|
|
} else {
|
|
tx.Commit()
|
|
}
|
|
}()
|
|
|
|
var emailExists bool
|
|
|
|
for _, oldClient := range oldClients {
|
|
emailExists = false
|
|
for _, newClient := range newClients {
|
|
if oldClient.Email == newClient.Email {
|
|
emailExists = true
|
|
break
|
|
}
|
|
}
|
|
if !emailExists {
|
|
err = s.DelClientStat(tx, oldClient.Email)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
for _, newClient := range newClients {
|
|
emailExists = false
|
|
for _, oldClient := range oldClients {
|
|
if newClient.Email == oldClient.Email {
|
|
emailExists = true
|
|
break
|
|
}
|
|
}
|
|
if !emailExists {
|
|
err = s.AddClientStat(tx, oldInbound.Id, &newClient)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
|
|
clients, err := s.GetClients(data)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
var settings map[string]interface{}
|
|
err = json.Unmarshal([]byte(data.Settings), &settings)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
interfaceClients := settings["clients"].([]interface{})
|
|
existEmail, err := s.checkEmailsExistForClients(clients)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if existEmail != "" {
|
|
return false, common.NewError("Duplicate email:", existEmail)
|
|
}
|
|
|
|
oldInbound, err := s.GetInbound(data.Id)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
var oldSettings map[string]interface{}
|
|
err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
oldClients := oldSettings["clients"].([]interface{})
|
|
oldClients = append(oldClients, interfaceClients...)
|
|
|
|
oldSettings["clients"] = oldClients
|
|
|
|
newSettings, err := json.MarshalIndent(oldSettings, "", " ")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
oldInbound.Settings = string(newSettings)
|
|
|
|
db := database.GetDB()
|
|
tx := db.Begin()
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
tx.Rollback()
|
|
} else {
|
|
tx.Commit()
|
|
}
|
|
}()
|
|
|
|
needRestart := false
|
|
s.xrayApi.Init(p.GetAPIPort())
|
|
for _, client := range clients {
|
|
if len(client.Email) > 0 {
|
|
s.AddClientStat(tx, data.Id, &client)
|
|
if client.Enable {
|
|
cipher := ""
|
|
if oldInbound.Protocol == "shadowsocks" {
|
|
cipher = oldSettings["method"].(string)
|
|
}
|
|
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
|
|
"email": client.Email,
|
|
"id": client.ID,
|
|
"flow": client.Flow,
|
|
"password": client.Password,
|
|
"cipher": cipher,
|
|
})
|
|
if err1 == nil {
|
|
logger.Debug("Client added by api:", client.Email)
|
|
} else {
|
|
logger.Debug("Error in adding client by api:", err1)
|
|
needRestart = true
|
|
}
|
|
}
|
|
} else {
|
|
needRestart = true
|
|
}
|
|
}
|
|
s.xrayApi.Close()
|
|
|
|
return needRestart, tx.Save(oldInbound).Error
|
|
}
|
|
|
|
func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool, error) {
|
|
oldInbound, err := s.GetInbound(inboundId)
|
|
if err != nil {
|
|
logger.Error("Load Old Data Error")
|
|
return false, err
|
|
}
|
|
var settings map[string]interface{}
|
|
err = json.Unmarshal([]byte(oldInbound.Settings), &settings)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
email := ""
|
|
client_key := "id"
|
|
if oldInbound.Protocol == "trojan" {
|
|
client_key = "password"
|
|
}
|
|
if oldInbound.Protocol == "shadowsocks" {
|
|
client_key = "email"
|
|
}
|
|
|
|
inerfaceClients := settings["clients"].([]interface{})
|
|
var newClients []interface{}
|
|
for _, client := range inerfaceClients {
|
|
c := client.(map[string]interface{})
|
|
c_id := c[client_key].(string)
|
|
if c_id == clientId {
|
|
email = c["email"].(string)
|
|
} else {
|
|
newClients = append(newClients, client)
|
|
}
|
|
}
|
|
|
|
settings["clients"] = newClients
|
|
newSettings, err := json.MarshalIndent(settings, "", " ")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
oldInbound.Settings = string(newSettings)
|
|
|
|
db := database.GetDB()
|
|
err = s.DelClientStat(db, email)
|
|
if err != nil {
|
|
logger.Error("Delete stats Data Error")
|
|
return false, err
|
|
}
|
|
|
|
err = s.DelClientIPs(db, email)
|
|
if err != nil {
|
|
logger.Error("Error in delete client IPs")
|
|
return false, err
|
|
}
|
|
needRestart := false
|
|
if len(email) > 0 {
|
|
s.xrayApi.Init(p.GetAPIPort())
|
|
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email)
|
|
if err1 == nil {
|
|
logger.Debug("Client deleted by api:", email)
|
|
needRestart = false
|
|
} else {
|
|
logger.Debug("Unable to del client by api:", err1)
|
|
needRestart = true
|
|
}
|
|
s.xrayApi.Close()
|
|
}
|
|
return needRestart, db.Save(oldInbound).Error
|
|
}
|
|
|
|
func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId string) (bool, error) {
|
|
clients, err := s.GetClients(data)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
var settings map[string]interface{}
|
|
err = json.Unmarshal([]byte(data.Settings), &settings)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
inerfaceClients := settings["clients"].([]interface{})
|
|
|
|
oldInbound, err := s.GetInbound(data.Id)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
oldClients, err := s.GetClients(oldInbound)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
oldEmail := ""
|
|
clientIndex := 0
|
|
for index, oldClient := range oldClients {
|
|
oldClientId := ""
|
|
if oldInbound.Protocol == "trojan" {
|
|
oldClientId = oldClient.Password
|
|
} else if oldInbound.Protocol == "shadowsocks" {
|
|
oldClientId = oldClient.Email
|
|
} else {
|
|
oldClientId = oldClient.ID
|
|
}
|
|
if clientId == oldClientId {
|
|
oldEmail = oldClient.Email
|
|
clientIndex = index
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(clients[0].Email) > 0 && clients[0].Email != oldEmail {
|
|
existEmail, err := s.checkEmailsExistForClients(clients)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if existEmail != "" {
|
|
return false, common.NewError("Duplicate email:", existEmail)
|
|
}
|
|
}
|
|
|
|
var oldSettings map[string]interface{}
|
|
err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
settingsClients := oldSettings["clients"].([]interface{})
|
|
settingsClients[clientIndex] = inerfaceClients[0]
|
|
oldSettings["clients"] = settingsClients
|
|
|
|
newSettings, err := json.MarshalIndent(oldSettings, "", " ")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
oldInbound.Settings = string(newSettings)
|
|
db := database.GetDB()
|
|
tx := db.Begin()
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
tx.Rollback()
|
|
} else {
|
|
tx.Commit()
|
|
}
|
|
}()
|
|
|
|
if len(clients[0].Email) > 0 {
|
|
if len(oldEmail) > 0 {
|
|
err = s.UpdateClientStat(oldEmail, &clients[0])
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
err = s.UpdateClientIPs(tx, oldEmail, clients[0].Email)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
} else {
|
|
s.AddClientStat(tx, data.Id, &clients[0])
|
|
}
|
|
} else {
|
|
err = s.DelClientStat(tx, oldEmail)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
err = s.DelClientIPs(tx, oldEmail)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
needRestart := false
|
|
if len(oldEmail) > 0 {
|
|
s.xrayApi.Init(p.GetAPIPort())
|
|
if s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail) == nil {
|
|
logger.Debug("Old client deleted by api:", clients[0].Email)
|
|
}
|
|
if clients[0].Enable {
|
|
cipher := ""
|
|
if oldInbound.Protocol == "shadowsocks" {
|
|
cipher = oldSettings["method"].(string)
|
|
}
|
|
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
|
|
"email": clients[0].Email,
|
|
"id": clients[0].ID,
|
|
"flow": clients[0].Flow,
|
|
"password": clients[0].Password,
|
|
"cipher": cipher,
|
|
})
|
|
if err1 == nil {
|
|
logger.Debug("Client edited by api:", clients[0].Email)
|
|
} else {
|
|
logger.Debug("Error in adding client by api:", err1)
|
|
needRestart = true
|
|
}
|
|
}
|
|
s.xrayApi.Close()
|
|
} else {
|
|
logger.Debug("Client old email not found")
|
|
needRestart = true
|
|
}
|
|
return needRestart, tx.Save(oldInbound).Error
|
|
}
|
|
|
|
func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) {
|
|
var err error
|
|
db := database.GetDB()
|
|
tx := db.Begin()
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
tx.Rollback()
|
|
} else {
|
|
tx.Commit()
|
|
}
|
|
}()
|
|
err = s.addInboundTraffic(tx, inboundTraffics)
|
|
if err != nil {
|
|
return err, false
|
|
}
|
|
err = s.addClientTraffic(tx, clientTraffics)
|
|
if err != nil {
|
|
return err, false
|
|
}
|
|
|
|
needRestart1, count, err := s.disableInvalidClients(tx)
|
|
if err != nil {
|
|
logger.Warning("Error in disabling invalid clients:", err)
|
|
} else if count > 0 {
|
|
logger.Debugf("%v clients disabled", count)
|
|
}
|
|
|
|
needRestart2, count, err := s.disableInvalidInbounds(tx)
|
|
if err != nil {
|
|
logger.Warning("Error in disabling invalid inbounds:", err)
|
|
} else if count > 0 {
|
|
logger.Debugf("%v inbounds disabled", count)
|
|
}
|
|
return nil, (needRestart1 || needRestart2)
|
|
}
|
|
|
|
func (s *InboundService) addInboundTraffic(tx *gorm.DB, traffics []*xray.Traffic) error {
|
|
if len(traffics) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
|
|
for _, traffic := range traffics {
|
|
if traffic.IsInbound {
|
|
err = tx.Model(&model.Inbound{}).Where("tag = ?", traffic.Tag).
|
|
Updates(map[string]interface{}{
|
|
"up": gorm.Expr("up + ?", traffic.Up),
|
|
"down": gorm.Expr("down + ?", traffic.Down),
|
|
}).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) addClientTraffic(tx *gorm.DB, traffics []*xray.ClientTraffic) error {
|
|
if len(traffics) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for _, traffic := range traffics {
|
|
err := tx.Model(&xray.ClientTraffic{}).Where("email = ?", traffic.Email).
|
|
Updates(map[string]interface{}{
|
|
"up": gorm.Expr("up + ?", traffic.Up),
|
|
"down": gorm.Expr("down + ?", traffic.Down),
|
|
}).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) adjustTraffics(tx *gorm.DB, dbClientTraffics []*xray.ClientTraffic) ([]*xray.ClientTraffic, error) {
|
|
inboundIds := make([]int, 0, len(dbClientTraffics))
|
|
for _, dbClientTraffic := range dbClientTraffics {
|
|
if dbClientTraffic.ExpiryTime < 0 {
|
|
inboundIds = append(inboundIds, dbClientTraffic.InboundId)
|
|
}
|
|
}
|
|
|
|
if len(inboundIds) > 0 {
|
|
var inbounds []*model.Inbound
|
|
err := tx.Model(model.Inbound{}).Where("id IN (?)", inboundIds).Find(&inbounds).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for inbound_index := range inbounds {
|
|
settings := map[string]interface{}{}
|
|
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
|
|
clients, ok := settings["clients"].([]interface{})
|
|
if ok {
|
|
var newClients []interface{}
|
|
for client_index := range clients {
|
|
c := clients[client_index].(map[string]interface{})
|
|
for traffic_index := range dbClientTraffics {
|
|
if dbClientTraffics[traffic_index].ExpiryTime < 0 && c["email"] == dbClientTraffics[traffic_index].Email {
|
|
oldExpiryTime := c["expiryTime"].(float64)
|
|
newExpiryTime := (time.Now().Unix() * 1000) - int64(oldExpiryTime)
|
|
c["expiryTime"] = newExpiryTime
|
|
dbClientTraffics[traffic_index].ExpiryTime = newExpiryTime
|
|
break
|
|
}
|
|
}
|
|
newClients = append(newClients, interface{}(c))
|
|
}
|
|
settings["clients"] = newClients
|
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
inbounds[inbound_index].Settings = string(modifiedSettings)
|
|
}
|
|
}
|
|
err = tx.Save(inbounds).Error
|
|
if err != nil {
|
|
logger.Warning("AddClientTraffic update inbounds ", err)
|
|
logger.Error(inbounds)
|
|
}
|
|
}
|
|
|
|
return dbClientTraffics, nil
|
|
}
|
|
|
|
func (s *InboundService) disableInvalidInbounds(tx *gorm.DB) (bool, int64, error) {
|
|
now := time.Now().Unix() * 1000
|
|
needRestart := false
|
|
|
|
if p != nil {
|
|
var tags []string
|
|
err := tx.Table("inbounds").
|
|
Select("inbounds.tag").
|
|
Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true).
|
|
Scan(&tags).Error
|
|
if err != nil {
|
|
return false, 0, err
|
|
}
|
|
s.xrayApi.Init(p.GetAPIPort())
|
|
for _, tag := range tags {
|
|
err1 := s.xrayApi.DelInbound(tag)
|
|
if err == nil {
|
|
logger.Debug("Inbound disabled by api:", tag)
|
|
} else {
|
|
logger.Debug("Error in disabling inbound by api:", err1)
|
|
needRestart = true
|
|
}
|
|
}
|
|
s.xrayApi.Close()
|
|
}
|
|
|
|
result := tx.Model(model.Inbound{}).
|
|
Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true).
|
|
Update("enable", false)
|
|
err := result.Error
|
|
count := result.RowsAffected
|
|
return needRestart, count, err
|
|
}
|
|
|
|
func (s *InboundService) disableInvalidClients(tx *gorm.DB) (bool, int64, error) {
|
|
now := time.Now().Unix() * 1000
|
|
needRestart := false
|
|
|
|
if p != nil {
|
|
var results []struct {
|
|
Tag string
|
|
Email string
|
|
}
|
|
|
|
err := tx.Table("inbounds").
|
|
Select("inbounds.tag, client_traffics.email").
|
|
Joins("JOIN client_traffics ON inbounds.id = client_traffics.inbound_id").
|
|
Where("((client_traffics.total > 0 AND client_traffics.up + client_traffics.down >= client_traffics.total) OR (client_traffics.expiry_time > 0 AND client_traffics.expiry_time <= ?)) AND client_traffics.enable = ?", now, true).
|
|
Scan(&results).Error
|
|
if err != nil {
|
|
return false, 0, err
|
|
}
|
|
s.xrayApi.Init(p.GetAPIPort())
|
|
for _, result := range results {
|
|
err1 := s.xrayApi.RemoveUser(result.Tag, result.Email)
|
|
if err1 == nil {
|
|
logger.Debug("Client disabled by api:", result.Email)
|
|
} else {
|
|
logger.Debug("Error in disabling client by api:", err1)
|
|
needRestart = true
|
|
}
|
|
}
|
|
s.xrayApi.Close()
|
|
}
|
|
result := tx.Model(xray.ClientTraffic{}).
|
|
Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true).
|
|
Update("enable", false)
|
|
err := result.Error
|
|
count := result.RowsAffected
|
|
return needRestart, count, err
|
|
}
|
|
|
|
func (s *InboundService) MigrationRemoveOrphanedTraffics() {
|
|
db := database.GetDB()
|
|
db.Exec(`
|
|
DELETE FROM client_traffics
|
|
WHERE email NOT IN (
|
|
SELECT JSON_EXTRACT(client.value, '$.email')
|
|
FROM inbounds,
|
|
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
|
)
|
|
`)
|
|
}
|
|
|
|
func (s *InboundService) AddClientStat(tx *gorm.DB, inboundId int, client *model.Client) error {
|
|
clientTraffic := xray.ClientTraffic{}
|
|
clientTraffic.InboundId = inboundId
|
|
clientTraffic.Email = client.Email
|
|
clientTraffic.Total = client.TotalGB
|
|
clientTraffic.ExpiryTime = client.ExpiryTime
|
|
clientTraffic.Enable = true
|
|
clientTraffic.Up = 0
|
|
clientTraffic.Down = 0
|
|
result := tx.Create(&clientTraffic)
|
|
err := result.Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) UpdateClientStat(email string, client *model.Client) error {
|
|
db := database.GetDB()
|
|
|
|
result := db.Model(xray.ClientTraffic{}).
|
|
Where("email = ?", email).
|
|
Updates(map[string]interface{}{
|
|
"enable": true,
|
|
"email": client.Email,
|
|
"total": client.TotalGB,
|
|
"expiry_time": client.ExpiryTime})
|
|
err := result.Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) UpdateClientIPs(tx *gorm.DB, oldEmail string, newEmail string) error {
|
|
return tx.Model(model.InboundClientIps{}).Where("client_email = ?", oldEmail).Update("client_email", newEmail).Error
|
|
}
|
|
|
|
func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error {
|
|
return tx.Where("email = ?", email).Delete(xray.ClientTraffic{}).Error
|
|
}
|
|
|
|
func (s *InboundService) DelClientIPs(tx *gorm.DB, email string) error {
|
|
logger.Warning(email)
|
|
return tx.Where("client_email = ?", email).Delete(model.InboundClientIps{}).Error
|
|
}
|
|
|
|
func (s *InboundService) GetClientInboundByTrafficID(trafficId int) (traffic *xray.ClientTraffic, inbound *model.Inbound, err error) {
|
|
db := database.GetDB()
|
|
var traffics []*xray.ClientTraffic
|
|
err = db.Model(xray.ClientTraffic{}).Where("id = ?", trafficId).Find(&traffics).Error
|
|
if err != nil {
|
|
logger.Warning(err)
|
|
return nil, nil, err
|
|
}
|
|
if len(traffics) > 0 {
|
|
inbound, err = s.GetInbound(traffics[0].InboundId)
|
|
return traffics[0], inbound, err
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (s *InboundService) GetClientInboundByEmail(email string) (traffic *xray.ClientTraffic, inbound *model.Inbound, err error) {
|
|
db := database.GetDB()
|
|
var traffics []*xray.ClientTraffic
|
|
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
|
if err != nil {
|
|
logger.Warning(err)
|
|
return nil, nil, err
|
|
}
|
|
if len(traffics) > 0 {
|
|
inbound, err = s.GetInbound(traffics[0].InboundId)
|
|
return traffics[0], inbound, err
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (s *InboundService) GetClientByEmail(clientEmail string) (*xray.ClientTraffic, *model.Client, error) {
|
|
traffic, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if inbound == nil {
|
|
return nil, nil, common.NewError("Inbound Not Found For Email:", clientEmail)
|
|
}
|
|
|
|
clients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
for _, client := range clients {
|
|
if client.Email == clientEmail {
|
|
return traffic, &client, nil
|
|
}
|
|
}
|
|
|
|
return nil, nil, common.NewError("Client Not Found In Inbound For Email:", clientEmail)
|
|
}
|
|
|
|
func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) error {
|
|
traffic, inbound, err := s.GetClientInboundByTrafficID(trafficId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if inbound == nil {
|
|
return common.NewError("Inbound Not Found For Traffic ID:", trafficId)
|
|
}
|
|
|
|
clientEmail := traffic.Email
|
|
|
|
oldClients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientId := ""
|
|
|
|
for _, oldClient := range oldClients {
|
|
if oldClient.Email == clientEmail {
|
|
if inbound.Protocol == "trojan" {
|
|
clientId = oldClient.Password
|
|
} else {
|
|
clientId = oldClient.ID
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(clientId) == 0 {
|
|
return common.NewError("Client Not Found For Email:", clientEmail)
|
|
}
|
|
|
|
var settings map[string]interface{}
|
|
err = json.Unmarshal([]byte(inbound.Settings), &settings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
clients := settings["clients"].([]interface{})
|
|
var newClients []interface{}
|
|
for client_index := range clients {
|
|
c := clients[client_index].(map[string]interface{})
|
|
if c["email"] == clientEmail {
|
|
c["tgId"] = tgId
|
|
newClients = append(newClients, interface{}(c))
|
|
}
|
|
}
|
|
settings["clients"] = newClients
|
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
inbound.Settings = string(modifiedSettings)
|
|
_, err = s.UpdateInboundClient(inbound, clientId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, error) {
|
|
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if inbound == nil {
|
|
return false, common.NewError("Inbound Not Found For Email:", clientEmail)
|
|
}
|
|
|
|
oldClients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
clientId := ""
|
|
clientOldEnabled := false
|
|
|
|
for _, oldClient := range oldClients {
|
|
if oldClient.Email == clientEmail {
|
|
if inbound.Protocol == "trojan" {
|
|
clientId = oldClient.Password
|
|
} else {
|
|
clientId = oldClient.ID
|
|
}
|
|
clientOldEnabled = oldClient.Enable
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(clientId) == 0 {
|
|
return false, common.NewError("Client Not Found For Email:", clientEmail)
|
|
}
|
|
|
|
var settings map[string]interface{}
|
|
err = json.Unmarshal([]byte(inbound.Settings), &settings)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
clients := settings["clients"].([]interface{})
|
|
var newClients []interface{}
|
|
for client_index := range clients {
|
|
c := clients[client_index].(map[string]interface{})
|
|
if c["email"] == clientEmail {
|
|
c["enable"] = !clientOldEnabled
|
|
newClients = append(newClients, interface{}(c))
|
|
}
|
|
}
|
|
settings["clients"] = newClients
|
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
inbound.Settings = string(modifiedSettings)
|
|
|
|
_, err = s.UpdateInboundClient(inbound, clientId)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return !clientOldEnabled, nil
|
|
}
|
|
|
|
func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int) error {
|
|
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if inbound == nil {
|
|
return common.NewError("Inbound Not Found For Email:", clientEmail)
|
|
}
|
|
|
|
oldClients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientId := ""
|
|
|
|
for _, oldClient := range oldClients {
|
|
if oldClient.Email == clientEmail {
|
|
if inbound.Protocol == "trojan" {
|
|
clientId = oldClient.Password
|
|
} else {
|
|
clientId = oldClient.ID
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(clientId) == 0 {
|
|
return common.NewError("Client Not Found For Email:", clientEmail)
|
|
}
|
|
|
|
var settings map[string]interface{}
|
|
err = json.Unmarshal([]byte(inbound.Settings), &settings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
clients := settings["clients"].([]interface{})
|
|
var newClients []interface{}
|
|
for client_index := range clients {
|
|
c := clients[client_index].(map[string]interface{})
|
|
if c["email"] == clientEmail {
|
|
c["limitIp"] = count
|
|
newClients = append(newClients, interface{}(c))
|
|
}
|
|
}
|
|
settings["clients"] = newClients
|
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
inbound.Settings = string(modifiedSettings)
|
|
_, err = s.UpdateInboundClient(inbound, clientId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry_time int64) error {
|
|
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if inbound == nil {
|
|
return common.NewError("Inbound Not Found For Email:", clientEmail)
|
|
}
|
|
|
|
oldClients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientId := ""
|
|
|
|
for _, oldClient := range oldClients {
|
|
if oldClient.Email == clientEmail {
|
|
if inbound.Protocol == "trojan" {
|
|
clientId = oldClient.Password
|
|
} else {
|
|
clientId = oldClient.ID
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(clientId) == 0 {
|
|
return common.NewError("Client Not Found For Email:", clientEmail)
|
|
}
|
|
|
|
var settings map[string]interface{}
|
|
err = json.Unmarshal([]byte(inbound.Settings), &settings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
clients := settings["clients"].([]interface{})
|
|
var newClients []interface{}
|
|
for client_index := range clients {
|
|
c := clients[client_index].(map[string]interface{})
|
|
if c["email"] == clientEmail {
|
|
c["expiryTime"] = expiry_time
|
|
newClients = append(newClients, interface{}(c))
|
|
}
|
|
}
|
|
settings["clients"] = newClients
|
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
inbound.Settings = string(modifiedSettings)
|
|
_, err = s.UpdateInboundClient(inbound, clientId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
|
|
db := database.GetDB()
|
|
|
|
result := db.Model(xray.ClientTraffic{}).
|
|
Where("email = ?", clientEmail).
|
|
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
|
|
|
err := result.Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, error) {
|
|
needRestart := false
|
|
|
|
traffic, err := s.GetClientTrafficByEmail(clientEmail)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if !traffic.Enable {
|
|
inbound, err := s.GetInbound(id)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
clients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
for _, client := range clients {
|
|
if client.Email == clientEmail {
|
|
s.xrayApi.Init(p.GetAPIPort())
|
|
cipher := ""
|
|
if string(inbound.Protocol) == "shadowsocks" {
|
|
var oldSettings map[string]interface{}
|
|
err = json.Unmarshal([]byte(inbound.Settings), &oldSettings)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
cipher = oldSettings["method"].(string)
|
|
}
|
|
err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{
|
|
"email": client.Email,
|
|
"id": client.ID,
|
|
"flow": client.Flow,
|
|
"password": client.Password,
|
|
"cipher": cipher,
|
|
})
|
|
if err1 == nil {
|
|
logger.Debug("Client enabled due to reset traffic:", clientEmail)
|
|
} else {
|
|
logger.Debug("Error in enabling client by api:", err1)
|
|
needRestart = true
|
|
}
|
|
s.xrayApi.Close()
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
traffic.Up = 0
|
|
traffic.Down = 0
|
|
traffic.Enable = true
|
|
|
|
db := database.GetDB()
|
|
err = db.Save(traffic).Error
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return needRestart, nil
|
|
}
|
|
|
|
func (s *InboundService) ResetAllClientTraffics(id int) error {
|
|
db := database.GetDB()
|
|
|
|
whereText := "inbound_id "
|
|
if id == -1 {
|
|
whereText += " > ?"
|
|
} else {
|
|
whereText += " = ?"
|
|
}
|
|
|
|
result := db.Model(xray.ClientTraffic{}).
|
|
Where(whereText, id).
|
|
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
|
|
|
|
err := result.Error
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) ResetAllTraffics() error {
|
|
db := database.GetDB()
|
|
|
|
result := db.Model(model.Inbound{}).
|
|
Where("user_id > ?", 0).
|
|
Updates(map[string]interface{}{"up": 0, "down": 0})
|
|
|
|
err := result.Error
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) DelDepletedClients(id int) (err error) {
|
|
db := database.GetDB()
|
|
tx := db.Begin()
|
|
defer func() {
|
|
if err == nil {
|
|
tx.Commit()
|
|
} else {
|
|
tx.Rollback()
|
|
}
|
|
}()
|
|
|
|
whereText := "inbound_id "
|
|
if id < 0 {
|
|
whereText += "> ?"
|
|
} else {
|
|
whereText += "= ?"
|
|
}
|
|
|
|
depletedClients := []xray.ClientTraffic{}
|
|
err = db.Model(xray.ClientTraffic{}).Where(whereText+" and enable = ?", id, false).Select("inbound_id, GROUP_CONCAT(email) as email").Group("inbound_id").Find(&depletedClients).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, depletedClient := range depletedClients {
|
|
emails := strings.Split(depletedClient.Email, ",")
|
|
oldInbound, err := s.GetInbound(depletedClient.InboundId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var oldSettings map[string]interface{}
|
|
err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
oldClients := oldSettings["clients"].([]interface{})
|
|
var newClients []interface{}
|
|
for _, client := range oldClients {
|
|
deplete := false
|
|
c := client.(map[string]interface{})
|
|
for _, email := range emails {
|
|
if email == c["email"].(string) {
|
|
deplete = true
|
|
break
|
|
}
|
|
}
|
|
if !deplete {
|
|
newClients = append(newClients, client)
|
|
}
|
|
}
|
|
if len(newClients) > 0 {
|
|
oldSettings["clients"] = newClients
|
|
|
|
newSettings, err := json.MarshalIndent(oldSettings, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
oldInbound.Settings = string(newSettings)
|
|
err = tx.Save(oldInbound).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
// Delete inbound if no client remains
|
|
s.DelInbound(depletedClient.InboundId)
|
|
}
|
|
}
|
|
|
|
err = tx.Where(whereText+" and enable = ?", id, false).Delete(xray.ClientTraffic{}).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTraffic, error) {
|
|
db := database.GetDB()
|
|
var inbounds []*model.Inbound
|
|
err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tguname)).Find(&inbounds).Error
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
return nil, err
|
|
}
|
|
var emails []string
|
|
for _, inbound := range inbounds {
|
|
clients, err := s.GetClients(inbound)
|
|
if err != nil {
|
|
logger.Error("Unable to get clients from inbound")
|
|
}
|
|
for _, client := range clients {
|
|
if client.TgID == tguname {
|
|
emails = append(emails, client.Email)
|
|
}
|
|
}
|
|
}
|
|
var traffics []*xray.ClientTraffic
|
|
err = db.Model(xray.ClientTraffic{}).Where("email IN ?", emails).Find(&traffics).Error
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
logger.Warning(err)
|
|
return nil, err
|
|
}
|
|
}
|
|
return traffics, err
|
|
}
|
|
|
|
func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.ClientTraffic, err error) {
|
|
db := database.GetDB()
|
|
var traffics []*xray.ClientTraffic
|
|
|
|
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
|
if err != nil {
|
|
logger.Warning(err)
|
|
return nil, err
|
|
}
|
|
if len(traffics) > 0 {
|
|
return traffics[0], nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.ClientTraffic, err error) {
|
|
db := database.GetDB()
|
|
inbound := &model.Inbound{}
|
|
traffic = &xray.ClientTraffic{}
|
|
|
|
err = db.Model(model.Inbound{}).Where("settings like ?", "%\""+query+"\"%").First(inbound).Error
|
|
if err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
logger.Warning(err)
|
|
return nil, err
|
|
}
|
|
}
|
|
traffic.InboundId = inbound.Id
|
|
|
|
// get settings clients
|
|
settings := map[string][]model.Client{}
|
|
json.Unmarshal([]byte(inbound.Settings), &settings)
|
|
clients := settings["clients"]
|
|
for _, client := range clients {
|
|
if client.ID == query && client.Email != "" {
|
|
traffic.Email = client.Email
|
|
break
|
|
}
|
|
if client.Password == query && client.Email != "" {
|
|
traffic.Email = client.Email
|
|
break
|
|
}
|
|
}
|
|
if traffic.Email == "" {
|
|
return nil, err
|
|
}
|
|
err = db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(traffic).Error
|
|
if err != nil {
|
|
logger.Warning(err)
|
|
return nil, err
|
|
}
|
|
return traffic, err
|
|
}
|
|
|
|
func (s *InboundService) GetInboundClientIps(clientEmail string) (string, error) {
|
|
db := database.GetDB()
|
|
InboundClientIps := &model.InboundClientIps{}
|
|
err := db.Model(model.InboundClientIps{}).Where("client_email = ?", clientEmail).First(InboundClientIps).Error
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return InboundClientIps.Ips, nil
|
|
}
|
|
|
|
func (s *InboundService) ClearClientIps(clientEmail string) error {
|
|
db := database.GetDB()
|
|
|
|
result := db.Model(model.InboundClientIps{}).
|
|
Where("client_email = ?", clientEmail).
|
|
Update("ips", "")
|
|
err := result.Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error) {
|
|
db := database.GetDB()
|
|
var inbounds []*model.Inbound
|
|
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("remark like ?", "%"+query+"%").Find(&inbounds).Error
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
return nil, err
|
|
}
|
|
return inbounds, nil
|
|
}
|
|
|
|
func (s *InboundService) MigrationRequirements() {
|
|
db := database.GetDB()
|
|
tx := db.Begin()
|
|
var err error
|
|
defer func() {
|
|
if err == nil {
|
|
tx.Commit()
|
|
} else {
|
|
tx.Rollback()
|
|
}
|
|
}()
|
|
|
|
// Fix inbounds based problems
|
|
var inbounds []*model.Inbound
|
|
err = tx.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan"}).Find(&inbounds).Error
|
|
if err != nil && err != gorm.ErrRecordNotFound {
|
|
return
|
|
}
|
|
for inbound_index := range inbounds {
|
|
settings := map[string]interface{}{}
|
|
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
|
|
clients, ok := settings["clients"].([]interface{})
|
|
if ok {
|
|
// Fix Clinet configuration problems
|
|
var newClients []interface{}
|
|
for client_index := range clients {
|
|
c := clients[client_index].(map[string]interface{})
|
|
|
|
// Add email='' if it is not exists
|
|
if _, ok := c["email"]; !ok {
|
|
c["email"] = ""
|
|
}
|
|
|
|
// Remove "flow": "xtls-rprx-direct"
|
|
if _, ok := c["flow"]; ok {
|
|
if c["flow"] == "xtls-rprx-direct" {
|
|
c["flow"] = ""
|
|
}
|
|
}
|
|
newClients = append(newClients, interface{}(c))
|
|
}
|
|
settings["clients"] = newClients
|
|
modifiedSettings, err := json.MarshalIndent(settings, "", " ")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
inbounds[inbound_index].Settings = string(modifiedSettings)
|
|
}
|
|
|
|
// Add client traffic row for all clients which has email
|
|
modelClients, err := s.GetClients(inbounds[inbound_index])
|
|
if err != nil {
|
|
return
|
|
}
|
|
for _, modelClient := range modelClients {
|
|
if len(modelClient.Email) > 0 {
|
|
var count int64
|
|
tx.Model(xray.ClientTraffic{}).Where("email = ?", modelClient.Email).Count(&count)
|
|
if count == 0 {
|
|
s.AddClientStat(tx, inbounds[inbound_index].Id, &modelClient)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tx.Save(inbounds)
|
|
|
|
// Remove orphaned traffics
|
|
tx.Where("inbound_id = 0").Delete(xray.ClientTraffic{})
|
|
}
|
|
|
|
func (s *InboundService) MigrateDB() {
|
|
s.MigrationRequirements()
|
|
s.MigrationRemoveOrphanedTraffics()
|
|
}
|