1
0

inbound_flow_restore.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package service
  2. import (
  3. "encoding/json"
  4. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  5. "gorm.io/gorm"
  6. )
  7. const visionFlow = "xtls-rprx-vision"
  8. // restoreVisionFlowForEligibleInbound re-adds the XTLS Vision flow to a VLESS
  9. // inbound's clients that lost it earlier.
  10. //
  11. // clientWithInboundFlow strips Vision from a client whenever the target inbound
  12. // is not flow-eligible at write time (e.g. an XHTTP inbound before its vlessenc
  13. // encryption is set). Nothing restored the flow when the inbound later became
  14. // eligible — an inbound edit stores its settings verbatim and never re-gates the
  15. // clients — so enabling encryption on an existing XHTTP inbound left every
  16. // client without flow, and the share links/subscriptions dropped it.
  17. //
  18. // This runs on the now-final inbound settings: when the inbound IS flow-eligible
  19. // it sets flow=Vision on each client that currently has no flow but whose
  20. // intended flow (its flow_override on a sibling inbound, via EffectiveFlowsByEmails)
  21. // is Vision. It never invents a flow for a client that has none anywhere, and it
  22. // never overwrites an explicit non-empty flow. Returns the rewritten settings
  23. // JSON and whether anything changed.
  24. func (s *InboundService) restoreVisionFlowForEligibleInbound(tx *gorm.DB, settings, streamSettings string, protocol model.Protocol) (string, bool) {
  25. if protocol != model.VLESS {
  26. return settings, false
  27. }
  28. if !inboundCanEnableTlsFlow(string(protocol), streamSettings, settings) {
  29. return settings, false
  30. }
  31. var parsed map[string]any
  32. if err := json.Unmarshal([]byte(settings), &parsed); err != nil {
  33. return settings, false
  34. }
  35. clients, ok := parsed["clients"].([]any)
  36. if !ok || len(clients) == 0 {
  37. return settings, false
  38. }
  39. // Collect empty-flow clients, then resolve their intended flow in one query.
  40. emails := make([]string, 0, len(clients))
  41. for i := range clients {
  42. cm, ok := clients[i].(map[string]any)
  43. if !ok {
  44. continue
  45. }
  46. if flow, _ := cm["flow"].(string); flow != "" {
  47. continue // respect an explicit flow (Vision or otherwise)
  48. }
  49. if email, _ := cm["email"].(string); email != "" {
  50. emails = append(emails, email)
  51. }
  52. }
  53. if len(emails) == 0 {
  54. return settings, false
  55. }
  56. intended, err := s.clientService.EffectiveFlowsByEmails(tx, emails)
  57. if err != nil {
  58. return settings, false
  59. }
  60. changed := false
  61. for i := range clients {
  62. cm, ok := clients[i].(map[string]any)
  63. if !ok {
  64. continue
  65. }
  66. if flow, _ := cm["flow"].(string); flow != "" {
  67. continue
  68. }
  69. email, _ := cm["email"].(string)
  70. if intended[email] != visionFlow {
  71. continue
  72. }
  73. cm["flow"] = visionFlow
  74. clients[i] = cm
  75. changed = true
  76. }
  77. if !changed {
  78. return settings, false
  79. }
  80. out, err := json.MarshalIndent(parsed, "", " ")
  81. if err != nil {
  82. return settings, false
  83. }
  84. return string(out), true
  85. }