diff options
| author | zhuzn <haimu0427@Outlook.com> | 2026-04-19 23:26:13 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-19 23:26:13 +0300 |
| commit | d580086361036f87af843d0f7386bdc54736720a (patch) | |
| tree | f22944b1ab1ea8c29858d796cc248b7b16436427 /sub/subController.go | |
| parent | 1e3b366fba3054698d55e36891d022a513e5a942 (diff) | |
feat add clash yaml convert (#3916)
* docs(agents): add AI agent guidance documentation
* feat(sub): add Clash/Mihomo YAML subscription service
Add SubClashService to convert subscription links to Clash/Mihomo
YAML format for direct client compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(sub): integrate Clash YAML endpoint into subscription system
- Add Clash route handler in SUBController
- Update BuildURLs to include Clash URL
- Pass Clash settings through subscription pipeline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(web): add Clash settings to entity and service
- Add SubClashEnable, SubClashPath, SubClashURI fields
- Add getter methods for Clash configuration
- Set default Clash path to /clash/ and enable by default
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(ui): add Clash settings to subscription panels
- Add Clash enable switch in general subscription settings
- Add Clash path/URI configuration in formats panel
- Display Clash QR code on subscription page
- Rename JSON tab to "Formats" for clarity
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(js): add Clash support to frontend models
- Add subClashEnable, subClashPath, subClashURI to AllSetting
- Generate and display Clash QR code on subscription page
- Handle Clash URL in subscription data binding
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
Diffstat (limited to 'sub/subController.go')
| -rw-r--r-- | sub/subController.go | 45 |
1 files changed, 38 insertions, 7 deletions
diff --git a/sub/subController.go b/sub/subController.go index 79ea755d..0e9e2c97 100644 --- a/sub/subController.go +++ b/sub/subController.go @@ -21,12 +21,15 @@ type SUBController struct { subRoutingRules string subPath string subJsonPath string + subClashPath string jsonEnabled bool + clashEnabled bool subEncrypt bool updateInterval string - subService *SubService - subJsonService *SubJsonService + subService *SubService + subJsonService *SubJsonService + subClashService *SubClashService } // NewSUBController creates a new subscription controller with the given configuration. @@ -34,7 +37,9 @@ func NewSUBController( g *gin.RouterGroup, subPath string, jsonPath string, + clashPath string, jsonEnabled bool, + clashEnabled bool, encrypt bool, showInfo bool, rModel string, @@ -60,12 +65,15 @@ func NewSUBController( subRoutingRules: subRoutingRules, subPath: subPath, subJsonPath: jsonPath, + subClashPath: clashPath, jsonEnabled: jsonEnabled, + clashEnabled: clashEnabled, subEncrypt: encrypt, updateInterval: update, - subService: sub, - subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub), + subService: sub, + subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub), + subClashService: NewSubClashService(sub), } a.initRouter(g) return a @@ -80,6 +88,10 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) { gJson := g.Group(a.subJsonPath) gJson.GET(":subid", a.subJsons) } + if a.clashEnabled { + gClash := g.Group(a.subClashPath) + gClash.GET(":subid", a.subClashs) + } } // subs handles HTTP requests for subscription links, returning either HTML page or base64-encoded subscription data. @@ -99,10 +111,13 @@ func (a *SUBController) subs(c *gin.Context) { 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) + subURL, subJsonURL, subClashURL := a.subService.BuildURLs(scheme, hostWithPort, a.subPath, a.subJsonPath, a.subClashPath, subId) if !a.jsonEnabled { subJsonURL = "" } + if !a.clashEnabled { + subClashURL = "" + } // Get base_path from context (set by middleware) basePath, exists := c.Get("base_path") if !exists { @@ -116,7 +131,7 @@ func (a *SUBController) subs(c *gin.Context) { // 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) + page := a.subService.BuildPageData(subId, hostHeader, traffic, lastOnline, subs, subURL, subJsonURL, subClashURL, basePathStr) c.HTML(200, "subpage.html", gin.H{ "title": "subscription.title", "cur_ver": config.GetVersion(), @@ -136,6 +151,7 @@ func (a *SUBController) subs(c *gin.Context) { "totalByte": page.TotalByte, "subUrl": page.SubUrl, "subJsonUrl": page.SubJsonUrl, + "subClashUrl": page.SubClashUrl, "result": page.Result, }) return @@ -165,7 +181,6 @@ func (a *SUBController) subJsons(c *gin.Context) { if err != nil || len(jsonSub) == 0 { c.String(400, "Error!") } else { - // Add headers profileUrl := a.subProfileUrl if profileUrl == "" { profileUrl = fmt.Sprintf("%s://%s%s", scheme, hostWithPort, c.Request.RequestURI) @@ -176,6 +191,22 @@ func (a *SUBController) subJsons(c *gin.Context) { } } +func (a *SUBController) subClashs(c *gin.Context) { + subId := c.Param("subid") + scheme, host, hostWithPort, _ := a.subService.ResolveRequest(c) + clashSub, header, err := a.subClashService.GetClash(subId, host) + if err != nil || len(clashSub) == 0 { + c.String(400, "Error!") + } else { + profileUrl := a.subProfileUrl + if profileUrl == "" { + profileUrl = fmt.Sprintf("%s://%s%s", scheme, hostWithPort, c.Request.RequestURI) + } + a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, profileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules) + c.Data(200, "application/yaml; charset=utf-8", []byte(clashSub)) + } +} + // ApplyCommonHeaders sets common HTTP headers for subscription responses including user info, update interval, and profile title. func (a *SUBController) ApplyCommonHeaders( c *gin.Context, |
