package controller import ( "net" "net/http" "github.com/mhsanaei/3x-ui/v2/config" "github.com/mhsanaei/3x-ui/v2/logger" "github.com/mhsanaei/3x-ui/v2/web/entity" "github.com/mhsanaei/3x-ui/v2/web/session" "github.com/gin-gonic/gin" ) // getRemoteIp extracts the real IP address from the request. // Trusts Cloudflare's CF-Connecting-IP header (overwritten by CF, cannot be spoofed by clients). // Falls back to RemoteAddr for direct connections without a trusted proxy. func getRemoteIp(c *gin.Context) string { // Cloudflare CDN sets CF-Connecting-IP to the real client IP and overwrites it, // so it can be trusted even though it's a header. if cfIP := c.GetHeader("CF-Connecting-IP"); cfIP != "" { return cfIP } addr := c.Request.RemoteAddr ip, _, _ := net.SplitHostPort(addr) return ip } // 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 m.Msg = msg + " (" + err.Error() + ")" logger.Warning(msg+" "+I18nWeb(c, "fail")+": ", err) } 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, }) } // html renders an HTML template with the provided data and title. func html(c *gin.Context, name string, title string, data gin.H) { if data == nil { data = gin.H{} } data["title"] = title host := c.GetHeader("X-Forwarded-Host") if host == "" { host = c.GetHeader("X-Real-IP") } if host == "" { var err error host, _, err = net.SplitHostPort(c.Request.Host) if err != nil { host = c.Request.Host } } data["host"] = host data["request_uri"] = c.Request.RequestURI data["base_path"] = c.GetString("base_path") if user := session.GetLoginUser(c); user != nil { data["is_admin"] = user.Role == "admin" data["current_user_id"] = user.Id data["current_username"] = user.Username } c.HTML(http.StatusOK, name, getContext(data)) } // getContext adds version and other context data to the provided gin.H. func getContext(h gin.H) gin.H { a := gin.H{ "cur_ver": config.GetVersion(), } for key, value := range h { a[key] = value } return a } // isAjax checks if the request is an AJAX request. func isAjax(c *gin.Context) bool { return c.GetHeader("X-Requested-With") == "XMLHttpRequest" }