client_inbound_apply.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  1. package service
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "strings"
  7. "time"
  8. "github.com/mhsanaei/3x-ui/v3/internal/database"
  9. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  10. "github.com/mhsanaei/3x-ui/v3/internal/logger"
  11. "github.com/mhsanaei/3x-ui/v3/internal/util/common"
  12. "github.com/mhsanaei/3x-ui/v3/internal/util/random"
  13. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  14. )
  15. // delInboundClients removes several clients from a single inbound in one pass:
  16. // one settings rewrite, one runtime sweep, one Save and one SyncInbound for the
  17. // whole batch, instead of repeating the full per-client cycle. It mirrors the
  18. // semantics of DelInboundClientByEmail for each removed client. needRestart is
  19. // the OR across all removals.
  20. func (s *ClientService) delInboundClients(inboundSvc *InboundService, inboundId int, recs []*model.ClientRecord, keepTraffic bool) (bool, error) {
  21. if len(recs) == 0 {
  22. return false, nil
  23. }
  24. defer lockInbound(inboundId).Unlock()
  25. oldInbound, err := inboundSvc.GetInbound(inboundId)
  26. if err != nil {
  27. logger.Error("Load Old Data Error")
  28. return false, err
  29. }
  30. var settings map[string]any
  31. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  32. return false, err
  33. }
  34. // Match by email — the client's stable identity (see Delete). Removes every
  35. // entry carrying a wanted email, independent of credential drift.
  36. wanted := make(map[string]struct{}, len(recs))
  37. for _, rec := range recs {
  38. if rec.Email != "" {
  39. wanted[rec.Email] = struct{}{}
  40. }
  41. }
  42. interfaceClients, ok := settings["clients"].([]any)
  43. if !ok {
  44. return false, common.NewError("invalid clients format in inbound settings")
  45. }
  46. type removedClient struct {
  47. email string
  48. needApiDel bool
  49. }
  50. removed := make([]removedClient, 0, len(wanted))
  51. newClients := make([]any, 0, len(interfaceClients))
  52. for _, client := range interfaceClients {
  53. c, ok := client.(map[string]any)
  54. if !ok {
  55. newClients = append(newClients, client)
  56. continue
  57. }
  58. email, _ := c["email"].(string)
  59. if _, hit := wanted[email]; hit && email != "" {
  60. enable, _ := c["enable"].(bool)
  61. removed = append(removed, removedClient{email: email, needApiDel: enable})
  62. continue
  63. }
  64. newClients = append(newClients, client)
  65. }
  66. if len(removed) == 0 {
  67. return false, nil
  68. }
  69. db := database.GetDB()
  70. newClients = compactOrphans(db, newClients)
  71. if newClients == nil {
  72. newClients = []any{}
  73. }
  74. settings["clients"] = newClients
  75. newSettings, err := json.MarshalIndent(settings, "", " ")
  76. if err != nil {
  77. return false, err
  78. }
  79. oldInbound.Settings = string(newSettings)
  80. var sharedSet map[string]bool
  81. if !keepTraffic {
  82. removedEmails := make([]string, 0, len(removed))
  83. for _, r := range removed {
  84. if r.email != "" {
  85. removedEmails = append(removedEmails, r.email)
  86. }
  87. }
  88. var sharedErr error
  89. sharedSet, sharedErr = inboundSvc.emailsUsedByOtherInbounds(removedEmails, inboundId)
  90. if sharedErr != nil {
  91. return false, sharedErr
  92. }
  93. }
  94. needRestart := false
  95. markDirty := false
  96. for _, r := range removed {
  97. email := r.email
  98. emailShared := sharedSet[strings.ToLower(strings.TrimSpace(email))]
  99. if !emailShared && !keepTraffic {
  100. if err := inboundSvc.DelClientIPs(db, email); err != nil {
  101. logger.Error("Error in delete client IPs")
  102. return needRestart, err
  103. }
  104. }
  105. if len(email) > 0 {
  106. var enables []bool
  107. if err := db.Model(xray.ClientTraffic{}).Where("email = ?", email).Limit(1).Pluck("enable", &enables).Error; err != nil {
  108. logger.Error("Get stats error")
  109. return needRestart, err
  110. }
  111. notDepleted := len(enables) > 0 && enables[0]
  112. if !emailShared && !keepTraffic {
  113. if err := inboundSvc.DelClientStat(db, email); err != nil {
  114. logger.Error("Delete stats Data Error")
  115. return needRestart, err
  116. }
  117. }
  118. if r.needApiDel && notDepleted && oldInbound.NodeID == nil {
  119. rt, rterr := inboundSvc.runtimeFor(oldInbound)
  120. if rterr != nil {
  121. needRestart = true
  122. } else if err1 := rt.RemoveUser(context.Background(), oldInbound, email); err1 != nil {
  123. if !strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  124. needRestart = true
  125. }
  126. }
  127. }
  128. }
  129. if oldInbound.NodeID != nil && len(email) > 0 {
  130. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  131. if perr != nil {
  132. return needRestart, perr
  133. }
  134. if dirty {
  135. markDirty = true
  136. }
  137. if push {
  138. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  139. logger.Warning("Error in deleting client on", rt.Name(), ":", err1)
  140. markDirty = true
  141. }
  142. }
  143. }
  144. }
  145. if err := db.Save(oldInbound).Error; err != nil {
  146. return needRestart, err
  147. }
  148. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  149. if gcErr != nil {
  150. return needRestart, gcErr
  151. }
  152. if err := s.SyncInbound(db, inboundId, finalClients); err != nil {
  153. return needRestart, err
  154. }
  155. if markDirty && oldInbound.NodeID != nil {
  156. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  157. logger.Warning("mark node dirty failed:", dErr)
  158. }
  159. }
  160. return needRestart, nil
  161. }
  162. func (s *ClientService) checkEmailsExistForClients(inboundSvc *InboundService, clients []model.Client, emailSubIDs map[string]string) (string, error) {
  163. if emailSubIDs == nil {
  164. var err error
  165. emailSubIDs, err = inboundSvc.getAllEmailSubIDs()
  166. if err != nil {
  167. return "", err
  168. }
  169. }
  170. seen := make(map[string]string, len(clients))
  171. for _, client := range clients {
  172. if client.Email == "" {
  173. continue
  174. }
  175. key := strings.ToLower(client.Email)
  176. if prev, ok := seen[key]; ok {
  177. if prev != client.SubID || client.SubID == "" {
  178. return client.Email, nil
  179. }
  180. continue
  181. }
  182. seen[key] = client.SubID
  183. if existingSub, ok := emailSubIDs[key]; ok {
  184. if client.SubID == "" || existingSub == "" || existingSub != client.SubID {
  185. return client.Email, nil
  186. }
  187. }
  188. }
  189. return "", nil
  190. }
  191. func (s *ClientService) AddInboundClient(inboundSvc *InboundService, data *model.Inbound) (bool, error) {
  192. return s.addInboundClient(inboundSvc, data, nil)
  193. }
  194. // addInboundClient is AddInboundClient with an optional precomputed email→subId
  195. // map. Bulk callers pass a single snapshot so the global getAllEmailSubIDs scan
  196. // runs once for the whole batch instead of once per target inbound; a nil map
  197. // makes it compute its own (the single-add path).
  198. func (s *ClientService) addInboundClient(inboundSvc *InboundService, data *model.Inbound, emailSubIDs map[string]string) (bool, error) {
  199. defer lockInbound(data.Id).Unlock()
  200. clients, err := inboundSvc.GetClients(data)
  201. if err != nil {
  202. return false, err
  203. }
  204. var settings map[string]any
  205. err = json.Unmarshal([]byte(data.Settings), &settings)
  206. if err != nil {
  207. return false, err
  208. }
  209. interfaceClients := settings["clients"].([]any)
  210. nowTs := time.Now().Unix() * 1000
  211. for i := range interfaceClients {
  212. if cm, ok := interfaceClients[i].(map[string]any); ok {
  213. if _, ok2 := cm["created_at"]; !ok2 {
  214. cm["created_at"] = nowTs
  215. }
  216. cm["updated_at"] = nowTs
  217. existingSub, _ := cm["subId"].(string)
  218. if strings.TrimSpace(existingSub) == "" {
  219. cm["subId"] = random.NumLower(16)
  220. }
  221. interfaceClients[i] = cm
  222. }
  223. }
  224. existEmail, err := s.checkEmailsExistForClients(inboundSvc, clients, emailSubIDs)
  225. if err != nil {
  226. return false, err
  227. }
  228. if existEmail != "" {
  229. return false, common.NewError("Duplicate email:", existEmail)
  230. }
  231. oldInbound, err := inboundSvc.GetInbound(data.Id)
  232. if err != nil {
  233. return false, err
  234. }
  235. for _, client := range clients {
  236. if strings.TrimSpace(client.Email) == "" {
  237. return false, common.NewError("client email is required")
  238. }
  239. switch oldInbound.Protocol {
  240. case "trojan":
  241. if client.Password == "" {
  242. return false, common.NewError("empty client ID")
  243. }
  244. case "shadowsocks":
  245. if client.Email == "" {
  246. return false, common.NewError("empty client ID")
  247. }
  248. case "hysteria":
  249. if client.Auth == "" {
  250. return false, common.NewError("empty client ID")
  251. }
  252. default:
  253. if client.ID == "" {
  254. return false, common.NewError("empty client ID")
  255. }
  256. }
  257. }
  258. var oldSettings map[string]any
  259. err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
  260. if err != nil {
  261. return false, err
  262. }
  263. if oldInbound.Protocol == model.Shadowsocks {
  264. applyShadowsocksClientMethod(interfaceClients, oldSettings)
  265. }
  266. oldClients := oldSettings["clients"].([]any)
  267. oldClients = compactOrphans(database.GetDB(), oldClients)
  268. oldClients = append(oldClients, interfaceClients...)
  269. oldSettings["clients"] = oldClients
  270. newSettings, err := json.MarshalIndent(oldSettings, "", " ")
  271. if err != nil {
  272. return false, err
  273. }
  274. oldInbound.Settings = string(newSettings)
  275. db := database.GetDB()
  276. tx := db.Begin()
  277. markDirty := false
  278. defer func() {
  279. if err != nil {
  280. tx.Rollback()
  281. return
  282. }
  283. tx.Commit()
  284. if markDirty && oldInbound.NodeID != nil {
  285. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  286. logger.Warning("mark node dirty failed:", dErr)
  287. }
  288. }
  289. }()
  290. needRestart := false
  291. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  292. if perr != nil {
  293. err = perr
  294. return false, err
  295. }
  296. if dirty {
  297. markDirty = true
  298. }
  299. if oldInbound.NodeID == nil {
  300. if !push {
  301. needRestart = true
  302. } else {
  303. for _, client := range clients {
  304. if len(client.Email) == 0 {
  305. needRestart = true
  306. continue
  307. }
  308. inboundSvc.AddClientStat(tx, data.Id, &client)
  309. if !client.Enable {
  310. continue
  311. }
  312. cipher := ""
  313. if oldInbound.Protocol == "shadowsocks" {
  314. cipher = oldSettings["method"].(string)
  315. }
  316. err1 := rt.AddUser(context.Background(), oldInbound, map[string]any{
  317. "email": client.Email,
  318. "id": client.ID,
  319. "auth": client.Auth,
  320. "security": client.Security,
  321. "flow": client.Flow,
  322. "password": client.Password,
  323. "cipher": cipher,
  324. })
  325. if err1 == nil {
  326. logger.Debug("Client added on", rt.Name(), ":", client.Email)
  327. } else {
  328. logger.Debug("Error in adding client on", rt.Name(), ":", err1)
  329. needRestart = true
  330. }
  331. }
  332. }
  333. } else {
  334. for _, client := range clients {
  335. if len(client.Email) > 0 {
  336. inboundSvc.AddClientStat(tx, data.Id, &client)
  337. }
  338. if push {
  339. if err1 := rt.AddClient(context.Background(), oldInbound, client); err1 != nil {
  340. logger.Warning("Error in adding client on", rt.Name(), ":", err1)
  341. markDirty = true
  342. push = false
  343. }
  344. }
  345. }
  346. }
  347. if err = tx.Save(oldInbound).Error; err != nil {
  348. return false, err
  349. }
  350. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  351. if gcErr != nil {
  352. err = gcErr
  353. return false, err
  354. }
  355. if err = s.SyncInbound(tx, oldInbound.Id, finalClients); err != nil {
  356. return false, err
  357. }
  358. return needRestart, nil
  359. }
  360. func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *model.Inbound, oldEmail string) (bool, error) {
  361. defer lockInbound(data.Id).Unlock()
  362. clients, err := inboundSvc.GetClients(data)
  363. if err != nil {
  364. return false, err
  365. }
  366. var settings map[string]any
  367. err = json.Unmarshal([]byte(data.Settings), &settings)
  368. if err != nil {
  369. return false, err
  370. }
  371. interfaceClients := settings["clients"].([]any)
  372. oldInbound, err := inboundSvc.GetInbound(data.Id)
  373. if err != nil {
  374. return false, err
  375. }
  376. oldClients, err := inboundSvc.GetClients(oldInbound)
  377. if err != nil {
  378. return false, err
  379. }
  380. newClientId := ""
  381. switch oldInbound.Protocol {
  382. case "trojan":
  383. newClientId = clients[0].Password
  384. case "shadowsocks":
  385. newClientId = clients[0].Email
  386. case "hysteria":
  387. newClientId = clients[0].Auth
  388. default:
  389. newClientId = clients[0].ID
  390. }
  391. // Locate the client to replace by email — the client's stable identity.
  392. // Credentials (uuid/password/auth) can drift from the inbound JSON, so they
  393. // are never used for matching.
  394. clientIndex := -1
  395. for index, oldClient := range oldClients {
  396. if strings.EqualFold(oldClient.Email, oldEmail) {
  397. oldEmail = oldClient.Email
  398. clientIndex = index
  399. break
  400. }
  401. }
  402. if newClientId == "" || clientIndex == -1 {
  403. return false, common.NewError("empty client ID")
  404. }
  405. if strings.TrimSpace(clients[0].Email) == "" {
  406. return false, common.NewError("client email is required")
  407. }
  408. if clients[0].Email != oldEmail {
  409. existEmail, err := s.checkEmailsExistForClients(inboundSvc, clients, nil)
  410. if err != nil {
  411. return false, err
  412. }
  413. if existEmail != "" {
  414. return false, common.NewError("Duplicate email:", existEmail)
  415. }
  416. }
  417. var oldSettings map[string]any
  418. err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
  419. if err != nil {
  420. return false, err
  421. }
  422. settingsClients := oldSettings["clients"].([]any)
  423. var preservedCreated any
  424. var preservedSubID string
  425. if clientIndex >= 0 && clientIndex < len(settingsClients) {
  426. if oldMap, ok := settingsClients[clientIndex].(map[string]any); ok {
  427. if v, ok2 := oldMap["created_at"]; ok2 {
  428. preservedCreated = v
  429. }
  430. preservedSubID, _ = oldMap["subId"].(string)
  431. }
  432. }
  433. if len(interfaceClients) > 0 {
  434. if newMap, ok := interfaceClients[0].(map[string]any); ok {
  435. if preservedCreated == nil {
  436. preservedCreated = time.Now().Unix() * 1000
  437. }
  438. newMap["created_at"] = preservedCreated
  439. newMap["updated_at"] = time.Now().Unix() * 1000
  440. newSub, _ := newMap["subId"].(string)
  441. if strings.TrimSpace(newSub) == "" {
  442. if strings.TrimSpace(preservedSubID) != "" {
  443. newMap["subId"] = preservedSubID
  444. } else {
  445. newMap["subId"] = random.NumLower(16)
  446. }
  447. }
  448. interfaceClients[0] = newMap
  449. }
  450. }
  451. if oldInbound.Protocol == model.Shadowsocks {
  452. applyShadowsocksClientMethod(interfaceClients, oldSettings)
  453. }
  454. settingsClients[clientIndex] = interfaceClients[0]
  455. oldSettings["clients"] = settingsClients
  456. if oldInbound.Protocol == model.VLESS {
  457. hasVisionFlow := false
  458. for _, c := range settingsClients {
  459. cm, ok := c.(map[string]any)
  460. if !ok {
  461. continue
  462. }
  463. if flow, _ := cm["flow"].(string); flow == "xtls-rprx-vision" {
  464. hasVisionFlow = true
  465. break
  466. }
  467. }
  468. if !hasVisionFlow {
  469. delete(oldSettings, "testseed")
  470. }
  471. }
  472. newSettings, err := json.MarshalIndent(oldSettings, "", " ")
  473. if err != nil {
  474. return false, err
  475. }
  476. oldInbound.Settings = string(newSettings)
  477. db := database.GetDB()
  478. tx := db.Begin()
  479. markDirty := false
  480. defer func() {
  481. if err != nil {
  482. tx.Rollback()
  483. return
  484. }
  485. tx.Commit()
  486. if markDirty && oldInbound.NodeID != nil {
  487. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  488. logger.Warning("mark node dirty failed:", dErr)
  489. }
  490. }
  491. }()
  492. if len(clients[0].Email) > 0 {
  493. if len(oldEmail) > 0 {
  494. emailUnchanged := strings.EqualFold(oldEmail, clients[0].Email)
  495. targetExists := int64(0)
  496. if !emailUnchanged {
  497. if err = tx.Model(xray.ClientTraffic{}).Where("email = ?", clients[0].Email).Count(&targetExists).Error; err != nil {
  498. return false, err
  499. }
  500. }
  501. if emailUnchanged || targetExists == 0 {
  502. err = inboundSvc.UpdateClientStat(tx, oldEmail, &clients[0])
  503. if err != nil {
  504. return false, err
  505. }
  506. err = inboundSvc.UpdateClientIPs(tx, oldEmail, clients[0].Email)
  507. if err != nil {
  508. return false, err
  509. }
  510. } else {
  511. stillUsed, sErr := inboundSvc.emailUsedByOtherInbounds(oldEmail, data.Id)
  512. if sErr != nil {
  513. return false, sErr
  514. }
  515. if !stillUsed {
  516. if err = inboundSvc.DelClientStat(tx, oldEmail); err != nil {
  517. return false, err
  518. }
  519. if err = inboundSvc.DelClientIPs(tx, oldEmail); err != nil {
  520. return false, err
  521. }
  522. }
  523. if err = inboundSvc.UpdateClientStat(tx, clients[0].Email, &clients[0]); err != nil {
  524. return false, err
  525. }
  526. }
  527. } else {
  528. inboundSvc.AddClientStat(tx, data.Id, &clients[0])
  529. }
  530. } else {
  531. stillUsed, err := inboundSvc.emailUsedByOtherInbounds(oldEmail, data.Id)
  532. if err != nil {
  533. return false, err
  534. }
  535. if !stillUsed {
  536. err = inboundSvc.DelClientStat(tx, oldEmail)
  537. if err != nil {
  538. return false, err
  539. }
  540. err = inboundSvc.DelClientIPs(tx, oldEmail)
  541. if err != nil {
  542. return false, err
  543. }
  544. }
  545. }
  546. needRestart := false
  547. if len(oldEmail) > 0 {
  548. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  549. if perr != nil {
  550. err = perr
  551. return false, err
  552. }
  553. if dirty {
  554. markDirty = true
  555. }
  556. if oldInbound.NodeID == nil {
  557. if !push {
  558. needRestart = true
  559. } else {
  560. if oldClients[clientIndex].Enable {
  561. err1 := rt.RemoveUser(context.Background(), oldInbound, oldEmail)
  562. if err1 == nil {
  563. logger.Debug("Old client deleted on", rt.Name(), ":", oldEmail)
  564. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", oldEmail)) {
  565. logger.Debug("User is already deleted. Nothing to do more...")
  566. } else {
  567. logger.Debug("Error in deleting client on", rt.Name(), ":", err1)
  568. needRestart = true
  569. }
  570. }
  571. if clients[0].Enable {
  572. cipher := ""
  573. if oldInbound.Protocol == "shadowsocks" {
  574. cipher = oldSettings["method"].(string)
  575. }
  576. err1 := rt.AddUser(context.Background(), oldInbound, map[string]any{
  577. "email": clients[0].Email,
  578. "id": clients[0].ID,
  579. "security": clients[0].Security,
  580. "flow": clients[0].Flow,
  581. "auth": clients[0].Auth,
  582. "password": clients[0].Password,
  583. "cipher": cipher,
  584. })
  585. if err1 == nil {
  586. logger.Debug("Client edited on", rt.Name(), ":", clients[0].Email)
  587. } else {
  588. logger.Debug("Error in adding client on", rt.Name(), ":", err1)
  589. needRestart = true
  590. }
  591. }
  592. }
  593. } else if push {
  594. if err1 := rt.UpdateUser(context.Background(), oldInbound, oldEmail, clients[0]); err1 != nil {
  595. logger.Warning("Error in updating client on", rt.Name(), ":", err1)
  596. markDirty = true
  597. }
  598. }
  599. } else {
  600. logger.Debug("Client old email not found")
  601. needRestart = true
  602. }
  603. if err = tx.Save(oldInbound).Error; err != nil {
  604. return false, err
  605. }
  606. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  607. if gcErr != nil {
  608. err = gcErr
  609. return false, err
  610. }
  611. if err = s.SyncInbound(tx, oldInbound.Id, finalClients); err != nil {
  612. return false, err
  613. }
  614. return needRestart, nil
  615. }
  616. func (s *ClientService) DelInboundClientByEmail(inboundSvc *InboundService, inboundId int, email string, keepTraffic bool) (bool, error) {
  617. defer lockInbound(inboundId).Unlock()
  618. oldInbound, err := inboundSvc.GetInbound(inboundId)
  619. if err != nil {
  620. logger.Error("Load Old Data Error")
  621. return false, err
  622. }
  623. var settings map[string]any
  624. if err := json.Unmarshal([]byte(oldInbound.Settings), &settings); err != nil {
  625. return false, err
  626. }
  627. interfaceClients, ok := settings["clients"].([]any)
  628. if !ok {
  629. return false, common.NewError("invalid clients format in inbound settings")
  630. }
  631. var newClients []any
  632. needApiDel := false
  633. found := false
  634. for _, client := range interfaceClients {
  635. c, ok := client.(map[string]any)
  636. if !ok {
  637. continue
  638. }
  639. if cEmail, ok := c["email"].(string); ok && cEmail == email {
  640. found = true
  641. needApiDel, _ = c["enable"].(bool)
  642. } else {
  643. newClients = append(newClients, client)
  644. }
  645. }
  646. if !found {
  647. return false, fmt.Errorf("%w for email: %s", ErrClientNotInInbound, email)
  648. }
  649. db := database.GetDB()
  650. newClients = compactOrphans(db, newClients)
  651. if newClients == nil {
  652. newClients = []any{}
  653. }
  654. settings["clients"] = newClients
  655. newSettings, err := json.MarshalIndent(settings, "", " ")
  656. if err != nil {
  657. return false, err
  658. }
  659. oldInbound.Settings = string(newSettings)
  660. emailShared, err := inboundSvc.emailUsedByOtherInbounds(email, inboundId)
  661. if err != nil {
  662. return false, err
  663. }
  664. if !emailShared && !keepTraffic {
  665. if err := inboundSvc.DelClientIPs(db, email); err != nil {
  666. logger.Error("Error in delete client IPs")
  667. return false, err
  668. }
  669. }
  670. needRestart := false
  671. markDirty := false
  672. if len(email) > 0 && !emailShared {
  673. if !keepTraffic {
  674. traffic, err := inboundSvc.GetClientTrafficByEmail(email)
  675. if err != nil {
  676. return false, err
  677. }
  678. if traffic != nil {
  679. if err := inboundSvc.DelClientStat(db, email); err != nil {
  680. logger.Error("Delete stats Data Error")
  681. return false, err
  682. }
  683. }
  684. }
  685. if oldInbound.NodeID == nil {
  686. // Local inbound: a disabled client isn't in the running Xray, so only
  687. // a live one (needApiDel) needs an API removal.
  688. if needApiDel {
  689. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  690. if perr != nil {
  691. return false, perr
  692. }
  693. if dirty {
  694. markDirty = true
  695. }
  696. if !push {
  697. needRestart = true
  698. } else if err1 := rt.RemoveUser(context.Background(), oldInbound, email); err1 == nil {
  699. logger.Debug("Client deleted on", rt.Name(), ":", email)
  700. needRestart = false
  701. } else if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
  702. logger.Debug("User is already deleted. Nothing to do more...")
  703. } else {
  704. logger.Debug("Error in deleting client on", rt.Name(), ":", email)
  705. needRestart = true
  706. }
  707. }
  708. } else {
  709. // Node inbound: propagate the delete regardless of the enable flag —
  710. // the node's own DB still carries a disabled client and would
  711. // resurrect it on the next snapshot otherwise.
  712. rt, push, dirty, perr := inboundSvc.nodePushPlan(oldInbound)
  713. if perr != nil {
  714. return false, perr
  715. }
  716. if dirty {
  717. markDirty = true
  718. }
  719. if push {
  720. if err1 := rt.DeleteUser(context.Background(), oldInbound, email); err1 != nil {
  721. logger.Warning("Error in deleting client on", rt.Name(), ":", err1)
  722. markDirty = true
  723. }
  724. }
  725. }
  726. }
  727. if err := db.Save(oldInbound).Error; err != nil {
  728. return false, err
  729. }
  730. finalClients, gcErr := inboundSvc.GetClients(oldInbound)
  731. if gcErr != nil {
  732. return false, gcErr
  733. }
  734. if err := s.SyncInbound(db, inboundId, finalClients); err != nil {
  735. return false, err
  736. }
  737. if markDirty && oldInbound.NodeID != nil {
  738. if dErr := (&NodeService{}).MarkNodeDirty(*oldInbound.NodeID); dErr != nil {
  739. logger.Warning("mark node dirty failed:", dErr)
  740. }
  741. }
  742. return needRestart, nil
  743. }
  744. func (s *ClientService) SetClientTelegramUserID(inboundSvc *InboundService, trafficId int, tgId int64) (bool, error) {
  745. traffic, inbound, err := inboundSvc.GetClientInboundByTrafficID(trafficId)
  746. if err != nil {
  747. return false, err
  748. }
  749. if inbound == nil {
  750. return false, common.NewError("Inbound Not Found For Traffic ID:", trafficId)
  751. }
  752. clientEmail := traffic.Email
  753. oldClients, err := inboundSvc.GetClients(inbound)
  754. if err != nil {
  755. return false, err
  756. }
  757. found := false
  758. for _, oldClient := range oldClients {
  759. if oldClient.Email == clientEmail {
  760. found = true
  761. break
  762. }
  763. }
  764. if !found {
  765. return false, common.NewError("Client Not Found For Email:", clientEmail)
  766. }
  767. var settings map[string]any
  768. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  769. if err != nil {
  770. return false, err
  771. }
  772. clients := settings["clients"].([]any)
  773. var newClients []any
  774. for client_index := range clients {
  775. c := clients[client_index].(map[string]any)
  776. if c["email"] == clientEmail {
  777. c["tgId"] = tgId
  778. c["updated_at"] = time.Now().Unix() * 1000
  779. newClients = append(newClients, any(c))
  780. }
  781. }
  782. settings["clients"] = newClients
  783. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  784. if err != nil {
  785. return false, err
  786. }
  787. inbound.Settings = string(modifiedSettings)
  788. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientEmail)
  789. return needRestart, err
  790. }
  791. func (s *ClientService) CheckIsEnabledByEmail(inboundSvc *InboundService, clientEmail string) (bool, error) {
  792. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  793. if err != nil {
  794. return false, err
  795. }
  796. if inbound == nil {
  797. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  798. }
  799. clients, err := inboundSvc.GetClients(inbound)
  800. if err != nil {
  801. return false, err
  802. }
  803. isEnable := false
  804. for _, client := range clients {
  805. if client.Email == clientEmail {
  806. isEnable = client.Enable
  807. break
  808. }
  809. }
  810. return isEnable, err
  811. }
  812. func (s *ClientService) ToggleClientEnableByEmail(inboundSvc *InboundService, clientEmail string) (bool, bool, error) {
  813. _, inbound, err := inboundSvc.GetClientInboundByEmail(clientEmail)
  814. if err != nil {
  815. return false, false, err
  816. }
  817. if inbound == nil {
  818. return false, false, common.NewError("Inbound Not Found For Email:", clientEmail)
  819. }
  820. oldClients, err := inboundSvc.GetClients(inbound)
  821. if err != nil {
  822. return false, false, err
  823. }
  824. found := false
  825. clientOldEnabled := false
  826. for _, oldClient := range oldClients {
  827. if oldClient.Email == clientEmail {
  828. found = true
  829. clientOldEnabled = oldClient.Enable
  830. break
  831. }
  832. }
  833. if !found {
  834. return false, false, common.NewError("Client Not Found For Email:", clientEmail)
  835. }
  836. var settings map[string]any
  837. err = json.Unmarshal([]byte(inbound.Settings), &settings)
  838. if err != nil {
  839. return false, false, err
  840. }
  841. clients := settings["clients"].([]any)
  842. var newClients []any
  843. for client_index := range clients {
  844. c := clients[client_index].(map[string]any)
  845. if c["email"] == clientEmail {
  846. c["enable"] = !clientOldEnabled
  847. c["updated_at"] = time.Now().Unix() * 1000
  848. newClients = append(newClients, any(c))
  849. }
  850. }
  851. settings["clients"] = newClients
  852. modifiedSettings, err := json.MarshalIndent(settings, "", " ")
  853. if err != nil {
  854. return false, false, err
  855. }
  856. inbound.Settings = string(modifiedSettings)
  857. needRestart, err := s.UpdateInboundClient(inboundSvc, inbound, clientEmail)
  858. if err != nil {
  859. return false, needRestart, err
  860. }
  861. return !clientOldEnabled, needRestart, nil
  862. }
  863. func (s *ClientService) SetClientEnableByEmail(inboundSvc *InboundService, clientEmail string, enable bool) (bool, bool, error) {
  864. current, err := s.CheckIsEnabledByEmail(inboundSvc, clientEmail)
  865. if err != nil {
  866. return false, false, err
  867. }
  868. if current == enable {
  869. return false, false, nil
  870. }
  871. newEnabled, needRestart, err := s.ToggleClientEnableByEmail(inboundSvc, clientEmail)
  872. if err != nil {
  873. return false, needRestart, err
  874. }
  875. return newEnabled == enable, needRestart, nil
  876. }
  877. // applyClientFieldByEmail loads the inbound currently hosting clientEmail,
  878. // confirms the client exists, applies mutate to the matching client (plus a
  879. // refreshed updated_at), and hands a single-client update payload to
  880. // UpdateInboundClient. The rebuilt clients array intentionally contains only
  881. // the matched client — that is the input contract UpdateInboundClient expects
  882. // (clients[0] is the new data; clientEmail locates the row to replace). It
  883. // backs the single-field by-email setters below.
  884. // applyClientFieldByEmail mutates a client field on every inbound the email is
  885. // attached to. A multi-inbound client is one logical identity: patching only
  886. // the first inbound's JSON would leave the siblings stale, and the next
  887. // SyncInbound over a stale sibling would revert the edit in the normalized
  888. // records (#5039).
  889. func (s *ClientService) applyClientFieldByEmail(inboundSvc *InboundService, clientEmail string, mutate func(c map[string]any)) (bool, error) {
  890. inboundIds, err := s.GetInboundIdsForEmail(database.GetDB(), clientEmail)
  891. if err != nil {
  892. return false, err
  893. }
  894. if len(inboundIds) == 0 {
  895. // Legacy fallback for clients that only live in the inbound JSON and
  896. // were never normalized into client_inbounds.
  897. _, inbound, gErr := inboundSvc.GetClientInboundByEmail(clientEmail)
  898. if gErr != nil {
  899. return false, gErr
  900. }
  901. if inbound == nil {
  902. return false, common.NewError("Inbound Not Found For Email:", clientEmail)
  903. }
  904. inboundIds = []int{inbound.Id}
  905. }
  906. needRestart := false
  907. found := false
  908. for _, ibId := range inboundIds {
  909. inbound, gErr := inboundSvc.GetInbound(ibId)
  910. if gErr != nil {
  911. return needRestart, gErr
  912. }
  913. var settings map[string]any
  914. if uErr := json.Unmarshal([]byte(inbound.Settings), &settings); uErr != nil {
  915. return needRestart, uErr
  916. }
  917. clients, _ := settings["clients"].([]any)
  918. // UpdateInboundClient expects a single-client payload, so keep only the
  919. // matching entry in the scratch copy; it splices the result back into
  920. // the inbound's full client list itself.
  921. var newClients []any
  922. for client_index := range clients {
  923. c, ok := clients[client_index].(map[string]any)
  924. if !ok {
  925. continue
  926. }
  927. if c["email"] == clientEmail {
  928. mutate(c)
  929. c["updated_at"] = time.Now().Unix() * 1000
  930. newClients = append(newClients, any(c))
  931. }
  932. }
  933. if len(newClients) == 0 {
  934. continue
  935. }
  936. found = true
  937. settings["clients"] = newClients
  938. modifiedSettings, mErr := json.MarshalIndent(settings, "", " ")
  939. if mErr != nil {
  940. return needRestart, mErr
  941. }
  942. inbound.Settings = string(modifiedSettings)
  943. nr, uErr := s.UpdateInboundClient(inboundSvc, inbound, clientEmail)
  944. if uErr != nil {
  945. return needRestart, uErr
  946. }
  947. needRestart = needRestart || nr
  948. }
  949. if !found {
  950. return needRestart, common.NewError("Client Not Found For Email:", clientEmail)
  951. }
  952. return needRestart, nil
  953. }
  954. func (s *ClientService) ResetClientIpLimitByEmail(inboundSvc *InboundService, clientEmail string, count int) (bool, error) {
  955. return s.applyClientFieldByEmail(inboundSvc, clientEmail, func(c map[string]any) {
  956. c["limitIp"] = count
  957. })
  958. }
  959. func (s *ClientService) ResetClientExpiryTimeByEmail(inboundSvc *InboundService, clientEmail string, expiry_time int64) (bool, error) {
  960. return s.applyClientFieldByEmail(inboundSvc, clientEmail, func(c map[string]any) {
  961. c["expiryTime"] = expiry_time
  962. })
  963. }
  964. func (s *ClientService) ResetClientTrafficLimitByEmail(inboundSvc *InboundService, clientEmail string, totalGB int) (bool, error) {
  965. if totalGB < 0 {
  966. return false, common.NewError("totalGB must be >= 0")
  967. }
  968. return s.applyClientFieldByEmail(inboundSvc, clientEmail, func(c map[string]any) {
  969. c["totalGB"] = totalGB * 1024 * 1024 * 1024
  970. })
  971. }