mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-02-27 20:53:01 +00:00
feat(db): split inbound tables to optional mysql with sqlite fallback
This commit is contained in:
parent
5b796672e9
commit
f57364e78b
6 changed files with 306 additions and 100 deletions
11
.env.example
11
.env.example
|
|
@ -2,3 +2,14 @@ XUI_DEBUG=true
|
||||||
XUI_DB_FOLDER=x-ui
|
XUI_DB_FOLDER=x-ui
|
||||||
XUI_LOG_FOLDER=x-ui
|
XUI_LOG_FOLDER=x-ui
|
||||||
XUI_BIN_FOLDER=x-ui
|
XUI_BIN_FOLDER=x-ui
|
||||||
|
|
||||||
|
# Optional: use MySQL only for inbounds and client_traffics tables.
|
||||||
|
# If XUI_MYSQL_DSN is set, it has priority over host/user/password fields.
|
||||||
|
# Example DSN: user:pass@tcp(127.0.0.1:3306)/xui_shared?charset=utf8mb4&parseTime=True&loc=Local
|
||||||
|
XUI_MYSQL_DSN=
|
||||||
|
XUI_MYSQL_HOST=
|
||||||
|
XUI_MYSQL_PORT=3306
|
||||||
|
XUI_MYSQL_USER=
|
||||||
|
XUI_MYSQL_PASSWORD=
|
||||||
|
XUI_MYSQL_DB=
|
||||||
|
XUI_MYSQL_PARAMS=charset=utf8mb4&parseTime=True&loc=Local
|
||||||
|
|
|
||||||
143
database/db.go
143
database/db.go
|
|
@ -1,5 +1,5 @@
|
||||||
// Package database provides database initialization, migration, and management utilities
|
// Package database provides database initialization, migration, and management utilities
|
||||||
// for the 3x-ui panel using GORM with SQLite.
|
// for the 3x-ui panel using GORM with SQLite and optional MySQL split storage.
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -11,34 +11,38 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/mhsanaei/3x-ui/v2/config"
|
"github.com/mhsanaei/3x-ui/v2/config"
|
||||||
"github.com/mhsanaei/3x-ui/v2/database/model"
|
"github.com/mhsanaei/3x-ui/v2/database/model"
|
||||||
"github.com/mhsanaei/3x-ui/v2/util/crypto"
|
"github.com/mhsanaei/3x-ui/v2/util/crypto"
|
||||||
"github.com/mhsanaei/3x-ui/v2/xray"
|
"github.com/mhsanaei/3x-ui/v2/xray"
|
||||||
|
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var db *gorm.DB
|
var db *gorm.DB
|
||||||
|
var inboundDB *gorm.DB
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultUsername = "admin"
|
defaultUsername = "admin"
|
||||||
defaultPassword = "admin"
|
defaultPassword = "admin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initModels() error {
|
func initSQLiteModels(includeInboundModels bool) error {
|
||||||
models := []any{
|
models := []any{
|
||||||
&model.User{},
|
&model.User{},
|
||||||
&model.Inbound{},
|
|
||||||
&model.OutboundTraffics{},
|
&model.OutboundTraffics{},
|
||||||
&model.Setting{},
|
&model.Setting{},
|
||||||
&model.InboundClientIps{},
|
&model.InboundClientIps{},
|
||||||
&xray.ClientTraffic{},
|
|
||||||
&model.HistoryOfSeeders{},
|
&model.HistoryOfSeeders{},
|
||||||
}
|
}
|
||||||
|
if includeInboundModels {
|
||||||
|
models = append(models, &model.Inbound{}, &xray.ClientTraffic{})
|
||||||
|
}
|
||||||
for _, model := range models {
|
for _, model := range models {
|
||||||
if err := db.AutoMigrate(model); err != nil {
|
if err := db.AutoMigrate(model); err != nil {
|
||||||
log.Printf("Error auto migrating model: %v", err)
|
log.Printf("Error auto migrating model: %v", err)
|
||||||
|
|
@ -48,6 +52,87 @@ func initModels() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initInboundModels() error {
|
||||||
|
if inboundDB == nil {
|
||||||
|
return errors.New("inbound database is nil")
|
||||||
|
}
|
||||||
|
models := []any{
|
||||||
|
&model.Inbound{},
|
||||||
|
&xray.ClientTraffic{},
|
||||||
|
}
|
||||||
|
for _, model := range models {
|
||||||
|
if err := inboundDB.AutoMigrate(model); err != nil {
|
||||||
|
log.Printf("Error auto migrating inbound model: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func migrateInboundDataIfNeeded() error {
|
||||||
|
if inboundDB == nil || db == nil || inboundDB == db {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var mysqlInboundCount int64
|
||||||
|
if err := inboundDB.Model(&model.Inbound{}).Count(&mysqlInboundCount).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if mysqlInboundCount == 0 {
|
||||||
|
var sqliteInbounds []model.Inbound
|
||||||
|
if err := db.Model(&model.Inbound{}).Find(&sqliteInbounds).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(sqliteInbounds) > 0 {
|
||||||
|
if err := inboundDB.CreateInBatches(&sqliteInbounds, 200).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mysqlClientTrafficCount int64
|
||||||
|
if err := inboundDB.Model(&xray.ClientTraffic{}).Count(&mysqlClientTrafficCount).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if mysqlClientTrafficCount == 0 {
|
||||||
|
var sqliteClientTraffics []xray.ClientTraffic
|
||||||
|
if err := db.Model(&xray.ClientTraffic{}).Find(&sqliteClientTraffics).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(sqliteClientTraffics) > 0 {
|
||||||
|
if err := inboundDB.CreateInBatches(&sqliteClientTraffics, 500).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMySQLDSN() string {
|
||||||
|
if dsn := strings.TrimSpace(os.Getenv("XUI_MYSQL_DSN")); dsn != "" {
|
||||||
|
return dsn
|
||||||
|
}
|
||||||
|
|
||||||
|
host := strings.TrimSpace(os.Getenv("XUI_MYSQL_HOST"))
|
||||||
|
port := strings.TrimSpace(os.Getenv("XUI_MYSQL_PORT"))
|
||||||
|
user := strings.TrimSpace(os.Getenv("XUI_MYSQL_USER"))
|
||||||
|
pass := os.Getenv("XUI_MYSQL_PASSWORD")
|
||||||
|
dbName := strings.TrimSpace(os.Getenv("XUI_MYSQL_DB"))
|
||||||
|
params := strings.TrimSpace(os.Getenv("XUI_MYSQL_PARAMS"))
|
||||||
|
|
||||||
|
if host == "" || user == "" || dbName == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if port == "" {
|
||||||
|
port = "3306"
|
||||||
|
}
|
||||||
|
if params == "" {
|
||||||
|
params = "charset=utf8mb4&parseTime=True&loc=Local"
|
||||||
|
}
|
||||||
|
return user + ":" + pass + "@tcp(" + host + ":" + port + ")/" + dbName + "?" + params
|
||||||
|
}
|
||||||
|
|
||||||
// initUser creates a default admin user if the users table is empty.
|
// initUser creates a default admin user if the users table is empty.
|
||||||
func initUser() error {
|
func initUser() error {
|
||||||
empty, err := isTableEmpty("users")
|
empty, err := isTableEmpty("users")
|
||||||
|
|
@ -142,10 +227,29 @@ func InitDB(dbPath string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
inboundDB = db
|
||||||
|
|
||||||
if err := initModels(); err != nil {
|
mysqlDSN := getMySQLDSN()
|
||||||
|
useDedicatedInboundDB := mysqlDSN != ""
|
||||||
|
if useDedicatedInboundDB {
|
||||||
|
mysqlInboundDB, mysqlErr := gorm.Open(mysql.Open(mysqlDSN), c)
|
||||||
|
if mysqlErr != nil {
|
||||||
|
return mysqlErr
|
||||||
|
}
|
||||||
|
inboundDB = mysqlInboundDB
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := initSQLiteModels(!useDedicatedInboundDB); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if useDedicatedInboundDB {
|
||||||
|
if err := initInboundModels(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := migrateInboundDataIfNeeded(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isUsersEmpty, err := isTableEmpty("users")
|
isUsersEmpty, err := isTableEmpty("users")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -160,14 +264,31 @@ func InitDB(dbPath string) error {
|
||||||
|
|
||||||
// CloseDB closes the database connection if it exists.
|
// CloseDB closes the database connection if it exists.
|
||||||
func CloseDB() error {
|
func CloseDB() error {
|
||||||
|
var closeErr error
|
||||||
|
if inboundDB != nil && db != nil && inboundDB != db {
|
||||||
|
sqlInboundDB, err := inboundDB.DB()
|
||||||
|
if err != nil {
|
||||||
|
closeErr = err
|
||||||
|
} else {
|
||||||
|
closeErr = sqlInboundDB.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
if db != nil {
|
if db != nil {
|
||||||
sqlDB, err := db.DB()
|
sqlDB, err := db.DB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if closeErr != nil {
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = sqlDB.Close(); err != nil {
|
||||||
|
if closeErr != nil {
|
||||||
|
return closeErr
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return sqlDB.Close()
|
|
||||||
}
|
}
|
||||||
return nil
|
return closeErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDB returns the global GORM database instance.
|
// GetDB returns the global GORM database instance.
|
||||||
|
|
@ -175,6 +296,14 @@ func GetDB() *gorm.DB {
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetInboundDB returns the DB used for inbounds and client traffics.
|
||||||
|
func GetInboundDB() *gorm.DB {
|
||||||
|
if inboundDB != nil {
|
||||||
|
return inboundDB
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
// IsNotFound checks if the given error is a GORM record not found error.
|
// IsNotFound checks if the given error is a GORM record not found error.
|
||||||
func IsNotFound(err error) bool {
|
func IsNotFound(err error) bool {
|
||||||
return err == gorm.ErrRecordNotFound
|
return err == gorm.ErrRecordNotFound
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -26,6 +26,7 @@ require (
|
||||||
golang.org/x/sys v0.41.0
|
golang.org/x/sys v0.41.0
|
||||||
golang.org/x/text v0.34.0
|
golang.org/x/text v0.34.0
|
||||||
google.golang.org/grpc v1.78.0
|
google.golang.org/grpc v1.78.0
|
||||||
|
gorm.io/driver/mysql v1.6.0
|
||||||
gorm.io/driver/sqlite v1.6.0
|
gorm.io/driver/sqlite v1.6.0
|
||||||
gorm.io/gorm v1.31.1
|
gorm.io/gorm v1.31.1
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -113,20 +113,30 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, int64, xray.C
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Where(`id in (
|
err := db.Model(model.Inbound{}).
|
||||||
SELECT DISTINCT inbounds.id
|
Preload("ClientStats").
|
||||||
FROM inbounds,
|
Where("enable = ?", true).
|
||||||
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
Where("protocol IN ?", []model.Protocol{model.VMESS, model.VLESS, model.Trojan, model.Shadowsocks}).
|
||||||
WHERE
|
Find(&inbounds).Error
|
||||||
protocol in ('vmess','vless','trojan','shadowsocks')
|
|
||||||
AND JSON_EXTRACT(client.value, '$.subId') = ? AND enable = ?
|
|
||||||
)`, subId, true).Find(&inbounds).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return inbounds, nil
|
filtered := make([]*model.Inbound, 0, len(inbounds))
|
||||||
|
for _, inbound := range inbounds {
|
||||||
|
clients, cErr := s.inboundService.GetClients(inbound)
|
||||||
|
if cErr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, client := range clients {
|
||||||
|
if client.Enable && client.SubID == subId {
|
||||||
|
filtered = append(filtered, inbound)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic {
|
func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email string) xray.ClientTraffic {
|
||||||
|
|
@ -139,16 +149,42 @@ func (s *SubService) getClientTraffics(traffics []xray.ClientTraffic, email stri
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) getFallbackMaster(dest string, streamSettings string) (string, int, string, error) {
|
func (s *SubService) getFallbackMaster(dest string, streamSettings string) (string, int, string, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inbound *model.Inbound
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(model.Inbound{}).
|
if err := db.Model(model.Inbound{}).Find(&inbounds).Error; err != nil {
|
||||||
Where("JSON_TYPE(settings, '$.fallbacks') = 'array'").
|
|
||||||
Where("EXISTS (SELECT * FROM json_each(settings, '$.fallbacks') WHERE json_extract(value, '$.dest') = ?)", dest).
|
|
||||||
Find(&inbound).Error
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, "", err
|
return "", 0, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var inbound *model.Inbound
|
||||||
|
for _, candidate := range inbounds {
|
||||||
|
var settings map[string]any
|
||||||
|
if err := json.Unmarshal([]byte(candidate.Settings), &settings); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fallbacks, ok := settings["fallbacks"].([]any)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
match := false
|
||||||
|
for _, item := range fallbacks {
|
||||||
|
fallback, ok := item.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if fallbackDest, ok := fallback["dest"].(string); ok && fallbackDest == dest {
|
||||||
|
match = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
inbound = candidate
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if inbound == nil {
|
||||||
|
return "", 0, "", fmt.Errorf("fallback master not found for dest %s", dest)
|
||||||
|
}
|
||||||
|
|
||||||
var stream map[string]any
|
var stream map[string]any
|
||||||
json.Unmarshal([]byte(streamSettings), &stream)
|
json.Unmarshal([]byte(streamSettings), &stream)
|
||||||
var masterStream map[string]any
|
var masterStream map[string]any
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ func (j *CheckClientIpJob) clearAccessLog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *CheckClientIpJob) hasLimitIp() bool {
|
func (j *CheckClientIpJob) hasLimitIp() bool {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
|
|
||||||
err := db.Model(model.Inbound{}).Find(&inbounds).Error
|
err := db.Model(model.Inbound{}).Find(&inbounds).Error
|
||||||
|
|
@ -440,7 +440,7 @@ func (j *CheckClientIpJob) disconnectClientTemporarily(inbound *model.Inbound, c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*model.Inbound, error) {
|
func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
inbound := &model.Inbound{}
|
inbound := &model.Inbound{}
|
||||||
|
|
||||||
err := db.Model(&model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").First(inbound).Error
|
err := db.Model(&model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").First(inbound).Error
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ type InboundService struct {
|
||||||
// GetInbounds retrieves all inbounds for a specific user.
|
// GetInbounds retrieves all inbounds for a specific user.
|
||||||
// Returns a slice of inbound models with their associated client statistics.
|
// Returns a slice of inbound models with their associated client statistics.
|
||||||
func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
|
func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Find(&inbounds).Error
|
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("user_id = ?", userId).Find(&inbounds).Error
|
||||||
if err != nil && err != gorm.ErrRecordNotFound {
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
|
@ -60,7 +60,7 @@ func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) {
|
||||||
// GetAllInbounds retrieves all inbounds from the database.
|
// GetAllInbounds retrieves all inbounds from the database.
|
||||||
// Returns a slice of all inbound models with their associated client statistics.
|
// Returns a slice of all inbound models with their associated client statistics.
|
||||||
func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
|
func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Find(&inbounds).Error
|
err := db.Model(model.Inbound{}).Preload("ClientStats").Find(&inbounds).Error
|
||||||
if err != nil && err != gorm.ErrRecordNotFound {
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
|
@ -88,7 +88,7 @@ func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*model.Inbound, error) {
|
func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(model.Inbound{}).Where("traffic_reset = ?", period).Find(&inbounds).Error
|
err := db.Model(model.Inbound{}).Where("traffic_reset = ?", period).Find(&inbounds).Error
|
||||||
if err != nil && err != gorm.ErrRecordNotFound {
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
|
@ -98,7 +98,7 @@ func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*model.Inbo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) {
|
func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" {
|
if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" {
|
||||||
db = db.Model(model.Inbound{}).Where("port = ?", port)
|
db = db.Model(model.Inbound{}).Where("port = ?", port)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -142,16 +142,29 @@ func (s *InboundService) GetClients(inbound *model.Inbound) ([]model.Client, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) getAllEmails() ([]string, error) {
|
func (s *InboundService) getAllEmails() ([]string, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var emails []string
|
var inbounds []*model.Inbound
|
||||||
err := db.Raw(`
|
err := db.Model(model.Inbound{}).Select("settings").Find(&inbounds).Error
|
||||||
SELECT JSON_EXTRACT(client.value, '$.email')
|
|
||||||
FROM inbounds,
|
|
||||||
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
|
||||||
`).Scan(&emails).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
emailSet := make(map[string]struct{})
|
||||||
|
for _, inbound := range inbounds {
|
||||||
|
clients, cErr := s.GetClients(inbound)
|
||||||
|
if cErr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, client := range clients {
|
||||||
|
if client.Email == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
emailSet[client.Email] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emails := make([]string, 0, len(emailSet))
|
||||||
|
for email := range emailSet {
|
||||||
|
emails = append(emails, email)
|
||||||
|
}
|
||||||
return emails, nil
|
return emails, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,7 +290,7 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
defer func() {
|
defer func() {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -323,7 +336,7 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo
|
||||||
// It removes the inbound from the database and the running Xray instance if active.
|
// It removes the inbound from the database and the running Xray instance if active.
|
||||||
// Returns whether Xray needs restart and any error.
|
// Returns whether Xray needs restart and any error.
|
||||||
func (s *InboundService) DelInbound(id int) (bool, error) {
|
func (s *InboundService) DelInbound(id int) (bool, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
|
|
||||||
var tag string
|
var tag string
|
||||||
needRestart := false
|
needRestart := false
|
||||||
|
|
@ -366,7 +379,7 @@ func (s *InboundService) DelInbound(id int) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetInbound(id int) (*model.Inbound, error) {
|
func (s *InboundService) GetInbound(id int) (*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
inbound := &model.Inbound{}
|
inbound := &model.Inbound{}
|
||||||
err := db.Model(model.Inbound{}).First(inbound, id).Error
|
err := db.Model(model.Inbound{}).First(inbound, id).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -394,7 +407,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
||||||
|
|
||||||
tag := oldInbound.Tag
|
tag := oldInbound.Tag
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -631,7 +644,7 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
|
||||||
|
|
||||||
oldInbound.Settings = string(newSettings)
|
oldInbound.Settings = string(newSettings)
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -723,7 +736,7 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
|
||||||
|
|
||||||
oldInbound.Settings = string(newSettings)
|
oldInbound.Settings = string(newSettings)
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
|
|
||||||
err = s.DelClientIPs(db, email)
|
err = s.DelClientIPs(db, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -861,7 +874,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
|
||||||
}
|
}
|
||||||
|
|
||||||
oldInbound.Settings = string(newSettings)
|
oldInbound.Settings = string(newSettings)
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -941,7 +954,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
|
||||||
|
|
||||||
func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) {
|
func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (error, bool) {
|
||||||
var err error
|
var err error
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -1300,7 +1313,7 @@ func (s *InboundService) disableInvalidClients(tx *gorm.DB) (bool, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetInboundTags() (string, error) {
|
func (s *InboundService) GetInboundTags() (string, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inboundTags []string
|
var inboundTags []string
|
||||||
err := db.Model(model.Inbound{}).Select("tag").Find(&inboundTags).Error
|
err := db.Model(model.Inbound{}).Select("tag").Find(&inboundTags).Error
|
||||||
if err != nil && err != gorm.ErrRecordNotFound {
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
|
@ -1311,15 +1324,21 @@ func (s *InboundService) GetInboundTags() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) MigrationRemoveOrphanedTraffics() {
|
func (s *InboundService) MigrationRemoveOrphanedTraffics() {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
db.Exec(`
|
emails, err := s.getAllEmails()
|
||||||
DELETE FROM client_traffics
|
if err != nil {
|
||||||
WHERE email NOT IN (
|
logger.Warningf("MigrationRemoveOrphanedTraffics failed to load emails: %v", err)
|
||||||
SELECT JSON_EXTRACT(client.value, '$.email')
|
return
|
||||||
FROM inbounds,
|
}
|
||||||
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
if len(emails) == 0 {
|
||||||
)
|
if err := db.Where("1 = 1").Delete(xray.ClientTraffic{}).Error; err != nil {
|
||||||
`)
|
logger.Warningf("MigrationRemoveOrphanedTraffics delete-all failed: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := db.Where("email NOT IN ?", emails).Delete(xray.ClientTraffic{}).Error; err != nil {
|
||||||
|
logger.Warningf("MigrationRemoveOrphanedTraffics delete-orphans failed: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) AddClientStat(tx *gorm.DB, inboundId int, client *model.Client) error {
|
func (s *InboundService) AddClientStat(tx *gorm.DB, inboundId int, client *model.Client) error {
|
||||||
|
|
@ -1352,7 +1371,7 @@ func (s *InboundService) UpdateClientStat(tx *gorm.DB, email string, client *mod
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) UpdateClientIPs(tx *gorm.DB, oldEmail string, newEmail string) error {
|
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
|
return database.GetDB().Model(model.InboundClientIps{}).Where("client_email = ?", oldEmail).Update("client_email", newEmail).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error {
|
func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error {
|
||||||
|
|
@ -1360,11 +1379,11 @@ func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) DelClientIPs(tx *gorm.DB, email string) error {
|
func (s *InboundService) DelClientIPs(tx *gorm.DB, email string) error {
|
||||||
return tx.Where("client_email = ?", email).Delete(model.InboundClientIps{}).Error
|
return database.GetDB().Where("client_email = ?", email).Delete(model.InboundClientIps{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetClientInboundByTrafficID(trafficId int) (traffic *xray.ClientTraffic, inbound *model.Inbound, err error) {
|
func (s *InboundService) GetClientInboundByTrafficID(trafficId int) (traffic *xray.ClientTraffic, inbound *model.Inbound, err error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var traffics []*xray.ClientTraffic
|
var traffics []*xray.ClientTraffic
|
||||||
err = db.Model(xray.ClientTraffic{}).Where("id = ?", trafficId).Find(&traffics).Error
|
err = db.Model(xray.ClientTraffic{}).Where("id = ?", trafficId).Find(&traffics).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1379,7 +1398,7 @@ func (s *InboundService) GetClientInboundByTrafficID(trafficId int) (traffic *xr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetClientInboundByEmail(email string) (traffic *xray.ClientTraffic, inbound *model.Inbound, err error) {
|
func (s *InboundService) GetClientInboundByEmail(email string) (traffic *xray.ClientTraffic, inbound *model.Inbound, err error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var traffics []*xray.ClientTraffic
|
var traffics []*xray.ClientTraffic
|
||||||
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1766,7 +1785,7 @@ func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail string, tota
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
|
func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
|
|
||||||
// Reset traffic stats in ClientTraffic table
|
// Reset traffic stats in ClientTraffic table
|
||||||
result := db.Model(xray.ClientTraffic{}).
|
result := db.Model(xray.ClientTraffic{}).
|
||||||
|
|
@ -1834,7 +1853,7 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e
|
||||||
traffic.Down = 0
|
traffic.Down = 0
|
||||||
traffic.Enable = true
|
traffic.Enable = true
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
err = db.Save(traffic).Error
|
err = db.Save(traffic).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
@ -1844,7 +1863,7 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) ResetAllClientTraffics(id int) error {
|
func (s *InboundService) ResetAllClientTraffics(id int) error {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
now := time.Now().Unix() * 1000
|
now := time.Now().Unix() * 1000
|
||||||
|
|
||||||
return db.Transaction(func(tx *gorm.DB) error {
|
return db.Transaction(func(tx *gorm.DB) error {
|
||||||
|
|
@ -1881,7 +1900,7 @@ func (s *InboundService) ResetAllClientTraffics(id int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) ResetAllTraffics() error {
|
func (s *InboundService) ResetAllTraffics() error {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
|
|
||||||
result := db.Model(model.Inbound{}).
|
result := db.Model(model.Inbound{}).
|
||||||
Where("user_id > ?", 0).
|
Where("user_id > ?", 0).
|
||||||
|
|
@ -1892,7 +1911,7 @@ func (s *InboundService) ResetAllTraffics() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) DelDepletedClients(id int) (err error) {
|
func (s *InboundService) DelDepletedClients(id int) (err error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
defer func() {
|
defer func() {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -1977,7 +1996,7 @@ func (s *InboundService) DelDepletedClients(id int) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.ClientTraffic, error) {
|
func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.ClientTraffic, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
|
|
||||||
// Retrieve inbounds where settings contain the given tgId
|
// Retrieve inbounds where settings contain the given tgId
|
||||||
|
|
@ -2041,7 +2060,7 @@ func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.Cl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) UpdateClientTrafficByEmail(email string, upload int64, download int64) error {
|
func (s *InboundService) UpdateClientTrafficByEmail(email string, upload int64, download int64) error {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
|
|
||||||
result := db.Model(xray.ClientTraffic{}).
|
result := db.Model(xray.ClientTraffic{}).
|
||||||
Where("email = ?", email).
|
Where("email = ?", email).
|
||||||
|
|
@ -2056,16 +2075,30 @@ func (s *InboundService) UpdateClientTrafficByEmail(email string, upload int64,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetClientTrafficByID(id string) ([]xray.ClientTraffic, error) {
|
func (s *InboundService) GetClientTrafficByID(id string) ([]xray.ClientTraffic, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var traffics []xray.ClientTraffic
|
var traffics []xray.ClientTraffic
|
||||||
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(xray.ClientTraffic{}).Where(`email IN(
|
err := db.Model(model.Inbound{}).Find(&inbounds).Error
|
||||||
SELECT JSON_EXTRACT(client.value, '$.email') as email
|
if err != nil {
|
||||||
FROM inbounds,
|
logger.Debug(err)
|
||||||
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
|
return nil, err
|
||||||
WHERE
|
}
|
||||||
JSON_EXTRACT(client.value, '$.id') in (?)
|
emails := make([]string, 0)
|
||||||
)`, id).Find(&traffics).Error
|
for _, inbound := range inbounds {
|
||||||
|
clients, cErr := s.GetClients(inbound)
|
||||||
|
if cErr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, client := range clients {
|
||||||
|
if client.ID == id && client.Email != "" {
|
||||||
|
emails = append(emails, client.Email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(emails) == 0 {
|
||||||
|
return traffics, nil
|
||||||
|
}
|
||||||
|
err = db.Model(xray.ClientTraffic{}).Where("email IN ?", emails).Find(&traffics).Error
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug(err)
|
logger.Debug(err)
|
||||||
|
|
@ -2083,7 +2116,7 @@ func (s *InboundService) GetClientTrafficByID(id string) ([]xray.ClientTraffic,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.ClientTraffic, err error) {
|
func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.ClientTraffic, err error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
inbound := &model.Inbound{}
|
inbound := &model.Inbound{}
|
||||||
traffic = &xray.ClientTraffic{}
|
traffic = &xray.ClientTraffic{}
|
||||||
|
|
||||||
|
|
@ -2195,7 +2228,7 @@ func (s *InboundService) ClearClientIps(clientEmail string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error) {
|
func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("remark like ?", "%"+query+"%").Find(&inbounds).Error
|
err := db.Model(model.Inbound{}).Preload("ClientStats").Where("remark like ?", "%"+query+"%").Find(&inbounds).Error
|
||||||
if err != nil && err != gorm.ErrRecordNotFound {
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
|
@ -2205,13 +2238,14 @@ func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) MigrationRequirements() {
|
func (s *InboundService) MigrationRequirements() {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
|
sqliteDB := database.GetDB()
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
var err error
|
var err error
|
||||||
defer func() {
|
defer func() {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
if dbErr := db.Exec(`VACUUM "main"`).Error; dbErr != nil {
|
if dbErr := sqliteDB.Exec(`VACUUM "main"`).Error; dbErr != nil {
|
||||||
logger.Warningf("VACUUM failed: %v", dbErr)
|
logger.Warningf("VACUUM failed: %v", dbErr)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2313,24 +2347,17 @@ func (s *InboundService) MigrationRequirements() {
|
||||||
tx.Where("inbound_id = 0").Delete(xray.ClientTraffic{})
|
tx.Where("inbound_id = 0").Delete(xray.ClientTraffic{})
|
||||||
|
|
||||||
// Migrate old MultiDomain to External Proxy
|
// Migrate old MultiDomain to External Proxy
|
||||||
var externalProxy []struct {
|
for _, ep := range inbounds {
|
||||||
Id int
|
|
||||||
Port int
|
|
||||||
StreamSettings []byte
|
|
||||||
}
|
|
||||||
err = tx.Raw(`select id, port, stream_settings
|
|
||||||
from inbounds
|
|
||||||
WHERE protocol in ('vmess','vless','trojan')
|
|
||||||
AND json_extract(stream_settings, '$.security') = 'tls'
|
|
||||||
AND json_extract(stream_settings, '$.tlsSettings.settings.domains') IS NOT NULL`).Scan(&externalProxy).Error
|
|
||||||
if err != nil || len(externalProxy) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ep := range externalProxy {
|
|
||||||
var reverses any
|
var reverses any
|
||||||
var stream map[string]any
|
var stream map[string]any
|
||||||
json.Unmarshal(ep.StreamSettings, &stream)
|
if err := json.Unmarshal([]byte(ep.StreamSettings), &stream); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
security, _ := stream["security"].(string)
|
||||||
|
if security != "tls" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if tlsSettings, ok := stream["tlsSettings"].(map[string]any); ok {
|
if tlsSettings, ok := stream["tlsSettings"].(map[string]any); ok {
|
||||||
if settings, ok := tlsSettings["settings"].(map[string]any); ok {
|
if settings, ok := tlsSettings["settings"].(map[string]any); ok {
|
||||||
if domains, ok := settings["domains"].([]any); ok {
|
if domains, ok := settings["domains"].([]any); ok {
|
||||||
|
|
@ -2338,7 +2365,9 @@ func (s *InboundService) MigrationRequirements() {
|
||||||
if domainMap, ok := domain.(map[string]any); ok {
|
if domainMap, ok := domain.(map[string]any); ok {
|
||||||
domainMap["forceTls"] = "same"
|
domainMap["forceTls"] = "same"
|
||||||
domainMap["port"] = ep.Port
|
domainMap["port"] = ep.Port
|
||||||
domainMap["dest"] = domainMap["domain"].(string)
|
if domainVal, ok2 := domainMap["domain"].(string); ok2 {
|
||||||
|
domainMap["dest"] = domainVal
|
||||||
|
}
|
||||||
delete(domainMap, "domain")
|
delete(domainMap, "domain")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2370,7 +2399,7 @@ func (s *InboundService) GetOnlineClients() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetClientsLastOnline() (map[string]int64, error) {
|
func (s *InboundService) GetClientsLastOnline() (map[string]int64, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
var rows []xray.ClientTraffic
|
var rows []xray.ClientTraffic
|
||||||
err := db.Model(&xray.ClientTraffic{}).Select("email, last_online").Find(&rows).Error
|
err := db.Model(&xray.ClientTraffic{}).Select("email, last_online").Find(&rows).Error
|
||||||
if err != nil && err != gorm.ErrRecordNotFound {
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
|
@ -2384,7 +2413,7 @@ func (s *InboundService) GetClientsLastOnline() (map[string]int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) FilterAndSortClientEmails(emails []string) ([]string, []string, error) {
|
func (s *InboundService) FilterAndSortClientEmails(emails []string) ([]string, []string, error) {
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
|
|
||||||
// Step 1: Get ClientTraffic records for emails in the input list
|
// Step 1: Get ClientTraffic records for emails in the input list
|
||||||
var clients []xray.ClientTraffic
|
var clients []xray.ClientTraffic
|
||||||
|
|
@ -2466,7 +2495,7 @@ func (s *InboundService) DelInboundClientByEmail(inboundId int, email string) (b
|
||||||
|
|
||||||
oldInbound.Settings = string(newSettings)
|
oldInbound.Settings = string(newSettings)
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetInboundDB()
|
||||||
|
|
||||||
// remove IP bindings
|
// remove IP bindings
|
||||||
if err := s.DelClientIPs(db, email); err != nil {
|
if err := s.DelClientIPs(db, email); err != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue