|
@@ -214,7 +214,7 @@ func (j *LdapSyncJob) batchSetEnable(ib *model.Inbound, emails []string, enable
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
- // Подготовка JSON для массового обновления
|
|
|
|
|
|
+ // Prepare JSON for mass update
|
|
clients := make([]model.Client, 0, len(emails))
|
|
clients := make([]model.Client, 0, len(emails))
|
|
for _, email := range emails {
|
|
for _, email := range emails {
|
|
clients = append(clients, model.Client{
|
|
clients = append(clients, model.Client{
|
|
@@ -238,7 +238,7 @@ func (j *LdapSyncJob) batchSetEnable(ib *model.Inbound, emails []string, enable
|
|
j.xrayService.SetToNeedRestart()
|
|
j.xrayService.SetToNeedRestart()
|
|
}
|
|
}
|
|
|
|
|
|
-// deleteClientsNotInLDAP performs batch deletion of clients not in LDAP
|
|
|
|
|
|
+// deleteClientsNotInLDAP deletes clients not in LDAP using batches and a single restart
|
|
func (j *LdapSyncJob) deleteClientsNotInLDAP(inboundTag string, ldapEmails map[string]struct{}) {
|
|
func (j *LdapSyncJob) deleteClientsNotInLDAP(inboundTag string, ldapEmails map[string]struct{}) {
|
|
inbounds, err := j.inboundService.GetAllInbounds()
|
|
inbounds, err := j.inboundService.GetAllInbounds()
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -246,22 +246,24 @@ func (j *LdapSyncJob) deleteClientsNotInLDAP(inboundTag string, ldapEmails map[s
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ batchSize := 50 // clients in 1 batch
|
|
|
|
+ restartNeeded := false
|
|
|
|
+
|
|
for _, ib := range inbounds {
|
|
for _, ib := range inbounds {
|
|
if ib.Tag != inboundTag {
|
|
if ib.Tag != inboundTag {
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
clients, err := j.inboundService.GetClients(ib)
|
|
clients, err := j.inboundService.GetClients(ib)
|
|
if err != nil {
|
|
if err != nil {
|
|
|
|
+ logger.Warningf("Failed to get clients for inbound %s: %v", ib.Tag, err)
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
|
|
|
|
- // Сбор клиентов для удаления
|
|
|
|
|
|
+ // Collect clients for deletion
|
|
toDelete := []model.Client{}
|
|
toDelete := []model.Client{}
|
|
for _, c := range clients {
|
|
for _, c := range clients {
|
|
if _, ok := ldapEmails[c.Email]; !ok {
|
|
if _, ok := ldapEmails[c.Email]; !ok {
|
|
- // Use appropriate field depending on protocol
|
|
|
|
- client := model.Client{Email: c.Email, ID: c.ID, Password: c.Password}
|
|
|
|
- toDelete = append(toDelete, client)
|
|
|
|
|
|
+ toDelete = append(toDelete, c)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -269,21 +271,47 @@ func (j *LdapSyncJob) deleteClientsNotInLDAP(inboundTag string, ldapEmails map[s
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
|
|
|
|
- payload := &model.Inbound{
|
|
|
|
- Id: ib.Id,
|
|
|
|
- Settings: j.clientsToJSON(toDelete),
|
|
|
|
- }
|
|
|
|
|
|
+ // Delete in batches
|
|
|
|
+ for i := 0; i < len(toDelete); i += batchSize {
|
|
|
|
+ end := i + batchSize
|
|
|
|
+ if end > len(toDelete) {
|
|
|
|
+ end = len(toDelete)
|
|
|
|
+ }
|
|
|
|
+ batch := toDelete[i:end]
|
|
|
|
+
|
|
|
|
+ for _, c := range batch {
|
|
|
|
+ var clientKey string
|
|
|
|
+ switch ib.Protocol {
|
|
|
|
+ case model.Trojan:
|
|
|
|
+ clientKey = c.Password
|
|
|
|
+ case model.Shadowsocks:
|
|
|
|
+ clientKey = c.Email
|
|
|
|
+ default: // vless/vmess
|
|
|
|
+ clientKey = c.ID
|
|
|
|
+ }
|
|
|
|
|
|
- if _, err := j.inboundService.DelInboundClient(payload.Id, payload.Settings); err != nil {
|
|
|
|
- logger.Warningf("Batch delete failed for inbound %s: %v", ib.Tag, err)
|
|
|
|
- } else {
|
|
|
|
- logger.Infof("Batch deleted %d clients from inbound %s", len(toDelete), ib.Tag)
|
|
|
|
- j.xrayService.SetToNeedRestart()
|
|
|
|
|
|
+ if _, err := j.inboundService.DelInboundClient(ib.Id, clientKey); err != nil {
|
|
|
|
+ logger.Warningf("Failed to delete client %s from inbound id=%d(tag=%s): %v",
|
|
|
|
+ c.Email, ib.Id, ib.Tag, err)
|
|
|
|
+ } else {
|
|
|
|
+ logger.Infof("Deleted client %s from inbound id=%d(tag=%s)",
|
|
|
|
+ c.Email, ib.Id, ib.Tag)
|
|
|
|
+ // do not restart here
|
|
|
|
+ restartNeeded = true
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // One time after all batches
|
|
|
|
+ if restartNeeded {
|
|
|
|
+ j.xrayService.SetToNeedRestart()
|
|
|
|
+ logger.Info("Xray restart scheduled after batch deletion")
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-// clientsToJSON сериализует массив клиентов в JSON
|
|
|
|
|
|
+
|
|
|
|
+// clientsToJSON serializes an array of clients to JSON
|
|
func (j *LdapSyncJob) clientsToJSON(clients []model.Client) string {
|
|
func (j *LdapSyncJob) clientsToJSON(clients []model.Client) string {
|
|
b := strings.Builder{}
|
|
b := strings.Builder{}
|
|
b.WriteString("{\"clients\":[")
|
|
b.WriteString("{\"clients\":[")
|