mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 05:04:22 +00:00
- Update go.mod module path from mhsanaei/3x-ui/v3 to saeederamy/3x-ui/v3 - Update all 73 Go files' import paths accordingly - Fix README.fa_IR.md install command to point to fork's main branch The fork was referencing the original repo's module path in go.mod and all Go source imports, making it dependent on MHSanaei's namespace at build time. https://claude.ai/code/session_01M6d5atbWjuLTj6UwRHoK5m
120 lines
2.7 KiB
Go
120 lines
2.7 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/netip"
|
|
"strings"
|
|
|
|
"github.com/saeederamy/3x-ui/v3/logger"
|
|
"github.com/saeederamy/3x-ui/v3/web/entity"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// getRemoteIp extracts the real IP address from the request headers or remote address.
|
|
func getRemoteIp(c *gin.Context) string {
|
|
if ip, ok := extractTrustedIP(c.GetHeader("X-Real-IP")); ok {
|
|
return ip
|
|
}
|
|
|
|
if xff := c.GetHeader("X-Forwarded-For"); xff != "" {
|
|
for _, part := range strings.Split(xff, ",") {
|
|
if ip, ok := extractTrustedIP(part); ok {
|
|
return ip
|
|
}
|
|
}
|
|
}
|
|
|
|
if ip, ok := extractTrustedIP(c.Request.RemoteAddr); ok {
|
|
return ip
|
|
}
|
|
|
|
return "unknown"
|
|
}
|
|
|
|
func extractTrustedIP(value string) (string, bool) {
|
|
candidate := strings.TrimSpace(value)
|
|
if candidate == "" {
|
|
return "", false
|
|
}
|
|
|
|
if ip, ok := parseIPCandidate(candidate); ok {
|
|
return ip.String(), true
|
|
}
|
|
|
|
if host, _, err := net.SplitHostPort(candidate); err == nil {
|
|
if ip, ok := parseIPCandidate(host); ok {
|
|
return ip.String(), true
|
|
}
|
|
}
|
|
|
|
if strings.Count(candidate, ":") == 1 {
|
|
if host, _, err := net.SplitHostPort(fmt.Sprintf("[%s]", candidate)); err == nil {
|
|
if ip, ok := parseIPCandidate(host); ok {
|
|
return ip.String(), true
|
|
}
|
|
}
|
|
}
|
|
|
|
return "", false
|
|
}
|
|
|
|
func parseIPCandidate(value string) (netip.Addr, bool) {
|
|
ip, err := netip.ParseAddr(strings.TrimSpace(value))
|
|
if err != nil {
|
|
return netip.Addr{}, false
|
|
}
|
|
return ip.Unmap(), true
|
|
}
|
|
|
|
// jsonMsg sends a JSON response with a message and error status.
|
|
func jsonMsg(c *gin.Context, msg string, err error) {
|
|
jsonMsgObj(c, msg, nil, err)
|
|
}
|
|
|
|
// jsonObj sends a JSON response with an object and error status.
|
|
func jsonObj(c *gin.Context, obj any, err error) {
|
|
jsonMsgObj(c, "", obj, err)
|
|
}
|
|
|
|
// jsonMsgObj sends a JSON response with a message, object, and error status.
|
|
func jsonMsgObj(c *gin.Context, msg string, obj any, err error) {
|
|
m := entity.Msg{
|
|
Obj: obj,
|
|
}
|
|
if err == nil {
|
|
m.Success = true
|
|
if msg != "" {
|
|
m.Msg = msg
|
|
}
|
|
} else {
|
|
m.Success = false
|
|
errStr := err.Error()
|
|
if errStr != "" {
|
|
m.Msg = msg + " (" + errStr + ")"
|
|
logger.Warning(msg+" "+I18nWeb(c, "fail")+": ", err)
|
|
} else if msg != "" {
|
|
m.Msg = msg
|
|
logger.Warning(msg + " " + I18nWeb(c, "fail"))
|
|
} else {
|
|
m.Msg = I18nWeb(c, "somethingWentWrong")
|
|
logger.Warning(I18nWeb(c, "somethingWentWrong") + " " + I18nWeb(c, "fail"))
|
|
}
|
|
}
|
|
c.JSON(http.StatusOK, m)
|
|
}
|
|
|
|
// pureJsonMsg sends a pure JSON message response with custom status code.
|
|
func pureJsonMsg(c *gin.Context, statusCode int, success bool, msg string) {
|
|
c.JSON(statusCode, entity.Msg{
|
|
Success: success,
|
|
Msg: msg,
|
|
})
|
|
}
|
|
|
|
// isAjax checks if the request is an AJAX request.
|
|
func isAjax(c *gin.Context) bool {
|
|
return c.GetHeader("X-Requested-With") == "XMLHttpRequest"
|
|
}
|