Files
3x-ui/internal/web/service/inbound_clients_stale_uuid_test.go
T
mrchatam 67addab343 fix(inbounds): serve fresh client UUIDs for list and allLinks (#6458)
* fix(inbounds): serve fresh client UUIDs for list and allLinks (#6436)

Resolve clients from the clients table in inboundLinks and
backfillClientStats so /inbounds/list ClientStats and allLinks match
the running Xray identity when embedded settings JSON is stale.

* fix(sub): keep WG/AWG settings identity in link exports (#6436)

clientsForLinkExport uses the clients table for UUID-bearing protocols and
the inbound settings JSON for WireGuard/AmneziaWG so allLinks and per-client
QR links stay consistent without collapsing per-inbound tunnel keys.

* fix(sub): fall back to settings clients for link export (#6458)

Prefer ListClientsForInbound for UUID protocols, but when the clients
table is empty or unavailable fall back to GetClients so settings-only
inbounds (and share-link unit tests) still produce links. Keep WG/AWG
on settings identity.

---------

Co-authored-by: mrchatam <mrchatam@users.noreply.github.com>
Co-authored-by: mrchatam <287639636+mrchatam@users.noreply.github.com>
2026-09-12 11:12:13 +02:00

58 lines
2.0 KiB
Go

package service
import (
"testing"
"github.com/mhsanaei/3x-ui/v3/internal/database"
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
"github.com/mhsanaei/3x-ui/v3/internal/xray"
)
// GetInbounds must enrich ClientStats.UUID from the clients table, not the
// embedded inbound settings JSON, when the two diverge (#6436).
func TestEnrichClientStats_UsesClientsTableUUIDWhenSettingsStale(t *testing.T) {
setupBulkDB(t)
db := database.GetDB()
stale := "11111111-1111-1111-1111-111111111111"
fresh := "22222222-2222-2222-2222-222222222222"
ib := &model.Inbound{
UserId: 1, Tag: "stale-uuid-list", Enable: true, Listen: "0.0.0.0", Port: 8443,
Protocol: model.VLESS, Remark: "stale",
Settings: `{"clients":[{"id":"` + stale + `","email":"stale@e","subId":"sub1","enable":true}],"decryption":"none"}`,
StreamSettings: `{"network":"tcp","security":"none"}`,
}
if err := db.Create(ib).Error; err != nil {
t.Fatalf("create inbound: %v", err)
}
rec := &model.ClientRecord{Email: "stale@e", SubID: "sub1", UUID: fresh, Enable: true}
if err := db.Create(rec).Error; err != nil {
t.Fatalf("create client: %v", err)
}
if err := db.Create(&model.ClientInbound{ClientId: rec.Id, InboundId: ib.Id}).Error; err != nil {
t.Fatalf("create client_inbound: %v", err)
}
if err := db.Create(&xray.ClientTraffic{InboundId: ib.Id, Email: "stale@e", Enable: true}).Error; err != nil {
t.Fatalf("create client_traffics: %v", err)
}
svc := &InboundService{}
inbounds, err := svc.GetInbounds(1)
if err != nil {
t.Fatalf("GetInbounds: %v", err)
}
if len(inbounds) != 1 {
t.Fatalf("inbounds = %d, want 1", len(inbounds))
}
if len(inbounds[0].ClientStats) != 1 {
t.Fatalf("ClientStats = %d, want 1", len(inbounds[0].ClientStats))
}
got := inbounds[0].ClientStats[0].UUID
if got != fresh {
t.Fatalf("ClientStats.UUID = %q, want fresh %q (settings still had stale %q)", got, fresh, stale)
}
if got == stale {
t.Fatalf("ClientStats.UUID still carries stale settings value %q", stale)
}
}