custom_geo.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package controller
  2. import (
  3. "errors"
  4. "net/http"
  5. "strconv"
  6. "github.com/mhsanaei/3x-ui/v2/database/model"
  7. "github.com/mhsanaei/3x-ui/v2/logger"
  8. "github.com/mhsanaei/3x-ui/v2/web/entity"
  9. "github.com/mhsanaei/3x-ui/v2/web/service"
  10. "github.com/gin-gonic/gin"
  11. )
  12. type CustomGeoController struct {
  13. BaseController
  14. customGeoService *service.CustomGeoService
  15. }
  16. func NewCustomGeoController(g *gin.RouterGroup, customGeo *service.CustomGeoService) *CustomGeoController {
  17. a := &CustomGeoController{customGeoService: customGeo}
  18. a.initRouter(g)
  19. return a
  20. }
  21. func (a *CustomGeoController) initRouter(g *gin.RouterGroup) {
  22. g.GET("/list", a.list)
  23. g.GET("/aliases", a.aliases)
  24. g.POST("/add", a.add)
  25. g.POST("/update/:id", a.update)
  26. g.POST("/delete/:id", a.delete)
  27. g.POST("/download/:id", a.download)
  28. g.POST("/update-all", a.updateAll)
  29. }
  30. func mapCustomGeoErr(c *gin.Context, err error) error {
  31. if err == nil {
  32. return nil
  33. }
  34. switch {
  35. case errors.Is(err, service.ErrCustomGeoInvalidType):
  36. return errors.New(I18nWeb(c, "pages.index.customGeoErrInvalidType"))
  37. case errors.Is(err, service.ErrCustomGeoAliasRequired):
  38. return errors.New(I18nWeb(c, "pages.index.customGeoErrAliasRequired"))
  39. case errors.Is(err, service.ErrCustomGeoAliasPattern):
  40. return errors.New(I18nWeb(c, "pages.index.customGeoErrAliasPattern"))
  41. case errors.Is(err, service.ErrCustomGeoAliasReserved):
  42. return errors.New(I18nWeb(c, "pages.index.customGeoErrAliasReserved"))
  43. case errors.Is(err, service.ErrCustomGeoURLRequired):
  44. return errors.New(I18nWeb(c, "pages.index.customGeoErrUrlRequired"))
  45. case errors.Is(err, service.ErrCustomGeoInvalidURL):
  46. return errors.New(I18nWeb(c, "pages.index.customGeoErrInvalidUrl"))
  47. case errors.Is(err, service.ErrCustomGeoURLScheme):
  48. return errors.New(I18nWeb(c, "pages.index.customGeoErrUrlScheme"))
  49. case errors.Is(err, service.ErrCustomGeoURLHost):
  50. return errors.New(I18nWeb(c, "pages.index.customGeoErrUrlHost"))
  51. case errors.Is(err, service.ErrCustomGeoDuplicateAlias):
  52. return errors.New(I18nWeb(c, "pages.index.customGeoErrDuplicateAlias"))
  53. case errors.Is(err, service.ErrCustomGeoNotFound):
  54. return errors.New(I18nWeb(c, "pages.index.customGeoErrNotFound"))
  55. case errors.Is(err, service.ErrCustomGeoDownload):
  56. logger.Warning("custom geo download:", err)
  57. return errors.New(I18nWeb(c, "pages.index.customGeoErrDownload"))
  58. case errors.Is(err, service.ErrCustomGeoSSRFBlocked):
  59. logger.Warning("custom geo SSRF blocked:", err)
  60. return errors.New(I18nWeb(c, "pages.index.customGeoErrUrlHost"))
  61. case errors.Is(err, service.ErrCustomGeoPathTraversal):
  62. logger.Warning("custom geo path traversal blocked:", err)
  63. return errors.New(I18nWeb(c, "pages.index.customGeoErrDownload"))
  64. default:
  65. return err
  66. }
  67. }
  68. func (a *CustomGeoController) list(c *gin.Context) {
  69. list, err := a.customGeoService.GetAll()
  70. if err != nil {
  71. jsonMsg(c, I18nWeb(c, "pages.index.customGeoToastList"), mapCustomGeoErr(c, err))
  72. return
  73. }
  74. jsonObj(c, list, nil)
  75. }
  76. func (a *CustomGeoController) aliases(c *gin.Context) {
  77. out, err := a.customGeoService.GetAliasesForUI()
  78. if err != nil {
  79. jsonMsg(c, I18nWeb(c, "pages.index.customGeoAliasesError"), mapCustomGeoErr(c, err))
  80. return
  81. }
  82. jsonObj(c, out, nil)
  83. }
  84. type customGeoForm struct {
  85. Type string `json:"type" form:"type"`
  86. Alias string `json:"alias" form:"alias"`
  87. Url string `json:"url" form:"url"`
  88. }
  89. func (a *CustomGeoController) add(c *gin.Context) {
  90. var form customGeoForm
  91. if err := c.ShouldBind(&form); err != nil {
  92. jsonMsg(c, I18nWeb(c, "pages.index.customGeoToastAdd"), err)
  93. return
  94. }
  95. r := &model.CustomGeoResource{
  96. Type: form.Type,
  97. Alias: form.Alias,
  98. Url: form.Url,
  99. }
  100. err := a.customGeoService.Create(r)
  101. jsonMsg(c, I18nWeb(c, "pages.index.customGeoToastAdd"), mapCustomGeoErr(c, err))
  102. }
  103. func parseCustomGeoID(c *gin.Context, idStr string) (int, bool) {
  104. id, err := strconv.Atoi(idStr)
  105. if err != nil {
  106. jsonMsg(c, I18nWeb(c, "pages.index.customGeoInvalidId"), err)
  107. return 0, false
  108. }
  109. if id <= 0 {
  110. jsonMsg(c, I18nWeb(c, "pages.index.customGeoInvalidId"), errors.New(""))
  111. return 0, false
  112. }
  113. return id, true
  114. }
  115. func (a *CustomGeoController) update(c *gin.Context) {
  116. id, ok := parseCustomGeoID(c, c.Param("id"))
  117. if !ok {
  118. return
  119. }
  120. var form customGeoForm
  121. if bindErr := c.ShouldBind(&form); bindErr != nil {
  122. jsonMsg(c, I18nWeb(c, "pages.index.customGeoToastUpdate"), bindErr)
  123. return
  124. }
  125. r := &model.CustomGeoResource{
  126. Type: form.Type,
  127. Alias: form.Alias,
  128. Url: form.Url,
  129. }
  130. err := a.customGeoService.Update(id, r)
  131. jsonMsg(c, I18nWeb(c, "pages.index.customGeoToastUpdate"), mapCustomGeoErr(c, err))
  132. }
  133. func (a *CustomGeoController) delete(c *gin.Context) {
  134. id, ok := parseCustomGeoID(c, c.Param("id"))
  135. if !ok {
  136. return
  137. }
  138. name, err := a.customGeoService.Delete(id)
  139. jsonMsg(c, I18nWeb(c, "pages.index.customGeoToastDelete", "fileName=="+name), mapCustomGeoErr(c, err))
  140. }
  141. func (a *CustomGeoController) download(c *gin.Context) {
  142. id, ok := parseCustomGeoID(c, c.Param("id"))
  143. if !ok {
  144. return
  145. }
  146. name, err := a.customGeoService.TriggerUpdate(id)
  147. jsonMsg(c, I18nWeb(c, "pages.index.customGeoToastDownload", "fileName=="+name), mapCustomGeoErr(c, err))
  148. }
  149. func (a *CustomGeoController) updateAll(c *gin.Context) {
  150. res, err := a.customGeoService.TriggerUpdateAll()
  151. if err != nil {
  152. jsonMsg(c, I18nWeb(c, "pages.index.customGeoToastUpdateAll"), mapCustomGeoErr(c, err))
  153. return
  154. }
  155. if len(res.Failed) > 0 {
  156. c.JSON(http.StatusOK, entity.Msg{
  157. Success: false,
  158. Msg: I18nWeb(c, "pages.index.customGeoErrUpdateAllIncomplete"),
  159. Obj: res,
  160. })
  161. return
  162. }
  163. jsonMsgObj(c, I18nWeb(c, "pages.index.customGeoToastUpdateAll"), res, nil)
  164. }