fix(sync): preserve node display names across desktop sync

This commit is contained in:
Haitao Pan 2026-03-17 16:51:06 +08:00
parent 74d9958274
commit b7b57c875f
2 changed files with 84 additions and 14 deletions

View File

@ -112,6 +112,14 @@ type testEmailSender struct {
messages []capturedEmail
}
type stubAgentStatusReader struct {
statuses []agentserver.StatusSnapshot
}
func (s stubAgentStatusReader) Statuses() []agentserver.StatusSnapshot {
return append([]agentserver.StatusSnapshot(nil), s.statuses...)
}
func (s *testEmailSender) Send(ctx context.Context, msg EmailMessage) error {
_ = ctx
s.mu.Lock()
@ -643,6 +651,60 @@ func TestSyncConfigSnapshotFallsBackWhenRenderFails(t *testing.T) {
}
}
func TestSyncConfigSnapshotIncludesNodeDisplayMetadata(t *testing.T) {
gin.SetMode(gin.TestMode)
router, _, token := newAuthenticatedSyncHarness(t, WithAgentStatusReader(stubAgentStatusReader{
statuses: []agentserver.StatusSnapshot{
{Agent: agentserver.Identity{ID: "jp-xhttp.svc.plus", Name: "Japan Node"}},
{Agent: agentserver.Identity{ID: "us-xhttp.svc.plus", Name: "US Node"}},
},
}))
req := httptest.NewRequest(http.MethodGet, "/api/auth/sync/config?since_version=0", nil)
req.Header.Set("Authorization", "Bearer "+token)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatalf("expected sync config success, got %d: %s", rr.Code, rr.Body.String())
}
resp := decodeSyncConfigResponse(t, rr)
if len(resp.Nodes) != 2 {
t.Fatalf("expected 2 nodes, got %d", len(resp.Nodes))
}
byHost := make(map[string]map[string]interface{}, len(resp.Nodes))
for _, node := range resp.Nodes {
host, _ := node["host"].(string)
if strings.TrimSpace(host) == "" {
t.Fatalf("expected node host to be populated: %#v", node)
}
byHost[host] = node
}
jp := byHost["jp-xhttp.svc.plus"]
if jp == nil {
t.Fatalf("expected jp-xhttp.svc.plus node in response: %#v", byHost)
}
if got, _ := jp["id"].(string); got != "jp-xhttp.svc.plus" {
t.Fatalf("expected node id to match host, got %q", got)
}
if got, _ := jp["name"].(string); got != "Japan Node" {
t.Fatalf("expected node name to preserve display name, got %q", got)
}
if got, _ := jp["display_name"].(string); got != "Japan Node" {
t.Fatalf("expected display_name to preserve display name, got %q", got)
}
if got, _ := jp["server_name"].(string); got != "jp-xhttp.svc.plus" {
t.Fatalf("expected server_name to match host, got %q", got)
}
if got, _ := jp["uri_scheme_xhttp"].(string); strings.TrimSpace(got) == "" {
t.Fatalf("expected uri_scheme_xhttp to be populated")
}
}
func TestResendVerificationEndpoint(t *testing.T) {
gin.SetMode(gin.TestMode)

View File

@ -92,6 +92,7 @@ func (h *handler) respondSyncConfigSnapshot(c *gin.Context) {
for _, host := range hosts {
nodeName := resolveNodeName(host, registeredNames)
countryCode := countryCodeForHost(host)
nodeID := host
vlessURI := renderVLESSURIScheme(xhttpScheme, map[string]string{
"UUID": proxyUUID,
"DOMAIN": host,
@ -104,8 +105,10 @@ func (h *handler) respondSyncConfigSnapshot(c *gin.Context) {
})
profiles = append(profiles, gin.H{
"id": strings.TrimSpace(user.ID),
"id": nodeID,
"remark": nodeName,
"display_name": nodeName,
"host": host,
"address": host,
"port": 443,
"uuid": proxyUUID,
@ -117,19 +120,24 @@ func (h *handler) respondSyncConfigSnapshot(c *gin.Context) {
"vless_uri": vlessURI,
})
nodes = append(nodes, gin.H{
"id": strings.TrimSpace(user.ID),
"name": nodeName,
"protocol": "vless",
"transport": "xhttp",
"security": "tls",
"address": host,
"port": 443,
"uuid": proxyUUID,
"flow": "",
"source": "server",
"country_code": countryCode,
"updated_at": updatedAt,
"vless_uri": vlessURI,
"id": nodeID,
"name": nodeName,
"display_name": nodeName,
"remark": nodeName,
"host": host,
"protocol": "vless",
"transport": "xhttp",
"security": "tls",
"address": host,
"port": 443,
"server_name": host,
"uuid": proxyUUID,
"flow": "",
"source": "server",
"country_code": countryCode,
"updated_at": updatedAt,
"vless_uri": vlessURI,
"uri_scheme_xhttp": vlessURI,
})
}
}