fix adding Shadowsocks 2022 users via API

This commit is contained in:
Alireza Ahmadi 2026-10-09 23:09:27 +02:00
parent 1b0f156ae5
commit c4ae0ecd12
2 changed files with 52 additions and 18 deletions

View file

@ -188,6 +188,28 @@ func (x *XrayAPI) HasOutbound(tag string) (bool, error) {
}
func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]interface{}) error {
account, err := userAccount(Protocol, user)
if err != nil || account == nil {
return err
}
client := *x.HandlerServiceClient
_, err = client.AlterInbound(context.Background(), &command.AlterInboundRequest{
Tag: inboundTag,
Operation: serial.ToTypedMessage(&command.AddUserOperation{
User: &protocol.User{
Email: user["email"].(string),
Account: account,
},
}),
})
return err
}
// userAccount builds the account Xray-core attaches to a user added at runtime;
// nil means the protocol cannot take users through the API.
func userAccount(Protocol string, user map[string]interface{}) (*serial.TypedMessage, error) {
var account *serial.TypedMessage
switch Protocol {
case "vmess":
@ -252,9 +274,8 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]in
CipherType: ssCipherType,
})
} else {
account = serial.ToTypedMessage(&shadowsocks_2022.ServerConfig{
Key: user["password"].(string),
Email: user["email"].(string),
account = serial.ToTypedMessage(&shadowsocks_2022.Account{
Key: user["password"].(string),
})
}
case "hysteria":
@ -264,25 +285,13 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]in
case "wireguard":
peer, err := wireguardPeerConfig(user)
if err != nil {
return err
return nil, err
}
account = serial.ToTypedMessage(peer)
default:
return nil
return nil, nil
}
client := *x.HandlerServiceClient
_, err := client.AlterInbound(context.Background(), &command.AlterInboundRequest{
Tag: inboundTag,
Operation: serial.ToTypedMessage(&command.AddUserOperation{
User: &protocol.User{
Email: user["email"].(string),
Account: account,
},
}),
})
return err
return account, nil
}
// wireguardPeerConfig turns a panel client entry into the peer config Xray

View file

@ -4,6 +4,8 @@ import (
"encoding/base64"
"encoding/json"
"testing"
"github.com/xtls/xray-core/common/protocol"
)
// A payload built from a model.Client keeps its Go types; one read back out of
@ -153,3 +155,26 @@ func TestToUint32(t *testing.T) {
})
}
}
// Every account handed to AlterInbound has to turn into a memory user, or the
// core refuses it and the panel falls back to restarting Xray.
func TestUserAccountsBecomeMemoryUsers(t *testing.T) {
cases := map[string]map[string]interface{}{
"vmess": {"id": "5783a3e7-e373-51cd-8642-c83782b807c5"},
"vless": {"id": "5783a3e7-e373-51cd-8642-c83782b807c5", "flow": ""},
"trojan": {"password": "pw"},
"shadowsocks": {"cipher": "", "password": "MDEyMzQ1Njc4OWFiY2RlZg=="},
"hysteria": {"auth": "pw"},
}
for proto, user := range cases {
user["email"] = proto + "@test"
account, err := userAccount(proto, user)
if err != nil || account == nil {
t.Fatalf("%s: account %v, err %v", proto, account, err)
}
u := &protocol.User{Email: proto + "@test", Account: account}
if _, err := u.ToMemoryUser(); err != nil {
t.Errorf("%s: %v", proto, err)
}
}
}