1
0

fallback.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package service
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/mhsanaei/3x-ui/v3/database"
  6. "github.com/mhsanaei/3x-ui/v3/database/model"
  7. "gorm.io/gorm"
  8. )
  9. type FallbackService struct{}
  10. // FallbackInput is the payload shape POSTed by the inbound form.
  11. type FallbackInput struct {
  12. ChildId int `json:"childId"`
  13. Name string `json:"name"`
  14. Alpn string `json:"alpn"`
  15. Path string `json:"path"`
  16. Dest string `json:"dest"`
  17. Xver int `json:"xver"`
  18. SortOrder int `json:"sortOrder"`
  19. }
  20. // GetByMaster returns every fallback rule attached to the master inbound.
  21. func (s *FallbackService) GetByMaster(masterId int) ([]model.InboundFallback, error) {
  22. var rows []model.InboundFallback
  23. err := database.GetDB().
  24. Where("master_id = ?", masterId).
  25. Order("sort_order ASC, id ASC").
  26. Find(&rows).Error
  27. if err != nil {
  28. return nil, err
  29. }
  30. return rows, nil
  31. }
  32. // GetParentForChild finds the first fallback rule that points at childId.
  33. // Used by client-link generation: when a child inbound is attached as a
  34. // fallback, its client links should advertise the master's address+port
  35. // and TLS instead of the child's loopback listen.
  36. func (s *FallbackService) GetParentForChild(childId int) (*model.InboundFallback, error) {
  37. var row model.InboundFallback
  38. err := database.GetDB().
  39. Where("child_id = ?", childId).
  40. Order("sort_order ASC, id ASC").
  41. First(&row).Error
  42. if err == gorm.ErrRecordNotFound {
  43. return nil, nil
  44. }
  45. if err != nil {
  46. return nil, err
  47. }
  48. return &row, nil
  49. }
  50. // SetByMaster replaces the master's entire fallback list atomically.
  51. func (s *FallbackService) SetByMaster(masterId int, items []FallbackInput) error {
  52. db := database.GetDB()
  53. return db.Transaction(func(tx *gorm.DB) error {
  54. if err := tx.Where("master_id = ?", masterId).Delete(&model.InboundFallback{}).Error; err != nil {
  55. return err
  56. }
  57. for i, c := range items {
  58. if c.ChildId <= 0 || c.ChildId == masterId {
  59. continue
  60. }
  61. row := model.InboundFallback{
  62. MasterId: masterId,
  63. ChildId: c.ChildId,
  64. Name: c.Name,
  65. Alpn: c.Alpn,
  66. Path: c.Path,
  67. Dest: c.Dest,
  68. Xver: c.Xver,
  69. SortOrder: c.SortOrder,
  70. }
  71. if row.SortOrder == 0 {
  72. row.SortOrder = i
  73. }
  74. if err := tx.Create(&row).Error; err != nil {
  75. return err
  76. }
  77. }
  78. return nil
  79. })
  80. }
  81. func (s *FallbackService) BuildFallbacksJSON(tx *gorm.DB, masterId int) ([]map[string]any, error) {
  82. if tx == nil {
  83. tx = database.GetDB()
  84. }
  85. var rows []model.InboundFallback
  86. err := tx.Where("master_id = ?", masterId).
  87. Order("sort_order ASC, id ASC").
  88. Find(&rows).Error
  89. if err != nil {
  90. return nil, err
  91. }
  92. if len(rows) == 0 {
  93. return nil, nil
  94. }
  95. childIds := make([]int, 0, len(rows))
  96. for i := range rows {
  97. childIds = append(childIds, rows[i].ChildId)
  98. }
  99. var children []model.Inbound
  100. if err := tx.Where("id IN ?", childIds).Find(&children).Error; err != nil {
  101. return nil, err
  102. }
  103. byId := make(map[int]*model.Inbound, len(children))
  104. for i := range children {
  105. byId[children[i].Id] = &children[i]
  106. }
  107. out := make([]map[string]any, 0, len(rows))
  108. for _, r := range rows {
  109. child, ok := byId[r.ChildId]
  110. if !ok {
  111. continue
  112. }
  113. dest := r.Dest
  114. if dest == "" {
  115. listen := strings.TrimSpace(child.Listen)
  116. if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" {
  117. listen = "127.0.0.1"
  118. }
  119. dest = fmt.Sprintf("%s:%d", listen, child.Port)
  120. }
  121. entry := map[string]any{
  122. "dest": dest,
  123. }
  124. if r.Name != "" {
  125. entry["name"] = r.Name
  126. }
  127. if r.Alpn != "" {
  128. entry["alpn"] = r.Alpn
  129. }
  130. if r.Path != "" {
  131. entry["path"] = r.Path
  132. }
  133. if r.Xver > 0 {
  134. entry["xver"] = r.Xver
  135. }
  136. out = append(out, entry)
  137. }
  138. return out, nil
  139. }