package sub import ( "encoding/base64" "fmt" "strings" "x-ui/config" "github.com/gin-gonic/gin" ) type SUBController struct { subTitle string subPath string subJsonPath string subEncrypt bool updateInterval string subService *SubService subJsonService *SubJsonService } func NewSUBController( g *gin.RouterGroup, subPath string, jsonPath string, encrypt bool, showInfo bool, rModel string, update string, jsonFragment string, jsonNoise string, jsonMux string, jsonRules string, subTitle string, ) *SUBController { sub := NewSubService(showInfo, rModel) a := &SUBController{ subTitle: subTitle, subPath: subPath, subJsonPath: jsonPath, subEncrypt: encrypt, updateInterval: update, subService: sub, subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub), } a.initRouter(g) return a } func (a *SUBController) initRouter(g *gin.RouterGroup) { gLink := g.Group(a.subPath) gJson := g.Group(a.subJsonPath) gLink.GET(":subid", a.subs) gJson.GET(":subid", a.subJsons) } func (a *SUBController) subs(c *gin.Context) { subId := c.Param("subid") scheme, host, hostWithPort, hostHeader := a.subService.ResolveRequest(c) subs, lastOnline, traffic, err := a.subService.GetSubs(subId, host) if err != nil || len(subs) == 0 { c.String(400, "Error!") } else { result := "" for _, sub := range subs { result += sub + "\n" } // If the request expects HTML (e.g., browser) or explicitly asked (?html=1 or ?view=html), render the info page here accept := c.GetHeader("Accept") if strings.Contains(strings.ToLower(accept), "text/html") || c.Query("html") == "1" || strings.EqualFold(c.Query("view"), "html") { // Build page data in service subURL, subJsonURL := a.subService.BuildURLs(scheme, hostWithPort, a.subPath, a.subJsonPath, subId) page := a.subService.BuildPageData(subId, hostHeader, traffic, lastOnline, subs, subURL, subJsonURL) c.HTML(200, "subscription.html", gin.H{ "title": "subscription.title", "cur_ver": config.GetVersion(), "host": page.Host, "base_path": page.BasePath, "sId": page.SId, "download": page.Download, "upload": page.Upload, "total": page.Total, "used": page.Used, "remained": page.Remained, "expire": page.Expire, "lastOnline": page.LastOnline, "datepicker": page.Datepicker, "downloadByte": page.DownloadByte, "uploadByte": page.UploadByte, "totalByte": page.TotalByte, "subUrl": page.SubUrl, "subJsonUrl": page.SubJsonUrl, "result": page.Result, }) return } // Add headers header := fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000) a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle) if a.subEncrypt { c.String(200, base64.StdEncoding.EncodeToString([]byte(result))) } else { c.String(200, result) } } } func (a *SUBController) subJsons(c *gin.Context) { subId := c.Param("subid") _, host, _, _ := a.subService.ResolveRequest(c) jsonSub, header, err := a.subJsonService.GetJson(subId, host) if err != nil || len(jsonSub) == 0 { c.String(400, "Error!") } else { // Add headers a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle) c.String(200, jsonSub) } } func (a *SUBController) ApplyCommonHeaders(c *gin.Context, header, updateInterval, profileTitle string) { c.Writer.Header().Set("Subscription-Userinfo", header) c.Writer.Header().Set("Profile-Update-Interval", updateInterval) c.Writer.Header().Set("Profile-Title", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileTitle))) }