Files
trihuy-russian/sub/subController.go
T

162 lines
4.9 KiB
Go
Raw Normal View History

2023-05-22 18:06:34 +03:30
package sub
2023-04-09 23:13:18 +03:30
import (
"encoding/base64"
2025-09-14 23:08:09 +02:00
"fmt"
"strings"
2025-09-18 22:06:01 +02:00
2025-09-19 10:05:43 +02:00
"github.com/mhsanaei/3x-ui/v2/config"
2023-04-09 23:13:18 +03:30
"github.com/gin-gonic/gin"
)
2025-09-20 09:35:50 +02:00
// SUBController handles HTTP requests for subscription links and JSON configurations.
2023-04-09 23:13:18 +03:30
type SUBController struct {
subTitle string
2024-02-21 14:17:52 +03:30
subPath string
subJsonPath string
2025-09-18 13:56:04 +02:00
jsonEnabled bool
2024-02-21 14:17:52 +03:30
subEncrypt bool
updateInterval string
subService *SubService
subJsonService *SubJsonService
2023-04-09 23:13:18 +03:30
}
2025-09-20 09:35:50 +02:00
// NewSUBController creates a new subscription controller with the given configuration.
2024-02-21 14:17:52 +03:30
func NewSUBController(
g *gin.RouterGroup,
subPath string,
jsonPath string,
2025-09-18 13:56:04 +02:00
jsonEnabled bool,
2024-02-21 14:17:52 +03:30
encrypt bool,
showInfo bool,
rModel string,
update string,
2024-03-11 01:01:24 +03:30
jsonFragment string,
2024-08-29 11:27:43 +02:00
jsonNoise string,
2024-03-12 19:44:51 +03:30
jsonMux string,
jsonRules string,
subTitle string,
2024-03-11 01:01:24 +03:30
) *SUBController {
2024-03-11 16:14:24 +03:30
sub := NewSubService(showInfo, rModel)
2024-02-21 14:17:52 +03:30
a := &SUBController{
subTitle: subTitle,
2024-02-21 14:17:52 +03:30
subPath: subPath,
subJsonPath: jsonPath,
2025-09-18 13:56:04 +02:00
jsonEnabled: jsonEnabled,
2024-02-21 14:17:52 +03:30
subEncrypt: encrypt,
updateInterval: update,
2024-03-11 16:14:24 +03:30
subService: sub,
2024-08-29 11:27:43 +02:00
subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
2024-02-21 14:17:52 +03:30
}
2023-04-09 23:13:18 +03:30
a.initRouter(g)
return a
}
2025-09-20 09:35:50 +02:00
// initRouter registers HTTP routes for subscription links and JSON endpoints
// on the provided router group.
2023-04-09 23:13:18 +03:30
func (a *SUBController) initRouter(g *gin.RouterGroup) {
2024-02-21 14:17:52 +03:30
gLink := g.Group(a.subPath)
gLink.GET(":subid", a.subs)
2025-09-18 13:56:04 +02:00
if a.jsonEnabled {
gJson := g.Group(a.subJsonPath)
gJson.GET(":subid", a.subJsons)
}
2023-04-09 23:13:18 +03:30
}
2025-09-20 09:35:50 +02:00
// subs handles HTTP requests for subscription links, returning either HTML page or base64-encoded subscription data.
2023-04-09 23:13:18 +03:30
func (a *SUBController) subs(c *gin.Context) {
subId := c.Param("subid")
2025-09-14 01:22:42 +02:00
scheme, host, hostWithPort, hostHeader := a.subService.ResolveRequest(c)
2025-09-14 23:08:09 +02:00
subs, lastOnline, traffic, err := a.subService.GetSubs(subId, host)
2023-04-18 21:34:06 +03:30
if err != nil || len(subs) == 0 {
2023-04-09 23:13:18 +03:30
c.String(400, "Error!")
} else {
result := ""
for _, sub := range subs {
result += sub + "\n"
}
2023-04-18 21:34:06 +03:30
2025-09-14 01:22:42 +02:00
// 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)
2025-09-18 13:56:04 +02:00
if !a.jsonEnabled {
subJsonURL = ""
}
2025-09-24 19:51:01 +02:00
// Get base_path from context (set by middleware)
basePath, exists := c.Get("base_path")
if !exists {
basePath = "/"
}
// Add subId to base_path for asset URLs
basePathStr := basePath.(string)
if basePathStr == "/" {
basePathStr = "/" + subId + "/"
} else {
// Remove trailing slash if exists, add subId, then add trailing slash
basePathStr = strings.TrimRight(basePathStr, "/") + "/" + subId + "/"
}
page := a.subService.BuildPageData(subId, hostHeader, traffic, lastOnline, subs, subURL, subJsonURL, basePathStr)
2025-09-18 12:20:21 +02:00
c.HTML(200, "subpage.html", gin.H{
2025-09-14 01:22:42 +02:00
"title": "subscription.title",
2025-09-14 19:44:26 +02:00
"cur_ver": config.GetVersion(),
2025-09-14 01:22:42 +02:00
"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
}
2023-04-18 21:34:06 +03:30
2025-09-14 19:44:26 +02:00
// Add headers
2025-09-14 23:08:09 +02:00
header := fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)
2025-09-14 19:44:26 +02:00
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle)
2024-02-21 14:17:52 +03:30
if a.subEncrypt {
2023-08-26 16:54:01 +03:30
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
} else {
c.String(200, result)
}
2023-04-09 23:13:18 +03:30
}
}
2024-02-21 14:17:52 +03:30
2025-09-20 09:35:50 +02:00
// subJsons handles HTTP requests for JSON subscription configurations.
2024-02-21 14:17:52 +03:30
func (a *SUBController) subJsons(c *gin.Context) {
subId := c.Param("subid")
2025-09-14 01:22:42 +02:00
_, host, _, _ := a.subService.ResolveRequest(c)
2024-02-21 14:17:52 +03:30
jsonSub, header, err := a.subJsonService.GetJson(subId, host)
if err != nil || len(jsonSub) == 0 {
c.String(400, "Error!")
} else {
2025-09-14 19:44:26 +02:00
// Add headers
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle)
2024-02-21 14:17:52 +03:30
c.String(200, jsonSub)
}
}
2025-09-20 09:35:50 +02:00
// ApplyCommonHeaders sets common HTTP headers for subscription responses including user info, update interval, and profile title.
2025-09-14 19:44:26 +02:00
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)))
}