fix(sub): resolve legacy socket fallback fronts on PostgreSQL too

The socket-front lookup filtered and extracted settings.fallbacks with
SQLite's json_valid, JSON_TYPE and json_extract, which PostgreSQL does
not have, so on PostgreSQL the statement failed and a legacy @socket
child never got its front's address and security in its links.

The lookup now selects the inbounds whose settings mention fallbacks and
parses them in Go, which works on both databases and still skips an
inbound whose settings do not decode.
This commit is contained in:
MHSanaei
2026-11-01 13:16:14 +01:00
parent 93d1e63554
commit f99c6c664d
2 changed files with 14 additions and 10 deletions
+4 -2
View File
@@ -2048,8 +2048,10 @@ func TestSub_LegacyFallbackMasterLookedUpOncePerRequest(t *testing.T) {
scans := 0 scans := 0
count := func(tx *gorm.DB) { count := func(tx *gorm.DB) {
if strings.Contains(tx.Statement.SQL.String(), "$.fallbacks") { for _, v := range tx.Statement.Vars {
scans++ if v == "%fallbacks%" {
scans++
}
} }
} }
_ = db.Callback().Query().After("gorm:query").Register("test:count_front_scans", count) _ = db.Callback().Query().After("gorm:query").Register("test:count_front_scans", count)
+10 -8
View File
@@ -3607,20 +3607,22 @@ func (s *SubService) socketFront(key socketFrontKey) *model.Inbound {
Listen string Listen string
Port int Port int
StreamSettings string StreamSettings string
Fallbacks string Settings string
} }
// json_valid keeps one inbound with unreadable settings from failing the // Parsed here, not with SQL JSON functions, so the lookup works on PostgreSQL too and one
// whole statement and hiding every front. // inbound with unreadable settings cannot hide every front.
if err := database.GetDB().Model(model.Inbound{}). if err := database.GetDB().Model(model.Inbound{}).
Select("node_id, listen, port, stream_settings, json_extract(settings, '$.fallbacks') AS fallbacks"). Select("node_id, listen, port, stream_settings, settings").
Where("CASE WHEN json_valid(settings) THEN JSON_TYPE(settings, '$.fallbacks') END = 'array'"). Where("settings LIKE ?", "%fallbacks%").
Order("id ASC").Scan(&fronts).Error; err != nil { Order("id ASC").Scan(&fronts).Error; err != nil {
return nil return nil
} }
for _, front := range fronts { for _, front := range fronts {
var fallbacks []any var parsed struct {
_ = json.Unmarshal([]byte(front.Fallbacks), &fallbacks) Fallbacks []any `json:"fallbacks"`
for _, fallback := range fallbacks { }
_ = json.Unmarshal([]byte(front.Settings), &parsed)
for _, fallback := range parsed.Fallbacks {
rule, _ := fallback.(map[string]any) rule, _ := fallback.(map[string]any)
dest, _ := rule["dest"].(string) dest, _ := rule["dest"].(string)
k := socketFrontKey{node: nodeKeyOf(front.NodeID), dest: dest} k := socketFrontKey{node: nodeKeyOf(front.NodeID), dest: dest}