|
@@ -53,6 +53,7 @@ type cachedSubTemplate struct {
|
|
|
type SUBController struct {
|
|
type SUBController struct {
|
|
|
subTitle string
|
|
subTitle string
|
|
|
subSupportUrl string
|
|
subSupportUrl string
|
|
|
|
|
+ subProfileMode string
|
|
|
subProfileUrl string
|
|
subProfileUrl string
|
|
|
subAnnounce string
|
|
subAnnounce string
|
|
|
subEnableRouting bool
|
|
subEnableRouting bool
|
|
@@ -115,6 +116,7 @@ type subControllerConfig struct {
|
|
|
|
|
|
|
|
subTitle string
|
|
subTitle string
|
|
|
subSupportURL string
|
|
subSupportURL string
|
|
|
|
|
+ subProfileMode string
|
|
|
subProfileURL string
|
|
subProfileURL string
|
|
|
subAnnounce string
|
|
subAnnounce string
|
|
|
subEnableRouting bool
|
|
subEnableRouting bool
|
|
@@ -224,6 +226,10 @@ func WithSUBProfileURL(value string) SUBControllerOption {
|
|
|
return func(config *subControllerConfig) { config.subProfileURL = value }
|
|
return func(config *subControllerConfig) { config.subProfileURL = value }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func WithSUBProfileMode(value string) SUBControllerOption {
|
|
|
|
|
+ return func(config *subControllerConfig) { config.subProfileMode = value }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
func WithSUBAnnounce(value string) SUBControllerOption {
|
|
func WithSUBAnnounce(value string) SUBControllerOption {
|
|
|
return func(config *subControllerConfig) { config.subAnnounce = value }
|
|
return func(config *subControllerConfig) { config.subAnnounce = value }
|
|
|
}
|
|
}
|
|
@@ -260,6 +266,7 @@ func defaultSUBControllerConfig() subControllerConfig {
|
|
|
subEncrypt: true,
|
|
subEncrypt: true,
|
|
|
remarkTemplate: service.DefaultRemarkTemplate,
|
|
remarkTemplate: service.DefaultRemarkTemplate,
|
|
|
updateInterval: "12",
|
|
updateInterval: "12",
|
|
|
|
|
+ subProfileMode: service.SubProfileModeNone,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -277,6 +284,7 @@ func NewSUBController(g *gin.RouterGroup, options ...SUBControllerOption) *SUBCo
|
|
|
a := &SUBController{
|
|
a := &SUBController{
|
|
|
subTitle: config.subTitle,
|
|
subTitle: config.subTitle,
|
|
|
subSupportUrl: config.subSupportURL,
|
|
subSupportUrl: config.subSupportURL,
|
|
|
|
|
+ subProfileMode: config.subProfileMode,
|
|
|
subProfileUrl: config.subProfileURL,
|
|
subProfileUrl: config.subProfileURL,
|
|
|
subAnnounce: config.subAnnounce,
|
|
subAnnounce: config.subAnnounce,
|
|
|
subEnableRouting: config.subEnableRouting,
|
|
subEnableRouting: config.subEnableRouting,
|
|
@@ -485,8 +493,7 @@ func (a *SUBController) subs(c *gin.Context) {
|
|
|
|
|
|
|
|
// Add headers
|
|
// Add headers
|
|
|
header := subReq.subscriptionUserinfo(traffic)
|
|
header := subReq.subscriptionUserinfo(traffic)
|
|
|
- profileURL := fmt.Sprintf("%s://%s%s", scheme, hostWithPort, c.Request.RequestURI)
|
|
|
|
|
- metadata := a.metadataForSubRequest(func() *SubService { return subReq }, subId, profileURL)
|
|
|
|
|
|
|
+ metadata := a.metadataForSubRequest(func() *SubService { return subReq }, subId, builtinProfileURL(c, scheme, hostWithPort))
|
|
|
a.ApplyCommonHeaders(c, header, a.updateInterval, metadata.Title, metadata.SupportURL, metadata.ProfileURL, metadata.Announce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
|
|
a.ApplyCommonHeaders(c, header, a.updateInterval, metadata.Title, metadata.SupportURL, metadata.ProfileURL, metadata.Announce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
|
|
|
|
|
|
|
|
if a.subIncyEnableRouting && a.subIncyRoutingRules != "" {
|
|
if a.subIncyEnableRouting && a.subIncyRoutingRules != "" {
|
|
@@ -818,14 +825,13 @@ func (a *SUBController) serveJsonBody(c *gin.Context, alwaysReturnArray bool, co
|
|
|
if len(jsonSub) == 0 && header == "" {
|
|
if len(jsonSub) == 0 && header == "" {
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|
|
|
- profileURL := fmt.Sprintf("%s://%s%s", scheme, hostWithPort, c.Request.RequestURI)
|
|
|
|
|
var subReq *SubService
|
|
var subReq *SubService
|
|
|
metadata := a.metadataForSubRequest(func() *SubService {
|
|
metadata := a.metadataForSubRequest(func() *SubService {
|
|
|
if subReq == nil {
|
|
if subReq == nil {
|
|
|
subReq = a.subService.ForRequest(host)
|
|
subReq = a.subService.ForRequest(host)
|
|
|
}
|
|
}
|
|
|
return subReq
|
|
return subReq
|
|
|
- }, subId, profileURL)
|
|
|
|
|
|
|
+ }, subId, builtinProfileURL(c, scheme, hostWithPort))
|
|
|
a.ApplyCommonHeaders(c, header, a.updateInterval, metadata.Title, metadata.SupportURL, metadata.ProfileURL, metadata.Announce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
|
|
a.ApplyCommonHeaders(c, header, a.updateInterval, metadata.Title, metadata.SupportURL, metadata.ProfileURL, metadata.Announce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
|
|
|
if rawDownload {
|
|
if rawDownload {
|
|
|
c.Writer.Header().Set("Content-Disposition", `attachment; filename="subscription.json"`)
|
|
c.Writer.Header().Set("Content-Disposition", `attachment; filename="subscription.json"`)
|
|
@@ -887,14 +893,13 @@ func (a *SUBController) serveClashBody(c *gin.Context, rawDownload bool, legacy
|
|
|
if len(clashSub) == 0 && header == "" {
|
|
if len(clashSub) == 0 && header == "" {
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|
|
|
- profileURL := fmt.Sprintf("%s://%s%s", scheme, hostWithPort, c.Request.RequestURI)
|
|
|
|
|
var subReq *SubService
|
|
var subReq *SubService
|
|
|
metadata := a.metadataForSubRequest(func() *SubService {
|
|
metadata := a.metadataForSubRequest(func() *SubService {
|
|
|
if subReq == nil {
|
|
if subReq == nil {
|
|
|
subReq = a.subService.ForRequest(host)
|
|
subReq = a.subService.ForRequest(host)
|
|
|
}
|
|
}
|
|
|
return subReq
|
|
return subReq
|
|
|
- }, subId, profileURL)
|
|
|
|
|
|
|
+ }, subId, builtinProfileURL(c, scheme, hostWithPort))
|
|
|
a.ApplyCommonHeaders(c, header, a.updateInterval, metadata.Title, metadata.SupportURL, metadata.ProfileURL, metadata.Announce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
|
|
a.ApplyCommonHeaders(c, header, a.updateInterval, metadata.Title, metadata.SupportURL, metadata.ProfileURL, metadata.Announce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
|
|
|
if rawDownload {
|
|
if rawDownload {
|
|
|
c.Writer.Header().Set("Content-Disposition", `attachment; filename="subscription.yaml"`)
|
|
c.Writer.Header().Set("Content-Disposition", `attachment; filename="subscription.yaml"`)
|
|
@@ -906,6 +911,11 @@ func (a *SUBController) serveClashBody(c *gin.Context, rawDownload bool, legacy
|
|
|
return true
|
|
return true
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func builtinProfileURL(c *gin.Context, scheme, hostWithPort string) string {
|
|
|
|
|
+ // Drop download/format selectors so the opt-in link always opens the HTML page.
|
|
|
|
|
+ return fmt.Sprintf("%s://%s%s?html=1", scheme, hostWithPort, c.Request.URL.EscapedPath())
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// ApplyCommonHeaders sets common HTTP headers for subscription responses including user info, update interval, and profile title.
|
|
// ApplyCommonHeaders sets common HTTP headers for subscription responses including user info, update interval, and profile title.
|
|
|
func (a *SUBController) ApplyCommonHeaders(
|
|
func (a *SUBController) ApplyCommonHeaders(
|
|
|
c *gin.Context,
|
|
c *gin.Context,
|