mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 21:24:10 +00:00
Merge pull request #1 from Kasp42/codex
Add support for additional subscription base URLs
This commit is contained in:
commit
e970e7e880
7 changed files with 67 additions and 2 deletions
|
|
@ -3,8 +3,10 @@ package sub
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"maps"
|
"maps"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"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
|
// Prepare statistics
|
||||||
for index, clientTraffic := range clientTraffics {
|
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
|
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) {
|
func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ class AllSetting {
|
||||||
this.subEncrypt = true;
|
this.subEncrypt = true;
|
||||||
this.subShowInfo = true;
|
this.subShowInfo = true;
|
||||||
this.subURI = "";
|
this.subURI = "";
|
||||||
|
this.subAdditionalURIs = "";
|
||||||
this.subJsonURI = "";
|
this.subJsonURI = "";
|
||||||
this.subClashURI = "";
|
this.subClashURI = "";
|
||||||
this.subJsonFragment = "";
|
this.subJsonFragment = "";
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ type AllSetting struct {
|
||||||
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"` // Encrypt subscription responses
|
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"` // Encrypt subscription responses
|
||||||
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"` // Show client information in subscriptions
|
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"` // Show client information in subscriptions
|
||||||
SubURI string `json:"subURI" form:"subURI"` // Subscription server URI
|
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
|
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"` // Path for JSON subscription endpoint
|
||||||
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"` // JSON subscription server URI
|
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"` // JSON subscription server URI
|
||||||
SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"` // Enable Clash/Mihomo subscription endpoint
|
SubClashEnable bool `json:"subClashEnable" form:"subClashEnable"` // Enable Clash/Mihomo subscription endpoint
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,16 @@
|
||||||
v-model="allSetting.subURI"></a-input>
|
v-model="allSetting.subURI"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</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>
|
||||||
<a-collapse-panel key="2" header='{{ i18n "pages.settings.information" }}'>
|
<a-collapse-panel key="2" header='{{ i18n "pages.settings.information" }}'>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ var defaultValueMap = map[string]string{
|
||||||
"subEncrypt": "true",
|
"subEncrypt": "true",
|
||||||
"subShowInfo": "true",
|
"subShowInfo": "true",
|
||||||
"subURI": "",
|
"subURI": "",
|
||||||
|
"subAdditionalURIs": "",
|
||||||
"subJsonPath": "/json/",
|
"subJsonPath": "/json/",
|
||||||
"subJsonURI": "",
|
"subJsonURI": "",
|
||||||
"subClashEnable": "true",
|
"subClashEnable": "true",
|
||||||
|
|
@ -564,6 +565,10 @@ func (s *SettingService) GetSubJsonURI() (string, error) {
|
||||||
return s.getString("subJsonURI")
|
return s.getString("subJsonURI")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubAdditionalURIs() (string, error) {
|
||||||
|
return s.getString("subAdditionalURIs")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetSubClashEnable() (bool, error) {
|
func (s *SettingService) GetSubClashEnable() (bool, error) {
|
||||||
return s.getBool("subClashEnable")
|
return s.getBool("subClashEnable")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,8 @@
|
||||||
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
|
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
|
||||||
"subURI" = "Reverse Proxy URI"
|
"subURI" = "Reverse Proxy URI"
|
||||||
"subURIDesc" = "The URI path of the subscription URL for use behind proxies."
|
"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"
|
"externalTrafficInformEnable" = "External Traffic Inform"
|
||||||
"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
|
"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
|
||||||
"externalTrafficInformURI" = "External Traffic Inform URI"
|
"externalTrafficInformURI" = "External Traffic Inform URI"
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,8 @@
|
||||||
"subShowInfoDesc" = "Отображать остаток трафика и дату окончания после имени конфигурации"
|
"subShowInfoDesc" = "Отображать остаток трафика и дату окончания после имени конфигурации"
|
||||||
"subURI" = "URI обратного прокси"
|
"subURI" = "URI обратного прокси"
|
||||||
"subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
|
"subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
|
||||||
|
"subAdditionalURIs" = "Дополнительные базовые URL подписки"
|
||||||
|
"subAdditionalURIsDesc" = "Базовые URL через запятую (например, https://x2.example.com:2096/sbp/). Работает только если на удалённом сервере существует подписка с тем же SubID."
|
||||||
"externalTrafficInformEnable" = "Информация о внешнем трафике"
|
"externalTrafficInformEnable" = "Информация о внешнем трафике"
|
||||||
"externalTrafficInformEnableDesc" = "Информировать внешний API о каждом обновлении трафика"
|
"externalTrafficInformEnableDesc" = "Информировать внешний API о каждом обновлении трафика"
|
||||||
"externalTrafficInformURI" = "URI информации о внешнем трафике"
|
"externalTrafficInformURI" = "URI информации о внешнем трафике"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue