1
0

util.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package controller
  2. import (
  3. "fmt"
  4. "net"
  5. "net/http"
  6. "net/netip"
  7. "strings"
  8. "github.com/mhsanaei/3x-ui/v3/logger"
  9. "github.com/mhsanaei/3x-ui/v3/web/entity"
  10. "github.com/gin-gonic/gin"
  11. )
  12. // getRemoteIp extracts the real IP address from the request headers or remote address.
  13. func getRemoteIp(c *gin.Context) string {
  14. if ip, ok := extractTrustedIP(c.GetHeader("X-Real-IP")); ok {
  15. return ip
  16. }
  17. if xff := c.GetHeader("X-Forwarded-For"); xff != "" {
  18. for _, part := range strings.Split(xff, ",") {
  19. if ip, ok := extractTrustedIP(part); ok {
  20. return ip
  21. }
  22. }
  23. }
  24. if ip, ok := extractTrustedIP(c.Request.RemoteAddr); ok {
  25. return ip
  26. }
  27. return "unknown"
  28. }
  29. func extractTrustedIP(value string) (string, bool) {
  30. candidate := strings.TrimSpace(value)
  31. if candidate == "" {
  32. return "", false
  33. }
  34. if ip, ok := parseIPCandidate(candidate); ok {
  35. return ip.String(), true
  36. }
  37. if host, _, err := net.SplitHostPort(candidate); err == nil {
  38. if ip, ok := parseIPCandidate(host); ok {
  39. return ip.String(), true
  40. }
  41. }
  42. if strings.Count(candidate, ":") == 1 {
  43. if host, _, err := net.SplitHostPort(fmt.Sprintf("[%s]", candidate)); err == nil {
  44. if ip, ok := parseIPCandidate(host); ok {
  45. return ip.String(), true
  46. }
  47. }
  48. }
  49. return "", false
  50. }
  51. func parseIPCandidate(value string) (netip.Addr, bool) {
  52. ip, err := netip.ParseAddr(strings.TrimSpace(value))
  53. if err != nil {
  54. return netip.Addr{}, false
  55. }
  56. return ip.Unmap(), true
  57. }
  58. // jsonMsg sends a JSON response with a message and error status.
  59. func jsonMsg(c *gin.Context, msg string, err error) {
  60. jsonMsgObj(c, msg, nil, err)
  61. }
  62. // jsonObj sends a JSON response with an object and error status.
  63. func jsonObj(c *gin.Context, obj any, err error) {
  64. jsonMsgObj(c, "", obj, err)
  65. }
  66. // jsonMsgObj sends a JSON response with a message, object, and error status.
  67. func jsonMsgObj(c *gin.Context, msg string, obj any, err error) {
  68. m := entity.Msg{
  69. Obj: obj,
  70. }
  71. if err == nil {
  72. m.Success = true
  73. if msg != "" {
  74. m.Msg = msg
  75. }
  76. } else {
  77. m.Success = false
  78. errStr := err.Error()
  79. if errStr != "" {
  80. m.Msg = msg + " (" + errStr + ")"
  81. logger.Warning(msg+" "+I18nWeb(c, "fail")+": ", err)
  82. } else if msg != "" {
  83. m.Msg = msg
  84. logger.Warning(msg + " " + I18nWeb(c, "fail"))
  85. } else {
  86. m.Msg = I18nWeb(c, "somethingWentWrong")
  87. logger.Warning(I18nWeb(c, "somethingWentWrong") + " " + I18nWeb(c, "fail"))
  88. }
  89. }
  90. c.JSON(http.StatusOK, m)
  91. }
  92. // pureJsonMsg sends a pure JSON message response with custom status code.
  93. func pureJsonMsg(c *gin.Context, statusCode int, success bool, msg string) {
  94. c.JSON(statusCode, entity.Msg{
  95. Success: success,
  96. Msg: msg,
  97. })
  98. }
  99. // isAjax checks if the request is an AJAX request.
  100. func isAjax(c *gin.Context) bool {
  101. return c.GetHeader("X-Requested-With") == "XMLHttpRequest"
  102. }