Merge pull request #1 from Kasp42/codex

Add support for additional subscription base URLs
This commit is contained in:
Vladislav Kasperov 2026-05-03 13:38:24 +03:00 committed by GitHub
commit e970e7e880
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 67 additions and 2 deletions

View file

@ -3,8 +3,10 @@ package sub
import (
"encoding/base64"
"fmt"
"io"
"maps"
"net"
"net/http"
"net/url"
"slices"
"strings"
@ -88,6 +90,7 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, int64, xray.C
}
}
}
result = append(result, s.getAdditionalSubs(subId)...)
// Prepare statistics
for index, clientTraffic := range clientTraffics {
@ -114,6 +117,47 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, int64, xray.C
return result, lastOnline, traffic, nil
}
func (s *SubService) getAdditionalSubs(subID string) []string {
additionalURIs, err := s.settingService.GetSubAdditionalURIs()
if err != nil || strings.TrimSpace(additionalURIs) == "" {
return nil
}
client := &http.Client{Timeout: 8 * time.Second}
var result []string
for _, baseURI := range strings.Split(additionalURIs, ",") {
baseURI = strings.TrimSpace(baseURI)
if baseURI == "" {
continue
}
if !strings.HasSuffix(baseURI, "/") {
baseURI += "/"
}
remoteSubURL := baseURI + subID
resp, reqErr := client.Get(remoteSubURL)
if reqErr != nil {
logger.Warningf("SubService - additional sub request failed for %s: %v", remoteSubURL, reqErr)
continue
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil || resp.StatusCode != http.StatusOK {
continue
}
rawBody := strings.TrimSpace(string(body))
decoded, decodeErr := base64.StdEncoding.DecodeString(rawBody)
if decodeErr == nil {
rawBody = string(decoded)
}
for _, line := range strings.Split(rawBody, "\n") {
line = strings.TrimSpace(line)
if line != "" {
result = append(result, line)
}
}
}
return result
}
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
db := database.GetDB()
var inbounds []*model.Inbound

View file

@ -49,6 +49,7 @@ class AllSetting {
this.subEncrypt = true;
this.subShowInfo = true;
this.subURI = "";
this.subAdditionalURIs = "";
this.subJsonURI = "";
this.subClashURI = "";
this.subJsonFragment = "";
@ -89,4 +90,4 @@ class AllSetting {
equals(other) {
return ObjectUtil.equals(this, other);
}
}
}

View file

@ -74,6 +74,7 @@ type AllSetting struct {
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"` // Encrypt subscription responses
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"` // Show client information in subscriptions
SubURI string `json:"subURI" form:"subURI"` // Subscription server URI
SubAdditionalURIs string `json:"subAdditionalURIs" form:"subAdditionalURIs"` // Comma-separated list of additional subscription base URLs
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"` // Path for JSON subscription endpoint
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"` // JSON subscription server URI
SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"` // Enable Clash/Mihomo subscription endpoint

View file

@ -72,6 +72,16 @@
v-model="allSetting.subURI"></a-input>
</template>
</a-setting-list-item>
<a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subAdditionalURIs"}}</template>
<template #description>{{ i18n
"pages.settings.subAdditionalURIsDesc"}}</template>
<template #control>
<a-input type="text"
placeholder="https://x2.example.com:2096/sbp/, https://x3.example.com/sub/"
v-model="allSetting.subAdditionalURIs"></a-input>
</template>
</a-setting-list-item>
</a-collapse-panel>
<a-collapse-panel key="2" header='{{ i18n "pages.settings.information" }}'>
<a-setting-list-item paddings="small">
@ -176,4 +186,4 @@
</a-setting-list-item>
</a-collapse-panel>
</a-collapse>
{{end}}
{{end}}

View file

@ -69,6 +69,7 @@ var defaultValueMap = map[string]string{
"subEncrypt": "true",
"subShowInfo": "true",
"subURI": "",
"subAdditionalURIs": "",
"subJsonPath": "/json/",
"subJsonURI": "",
"subClashEnable": "true",
@ -564,6 +565,10 @@ func (s *SettingService) GetSubJsonURI() (string, error) {
return s.getString("subJsonURI")
}
func (s *SettingService) GetSubAdditionalURIs() (string, error) {
return s.getString("subAdditionalURIs")
}
func (s *SettingService) GetSubClashEnable() (bool, error) {
return s.getBool("subClashEnable")
}

View file

@ -473,6 +473,8 @@
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
"subURI" = "Reverse Proxy URI"
"subURIDesc" = "The URI path of the subscription URL for use behind proxies."
"subAdditionalURIs" = "Extra Subscription Base URLs"
"subAdditionalURIsDesc" = "Comma-separated base URLs (e.g. https://x2.example.com:2096/sbp/). They work only when the same subscription ID exists on that server."
"externalTrafficInformEnable" = "External Traffic Inform"
"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
"externalTrafficInformURI" = "External Traffic Inform URI"

View file

@ -473,6 +473,8 @@
"subShowInfoDesc" = "Отображать остаток трафика и дату окончания после имени конфигурации"
"subURI" = "URI обратного прокси"
"subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
"subAdditionalURIs" = "Дополнительные базовые URL подписки"
"subAdditionalURIsDesc" = "Базовые URL через запятую (например, https://x2.example.com:2096/sbp/). Работает только если на удалённом сервере существует подписка с тем же SubID."
"externalTrafficInformEnable" = "Информация о внешнем трафике"
"externalTrafficInformEnableDesc" = "Информировать внешний API о каждом обновлении трафика"
"externalTrafficInformURI" = "URI информации о внешнем трафике"