feat(sub): per-client external links and remote subscriptions

Add a Links tab to the client form for attaching third-party share
links and remote subscription URLs per client. They are merged into
the client's raw/JSON/Clash subscription output: links are emitted
verbatim and parsed for JSON/Clash; subscription URLs are fetched
(cached, with a short timeout) and their configs merged in.

i18n keys added across all 13 locales.
This commit is contained in:
MHSanaei
2026-06-14 20:57:14 +02:00
parent 7c2598fae9
commit dcb923b4a1
33 changed files with 1204 additions and 28 deletions
+29 -1
View File
@@ -62,9 +62,16 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
// resolveInboundAddress call inside picks node-aware host values.
s.SubService.PrepareForRequest(host)
inbounds, err := s.SubService.getInboundsBySubId(subId)
if err != nil || len(inbounds) == 0 {
if err != nil {
return "", "", err
}
externalLinks, err := s.SubService.getClientExternalLinksBySubId(subId)
if err != nil {
return "", "", err
}
if len(inbounds) == 0 && len(externalLinks) == 0 {
return "", "", nil
}
var header string
var configArray []json_util.RawMessage
@@ -83,6 +90,27 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
configArray = append(configArray, s.getConfig(inbound, client, host)...)
}
}
for _, ext := range externalLinks {
for _, el := range expandEntry(ext) {
outbound := parsedExternalOutbound(el.Link)
if outbound == nil {
continue
}
seenEmails[ext.Email] = struct{}{}
remark := el.Name
if remark == "" {
remark = ext.Email
}
newOutbounds := []json_util.RawMessage{outbound}
newOutbounds = append(newOutbounds, s.defaultOutbounds...)
newConfigJson := make(map[string]any)
maps.Copy(newConfigJson, s.configJson)
newConfigJson["outbounds"] = newOutbounds
newConfigJson["remarks"] = remark
newConfig, _ := json.MarshalIndent(newConfigJson, "", " ")
configArray = append(configArray, newConfig)
}
}
if len(configArray) == 0 {
return "", "", nil