mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-07 13:24:21 +00:00
ac8cb505d1
* fix(subscriptions): avoid shared mutable state during generation * fix(subscriptions): serve external-link-only subs in JSON/Clash; load remark settings per request The ForRequest refactor added an early `len(inbounds) == 0` return to GetJson/GetClash that fired before external links were fetched, so a subscription whose only entries are external links (or whose inbounds are all disabled) rendered empty in the JSON and Clash formats. Drop the premature check — the existing inbounds+externalLinks empty guard already covers the truly-empty case. Also load datepicker/emailInRemark in PrepareForRequest rather than only in getSubs, so JSON and Clash remarks honor these settings instead of seeing the zero values (emailInRemark previously depended on the shared-state leak this PR fixes). Add a regression test covering an external-link-only sub across both formats. --------- Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
52 lines
1.8 KiB
Go
52 lines
1.8 KiB
Go
package sub
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
)
|
|
|
|
// A subscription whose only entries are external links — no enabled standard
|
|
// inbound — must still render in the JSON and Clash formats, not just the raw
|
|
// one. Regression guard for the premature len(inbounds)==0 early return that
|
|
// short-circuited GetJson/GetClash before external links were ever fetched.
|
|
func TestJsonAndClashServeExternalLinkOnlySub(t *testing.T) {
|
|
initSubDB(t)
|
|
db := database.GetDB()
|
|
|
|
rec := &model.ClientRecord{Email: "ext@x", SubID: "ext-only", UUID: "ext-uuid", Enable: true}
|
|
if err := db.Create(rec).Error; err != nil {
|
|
t.Fatalf("seed client: %v", err)
|
|
}
|
|
link := "vless://11111111-1111-1111-1111-111111111111@example.com:443?type=tcp&security=reality&pbk=abc&sid=12&fp=chrome#orig"
|
|
if err := db.Create(&model.ClientExternalLink{ClientId: rec.Id, Kind: model.ExternalLinkKindLink, Value: link, Remark: "DE-Provider", SortIndex: 1}).Error; err != nil {
|
|
t.Fatalf("seed external link: %v", err)
|
|
}
|
|
|
|
base := NewSubService(false, "-io")
|
|
|
|
jsonOut, _, err := NewSubJsonService("", "", "", base).GetJson("ext-only", "sub.example.com")
|
|
if err != nil {
|
|
t.Fatalf("GetJson err = %v", err)
|
|
}
|
|
if jsonOut == "" {
|
|
t.Fatal("GetJson returned empty for an external-link-only sub")
|
|
}
|
|
if !strings.Contains(jsonOut, "DE-Provider") {
|
|
t.Fatalf("GetJson missing external remark: %s", jsonOut)
|
|
}
|
|
|
|
clashOut, _, err := NewSubClashService(false, "", base).GetClash("ext-only", "sub.example.com")
|
|
if err != nil {
|
|
t.Fatalf("GetClash err = %v", err)
|
|
}
|
|
if clashOut == "" {
|
|
t.Fatal("GetClash returned empty for an external-link-only sub")
|
|
}
|
|
if !strings.Contains(clashOut, "DE-Provider") {
|
|
t.Fatalf("GetClash missing external proxy: %s", clashOut)
|
|
}
|
|
}
|