fix: MariaDB JSON_EACH compatibility for subscription and traffic queries

Replace SQLite-only JSON_EACH with DB-type branching (JSON_TABLE for
MariaDB) in subscription, client traffic, and migration queries.
Bump version to v1.5.1.
This commit is contained in:
root 2026-04-24 10:16:48 +08:00
parent eca9b219cf
commit 1a02ebb024
3 changed files with 76 additions and 25 deletions

View file

@ -1 +1 @@
v1.4.6-beta v1.5.1

View file

@ -11,6 +11,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/goccy/go-json" "github.com/goccy/go-json"
"github.com/mhsanaei/3x-ui/v2/config"
"github.com/mhsanaei/3x-ui/v2/database" "github.com/mhsanaei/3x-ui/v2/database"
"github.com/mhsanaei/3x-ui/v2/database/model" "github.com/mhsanaei/3x-ui/v2/database/model"
"github.com/mhsanaei/3x-ui/v2/logger" "github.com/mhsanaei/3x-ui/v2/logger"
@ -115,14 +116,29 @@ 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.GetDB()
var inbounds []*model.Inbound var inbounds []*model.Inbound
err := db.Model(model.Inbound{}).Preload("ClientStats").Where(`id in (
var query string
if config.GetDBTypeFromJSON() == "mariadb" {
query = `id in (
SELECT DISTINCT inbounds.id
FROM inbounds,
JSON_TABLE(inbounds.settings, '$.clients[*]' COLUMNS(value JSON PATH '$')) AS client
WHERE
protocol in ('vmess','vless','trojan','shadowsocks')
AND JSON_EXTRACT(client.value, '$.subId') = ? AND enable = ?
)`
} else {
query = `id in (
SELECT DISTINCT inbounds.id SELECT DISTINCT inbounds.id
FROM inbounds, FROM inbounds,
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
WHERE WHERE
protocol in ('vmess','vless','trojan','shadowsocks') protocol in ('vmess','vless','trojan','shadowsocks')
AND JSON_EXTRACT(client.value, '$.subId') = ? AND enable = ? AND JSON_EXTRACT(client.value, '$.subId') = ? AND enable = ?
)`, subId, true).Find(&inbounds).Error )`
}
err := db.Model(model.Inbound{}).Preload("ClientStats").Where(query, subId, true).Find(&inbounds).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -141,9 +157,17 @@ 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.GetDB()
var inbound *model.Inbound var inbound *model.Inbound
var jsonQuery string
if config.GetDBTypeFromJSON() == "mariadb" {
jsonQuery = "EXISTS (SELECT * FROM JSON_TABLE(settings, '$.fallbacks[*]' COLUMNS(value JSON PATH '$')) AS jt WHERE JSON_EXTRACT(jt.value, '$.dest') = ?)"
} else {
jsonQuery = "EXISTS (SELECT * FROM json_each(settings, '$.fallbacks') WHERE json_extract(value, '$.dest') = ?)"
}
err := db.Model(model.Inbound{}). err := db.Model(model.Inbound{}).
Where("JSON_TYPE(settings, '$.fallbacks') = 'array'"). Where("JSON_TYPE(settings, '$.fallbacks') = 'array'").
Where("EXISTS (SELECT * FROM json_each(settings, '$.fallbacks') WHERE json_extract(value, '$.dest') = ?)", dest). Where(jsonQuery, dest).
Find(&inbound).Error Find(&inbound).Error
if err != nil { if err != nil {
return "", 0, "", err return "", 0, "", err

View file

@ -10,6 +10,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/mhsanaei/3x-ui/v2/config"
"github.com/mhsanaei/3x-ui/v2/database" "github.com/mhsanaei/3x-ui/v2/database"
"github.com/mhsanaei/3x-ui/v2/database/model" "github.com/mhsanaei/3x-ui/v2/database/model"
"github.com/mhsanaei/3x-ui/v2/logger" "github.com/mhsanaei/3x-ui/v2/logger"
@ -1525,14 +1526,27 @@ func (s *InboundService) GetInboundTags() (string, error) {
func (s *InboundService) MigrationRemoveOrphanedTraffics() { func (s *InboundService) MigrationRemoveOrphanedTraffics() {
db := database.GetDB() db := database.GetDB()
db.Exec(`
var query string
if config.GetDBTypeFromJSON() == "mariadb" {
query = `
DELETE FROM client_traffics
WHERE email NOT IN (
SELECT JSON_EXTRACT(client.value, '$.email')
FROM inbounds,
JSON_TABLE(inbounds.settings, '$.clients[*]' COLUMNS(value JSON PATH '$')) AS client
)`
} else {
query = `
DELETE FROM client_traffics DELETE FROM client_traffics
WHERE email NOT IN ( WHERE email NOT IN (
SELECT JSON_EXTRACT(client.value, '$.email') SELECT JSON_EXTRACT(client.value, '$.email')
FROM inbounds, FROM inbounds,
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
) )`
`) }
db.Exec(query)
} }
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 {
@ -2327,13 +2341,26 @@ func (s *InboundService) GetClientTrafficByID(id string) ([]xray.ClientTraffic,
db := database.GetDB() db := database.GetDB()
var traffics []xray.ClientTraffic var traffics []xray.ClientTraffic
err := db.Model(xray.ClientTraffic{}).Where(`email IN( var query string
if config.GetDBTypeFromJSON() == "mariadb" {
query = `email IN(
SELECT JSON_EXTRACT(client.value, '$.email') as email
FROM inbounds,
JSON_TABLE(inbounds.settings, '$.clients[*]' COLUMNS(value JSON PATH '$')) AS client
WHERE
JSON_EXTRACT(client.value, '$.id') in (?)
)`
} else {
query = `email IN(
SELECT JSON_EXTRACT(client.value, '$.email') as email SELECT JSON_EXTRACT(client.value, '$.email') as email
FROM inbounds, FROM inbounds,
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
WHERE WHERE
JSON_EXTRACT(client.value, '$.id') in (?) JSON_EXTRACT(client.value, '$.id') in (?)
)`, id).Find(&traffics).Error )`
}
err := db.Model(xray.ClientTraffic{}).Where(query, id).Find(&traffics).Error
if err != nil { if err != nil {
logger.Debug(err) logger.Debug(err)