diff --git a/backend/xray/config.go b/backend/xray/config.go index e7a5a13..2b71668 100644 --- a/backend/xray/config.go +++ b/backend/xray/config.go @@ -130,12 +130,13 @@ func (i *Inbound) syncUsers(users []*common.User) { if slices.Contains(user.Inbounds, i.Tag) { account, err := api.NewVmessAccount(user) if err != nil { - log.Println("error for user", user.GetEmail(), ":", err) + log.Printf("error creating vmess account for user %s in inbound %s: %v", user.GetEmail(), i.Tag, err) continue } i.clients[user.GetEmail()] = account } } + log.Printf("inbound %s (%s): synced %d users", i.Tag, i.Protocol, len(i.clients)) case Vless: for _, user := range users { @@ -145,13 +146,14 @@ func (i *Inbound) syncUsers(users []*common.User) { if slices.Contains(user.Inbounds, i.Tag) { account, err := api.NewVlessAccount(user) if err != nil { - log.Println("error for user", user.GetEmail(), ":", err) + log.Printf("error creating vless account for user %s in inbound %s: %v", user.GetEmail(), i.Tag, err) continue } newAccount := checkVless(i, *account) i.clients[user.GetEmail()] = &newAccount } } + log.Printf("inbound %s (%s): synced %d users", i.Tag, i.Protocol, len(i.clients)) case Trojan: for _, user := range users { @@ -162,6 +164,7 @@ func (i *Inbound) syncUsers(users []*common.User) { i.clients[user.GetEmail()] = api.NewTrojanAccount(user) } } + log.Printf("inbound %s (%s): synced %d users", i.Tag, i.Protocol, len(i.clients)) case Shadowsocks: method, methodOk := i.Settings["method"].(string) @@ -186,6 +189,7 @@ func (i *Inbound) syncUsers(users []*common.User) { } } } + log.Printf("inbound %s (%s): synced %d users", i.Tag, i.Protocol, len(i.clients)) } } diff --git a/backend/xray/core.go b/backend/xray/core.go index b5c70b6..616a7d9 100644 --- a/backend/xray/core.go +++ b/backend/xray/core.go @@ -118,10 +118,15 @@ func (c *Core) Start(xConfig *Config, debugMode bool) error { accessFile, errorFile := xConfig.RemoveLogFiles() bytesConfig, err := xConfig.ToBytes() - if debugMode { - if err = c.GenerateConfigFile(bytesConfig); err != nil { - return err - } + if err != nil { + return fmt.Errorf("failed to serialize config: %w", err) + } + + // Always write config file for debugging, not just in debug mode + // This helps diagnose issues when users are missing + if err = c.GenerateConfigFile(bytesConfig); err != nil { + log.Printf("warning: failed to write config file: %v", err) + // Don't fail startup if config file write fails, Xray reads from stdin anyway } c.mu.Lock() @@ -167,6 +172,14 @@ func (c *Core) Start(xConfig *Config, debugMode bool) error { return err } + // Verify config has content before starting + if len(bytesConfig) == 0 { + return errors.New("config is empty, cannot start xray") + } + + // Log config size for debugging + log.Printf("starting xray with config size: %d bytes", len(bytesConfig)) + cmd.Stdin = bytes.NewBuffer(bytesConfig) if err = cmd.Start(); err != nil { return err @@ -258,6 +271,10 @@ func (c *Core) Restart(config *Config, debugMode bool) error { log.Println("restarting Xray core...") c.Stop() + + // Small delay to ensure previous process is fully terminated + time.Sleep(time.Millisecond * 100) + if err := c.Start(config, debugMode); err != nil { return err } diff --git a/backend/xray/user.go b/backend/xray/user.go index fca5782..7a03a87 100644 --- a/backend/xray/user.go +++ b/backend/xray/user.go @@ -165,6 +165,16 @@ func (x *Xray) SyncUser(ctx context.Context, user *common.User) error { func (x *Xray) SyncUsers(_ context.Context, users []*common.User) error { x.config.syncUsers(users) + + // Verify users were synced before restarting + totalClients := 0 + for _, inbound := range x.config.InboundConfigs { + if !inbound.exclude && inbound.clients != nil { + totalClients += len(inbound.clients) + } + } + log.Printf("syncing %d users, total clients in config: %d", len(users), totalClients) + if err := x.Restart(); err != nil { return err } @@ -210,6 +220,16 @@ func (x *Xray) UpdateUsers(ctx context.Context, users []*common.User) error { func (x *Xray) UpdateUsersAndRestart(_ context.Context, users []*common.User) error { x.config.updateUsers(users) + + // Verify users were updated before restarting + totalClients := 0 + for _, inbound := range x.config.InboundConfigs { + if !inbound.exclude && inbound.clients != nil { + totalClients += len(inbound.clients) + } + } + log.Printf("updating %d users, total clients in config: %d", len(users), totalClients) + if err := x.Restart(); err != nil { return err } diff --git a/controller/rest/user.go b/controller/rest/user.go index 896329c..40d3ec6 100644 --- a/controller/rest/user.go +++ b/controller/rest/user.go @@ -65,7 +65,13 @@ func (s *Service) SyncUsers(w http.ResponseWriter, r *http.Request) { return } - if err = s.Backend().SyncUsers(r.Context(), users.GetUsers()); err != nil { + userList := users.GetUsers() + log.Printf("SyncUsers: received %d users from panel", len(userList)) + if len(userList) == 0 { + log.Printf("WARNING: SyncUsers received 0 users - panel may not be sending users correctly") + } + + if err = s.Backend().SyncUsers(r.Context(), userList); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/controller/rpc/user.go b/controller/rpc/user.go index 2e43f14..50f0a39 100644 --- a/controller/rpc/user.go +++ b/controller/rpc/user.go @@ -35,7 +35,13 @@ func (s *Service) SyncUser(stream grpc.ClientStreamingServer[common.User, common } func (s *Service) SyncUsers(ctx context.Context, users *common.Users) (*common.Empty, error) { - if err := s.Backend().SyncUsers(ctx, users.GetUsers()); err != nil { + userList := users.GetUsers() + log.Printf("SyncUsers: received %d users from panel", len(userList)) + if len(userList) == 0 { + log.Printf("WARNING: SyncUsers received 0 users - panel may not be sending users correctly") + } + + if err := s.Backend().SyncUsers(ctx, userList); err != nil { return nil, err }