Compare commits

..

1 commit

Author SHA1 Message Date
nagibator_archivator
c93468cc86
Merge 2d722fcc49 into d8c783a296 2026-01-18 19:54:19 +00:00
23 changed files with 37 additions and 440 deletions

View file

@ -147,7 +147,7 @@ setup_ssl_certificate() {
echo -e "${green}Issuing SSL certificate for ${domain}...${plain}" echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}" echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force >/dev/null 2>&1 ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@ -521,13 +521,12 @@ prompt_and_setup_ssl() {
echo -e "${yellow}Choose SSL certificate setup method:${plain}" echo -e "${yellow}Choose SSL certificate setup method:${plain}"
echo -e "${green}1.${plain} Let's Encrypt for Domain (90-day validity, auto-renews)" echo -e "${green}1.${plain} Let's Encrypt for Domain (90-day validity, auto-renews)"
echo -e "${green}2.${plain} Let's Encrypt for IP Address (6-day validity, auto-renews)" echo -e "${green}2.${plain} Let's Encrypt for IP Address (6-day validity, auto-renews)"
echo -e "${green}3.${plain} Custom SSL Certificate (Path to existing files)" echo -e "${blue}Note:${plain} Both options require port 80 open. IP certs use shortlived profile."
echo -e "${blue}Note:${plain} Options 1 & 2 require port 80 open. Option 3 requires manual paths."
read -rp "Choose an option (default 2 for IP): " ssl_choice read -rp "Choose an option (default 2 for IP): " ssl_choice
ssl_choice="${ssl_choice// /}" # Trim whitespace ssl_choice="${ssl_choice// /}" # Trim whitespace
# Default to 2 (IP cert) if input is empty or invalid (not 1 or 3) # Default to 2 (IP cert) if not 1
if [[ "$ssl_choice" != "1" && "$ssl_choice" != "3" ]]; then if [[ "$ssl_choice" != "1" ]]; then
ssl_choice="2" ssl_choice="2"
fi fi
@ -570,66 +569,7 @@ prompt_and_setup_ssl() {
echo -e "${red}✗ IP certificate setup failed. Please check port 80 is open.${plain}" echo -e "${red}✗ IP certificate setup failed. Please check port 80 is open.${plain}"
SSL_HOST="${server_ip}" SSL_HOST="${server_ip}"
fi fi
;;
3)
# User chose Custom Paths (User Provided) option
echo -e "${green}Using custom existing certificate...${plain}"
local custom_cert=""
local custom_key=""
local custom_domain=""
# 3.1 Request Domain to compose Panel URL later
read -rp "Please enter domain name certificate issued for: " custom_domain
custom_domain="${custom_domain// /}" # Убираем пробелы
# 3.2 Loop for Certificate Path
while true; do
read -rp "Input certificate path (keywords: .crt / fullchain): " custom_cert
# Strip quotes if present
custom_cert=$(echo "$custom_cert" | tr -d '"' | tr -d "'")
if [[ -f "$custom_cert" && -r "$custom_cert" && -s "$custom_cert" ]]; then
break
elif [[ ! -f "$custom_cert" ]]; then
echo -e "${red}Error: File does not exist! Try again.${plain}"
elif [[ ! -r "$custom_cert" ]]; then
echo -e "${red}Error: File exists but is not readable (check permissions)!${plain}"
else
echo -e "${red}Error: File is empty!${plain}"
fi
done
# 3.3 Loop for Private Key Path
while true; do
read -rp "Input private key path (keywords: .key / privatekey): " custom_key
# Strip quotes if present
custom_key=$(echo "$custom_key" | tr -d '"' | tr -d "'")
if [[ -f "$custom_key" && -r "$custom_key" && -s "$custom_key" ]]; then
break
elif [[ ! -f "$custom_key" ]]; then
echo -e "${red}Error: File does not exist! Try again.${plain}"
elif [[ ! -r "$custom_key" ]]; then
echo -e "${red}Error: File exists but is not readable (check permissions)!${plain}"
else
echo -e "${red}Error: File is empty!${plain}"
fi
done
# 3.4 Apply Settings via x-ui binary
${xui_folder}/x-ui cert -webCert "$custom_cert" -webCertKey "$custom_key" >/dev/null 2>&1
# Set SSL_HOST for composing Panel URL
if [[ -n "$custom_domain" ]]; then
SSL_HOST="$custom_domain"
else
SSL_HOST="${server_ip}"
fi
echo -e "${green}✓ Custom certificate paths applied.${plain}"
echo -e "${yellow}Note: You are responsible for renewing these files externally.${plain}"
systemctl restart x-ui >/dev/null 2>&1 || rc-service x-ui restart >/dev/null 2>&1
;; ;;
*) *)
echo -e "${red}Invalid option. Skipping SSL setup.${plain}" echo -e "${red}Invalid option. Skipping SSL setup.${plain}"

View file

@ -153,31 +153,6 @@ func (s *Server) initRouter() (*gin.Engine, error) {
SubTitle = "" SubTitle = ""
} }
SubSupportUrl, err := s.settingService.GetSubSupportUrl()
if err != nil {
SubSupportUrl = ""
}
SubProfileUrl, err := s.settingService.GetSubProfileUrl()
if err != nil {
SubProfileUrl = ""
}
SubAnnounce, err := s.settingService.GetSubAnnounce()
if err != nil {
SubAnnounce = ""
}
SubEnableRouting, err := s.settingService.GetSubEnableRouting()
if err != nil {
return nil, err
}
SubRoutingRules, err := s.settingService.GetSubRoutingRules()
if err != nil {
SubRoutingRules = ""
}
// set per-request localizer from headers/cookies // set per-request localizer from headers/cookies
engine.Use(locale.LocalizerMiddleware()) engine.Use(locale.LocalizerMiddleware())
@ -256,8 +231,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
s.sub = NewSUBController( s.sub = NewSUBController(
g, LinksPath, JsonPath, subJsonEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates, g, LinksPath, JsonPath, subJsonEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates,
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubTitle, SubSupportUrl, SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubTitle)
SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules)
return engine, nil return engine, nil
} }

View file

@ -4,7 +4,6 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"strings" "strings"
"strconv"
"github.com/mhsanaei/3x-ui/v2/config" "github.com/mhsanaei/3x-ui/v2/config"
@ -13,17 +12,12 @@ import (
// SUBController handles HTTP requests for subscription links and JSON configurations. // SUBController handles HTTP requests for subscription links and JSON configurations.
type SUBController struct { type SUBController struct {
subTitle string subTitle string
subSupportUrl string subPath string
subProfileUrl string subJsonPath string
subAnnounce string jsonEnabled bool
subEnableRouting bool subEncrypt bool
subRoutingRules string updateInterval string
subPath string
subJsonPath string
jsonEnabled bool
subEncrypt bool
updateInterval string
subService *SubService subService *SubService
subJsonService *SubJsonService subJsonService *SubJsonService
@ -44,28 +38,18 @@ func NewSUBController(
jsonMux string, jsonMux string,
jsonRules string, jsonRules string,
subTitle string, subTitle string,
subSupportUrl string,
subProfileUrl string,
subAnnounce string,
subEnableRouting bool,
subRoutingRules string,
) *SUBController { ) *SUBController {
sub := NewSubService(showInfo, rModel) sub := NewSubService(showInfo, rModel)
a := &SUBController{ a := &SUBController{
subTitle: subTitle, subTitle: subTitle,
subSupportUrl: subSupportUrl, subPath: subPath,
subProfileUrl: subProfileUrl, subJsonPath: jsonPath,
subAnnounce: subAnnounce, jsonEnabled: jsonEnabled,
subEnableRouting: subEnableRouting, subEncrypt: encrypt,
subRoutingRules: subRoutingRules, updateInterval: update,
subPath: subPath,
subJsonPath: jsonPath,
jsonEnabled: jsonEnabled,
subEncrypt: encrypt,
updateInterval: update,
subService: sub, subService: sub,
subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub), subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
} }
a.initRouter(g) a.initRouter(g)
return a return a
@ -143,7 +127,7 @@ func (a *SUBController) subs(c *gin.Context) {
// Add headers // Add headers
header := fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000) header := fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, a.subProfileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules) a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle)
if a.subEncrypt { if a.subEncrypt {
c.String(200, base64.StdEncoding.EncodeToString([]byte(result))) c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
@ -161,31 +145,17 @@ func (a *SUBController) subJsons(c *gin.Context) {
if err != nil || len(jsonSub) == 0 { if err != nil || len(jsonSub) == 0 {
c.String(400, "Error!") c.String(400, "Error!")
} else { } else {
// Add headers // Add headers
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, a.subProfileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules) a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle)
c.String(200, jsonSub) c.String(200, jsonSub)
} }
} }
// 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, header, updateInterval, profileTitle string) {
c *gin.Context,
header,
updateInterval,
profileTitle string,
profileSupportUrl string,
profileUrl string,
profileAnnounce string,
profileEnableRouting bool,
profileRoutingRules string,
) {
c.Writer.Header().Set("Subscription-Userinfo", header) c.Writer.Header().Set("Subscription-Userinfo", header)
c.Writer.Header().Set("Profile-Update-Interval", updateInterval) c.Writer.Header().Set("Profile-Update-Interval", updateInterval)
c.Writer.Header().Set("Profile-Title", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileTitle))) c.Writer.Header().Set("Profile-Title", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileTitle)))
c.Writer.Header().Set("Support-Url", profileSupportUrl)
c.Writer.Header().Set("Profile-Web-Page-Url", profileUrl)
c.Writer.Header().Set("Announce", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileAnnounce)))
c.Writer.Header().Set("Routing-Enable", strconv.FormatBool(profileEnableRouting))
c.Writer.Header().Set("Routing", profileRoutingRules)
} }

View file

@ -541,13 +541,12 @@ prompt_and_setup_ssl() {
echo -e "${yellow}Choose SSL certificate setup method:${plain}" echo -e "${yellow}Choose SSL certificate setup method:${plain}"
echo -e "${green}1.${plain} Let's Encrypt for Domain (90-day validity, auto-renews)" echo -e "${green}1.${plain} Let's Encrypt for Domain (90-day validity, auto-renews)"
echo -e "${green}2.${plain} Let's Encrypt for IP Address (6-day validity, auto-renews)" echo -e "${green}2.${plain} Let's Encrypt for IP Address (6-day validity, auto-renews)"
echo -e "${green}3.${plain} Custom SSL Certificate (Path to existing files)" echo -e "${blue}Note:${plain} Both options require port 80 open. IP certs use shortlived profile."
echo -e "${blue}Note:${plain} Options 1 & 2 require port 80 open. Option 3 requires manual paths."
read -rp "Choose an option (default 2 for IP): " ssl_choice read -rp "Choose an option (default 2 for IP): " ssl_choice
ssl_choice="${ssl_choice// /}" # Trim whitespace ssl_choice="${ssl_choice// /}" # Trim whitespace
# Default to 2 (IP cert) if input is empty or invalid (not 1 or 3) # Default to 2 (IP cert) if not 1
if [[ "$ssl_choice" != "1" && "$ssl_choice" != "3" ]]; then if [[ "$ssl_choice" != "1" ]]; then
ssl_choice="2" ssl_choice="2"
fi fi
@ -597,67 +596,6 @@ prompt_and_setup_ssl() {
else else
systemctl restart x-ui >/dev/null 2>&1 systemctl restart x-ui >/dev/null 2>&1
fi fi
;;
3)
# User chose Custom Paths (User Provided) option
echo -e "${green}Using custom existing certificate...${plain}"
local custom_cert=""
local custom_key=""
local custom_domain=""
# 3.1 Request Domain to compose Panel URL later
read -rp "Please enter domain name certificate issued for: " custom_domain
custom_domain="${custom_domain// /}" # Убираем пробелы
# 3.2 Loop for Certificate Path
while true; do
read -rp "Input certificate path (keywords: .crt / fullchain): " custom_cert
# Strip quotes if present
custom_cert=$(echo "$custom_cert" | tr -d '"' | tr -d "'")
if [[ -f "$custom_cert" && -r "$custom_cert" && -s "$custom_cert" ]]; then
break
elif [[ ! -f "$custom_cert" ]]; then
echo -e "${red}Error: File does not exist! Try again.${plain}"
elif [[ ! -r "$custom_cert" ]]; then
echo -e "${red}Error: File exists but is not readable (check permissions)!${plain}"
else
echo -e "${red}Error: File is empty!${plain}"
fi
done
# 3.3 Loop for Private Key Path
while true; do
read -rp "Input private key path (keywords: .key / privatekey): " custom_key
# Strip quotes if present
custom_key=$(echo "$custom_key" | tr -d '"' | tr -d "'")
if [[ -f "$custom_key" && -r "$custom_key" && -s "$custom_key" ]]; then
break
elif [[ ! -f "$custom_key" ]]; then
echo -e "${red}Error: File does not exist! Try again.${plain}"
elif [[ ! -r "$custom_key" ]]; then
echo -e "${red}Error: File exists but is not readable (check permissions)!${plain}"
else
echo -e "${red}Error: File is empty!${plain}"
fi
done
# 3.4 Apply Settings via x-ui binary
${xui_folder}/x-ui cert -webCert "$custom_cert" -webCertKey "$custom_key" >/dev/null 2>&1
# Set SSL_HOST for composing Panel URL
if [[ -n "$custom_domain" ]]; then
SSL_HOST="$custom_domain"
else
SSL_HOST="${server_ip}"
fi
echo -e "${green}✓ Custom certificate paths applied.${plain}"
echo -e "${yellow}Note: You are responsible for renewing these files externally.${plain}"
systemctl restart x-ui >/dev/null 2>&1 || rc-service x-ui restart >/dev/null 2>&1
;; ;;
*) *)
echo -e "${red}Invalid option. Skipping SSL setup.${plain}" echo -e "${red}Invalid option. Skipping SSL setup.${plain}"

View file

@ -29,11 +29,6 @@ class AllSetting {
this.subEnable = true; this.subEnable = true;
this.subJsonEnable = false; this.subJsonEnable = false;
this.subTitle = ""; this.subTitle = "";
this.subSupportUrl = "";
this.subProfileUrl = "";
this.subAnnounce = "";
this.subEnableRouting = true;
this.subRoutingRules = "";
this.subListen = ""; this.subListen = "";
this.subPort = 2096; this.subPort = 2096;
this.subPath = "/sub/"; this.subPath = "/sub/";

View file

@ -57,11 +57,6 @@ type AllSetting struct {
SubEnable bool `json:"subEnable" form:"subEnable"` // Enable subscription server SubEnable bool `json:"subEnable" form:"subEnable"` // Enable subscription server
SubJsonEnable bool `json:"subJsonEnable" form:"subJsonEnable"` // Enable JSON subscription endpoint SubJsonEnable bool `json:"subJsonEnable" form:"subJsonEnable"` // Enable JSON subscription endpoint
SubTitle string `json:"subTitle" form:"subTitle"` // Subscription title SubTitle string `json:"subTitle" form:"subTitle"` // Subscription title
SubSupportUrl string `json:"subSupportUrl" form:"subSupportUrl"` // Subscription support URL
SubProfileUrl string `json:"subProfileUrl" form:"subProfileUrl"` // Subscription profile URL
SubAnnounce string `json:"subAnnounce" form:"subAnnounce"` // Subscription announce
SubEnableRouting bool `json:"subEnableRouting" form:"subEnableRouting"` // Enable routing for subscription
SubRoutingRules string `json:"subRoutingRules" form:"subRoutingRules"` // Subscription global routing rules (Only for Happ)
SubListen string `json:"subListen" form:"subListen"` // Subscription server listen IP SubListen string `json:"subListen" form:"subListen"` // Subscription server listen IP
SubPort int `json:"subPort" form:"subPort"` // Subscription server port SubPort int `json:"subPort" form:"subPort"` // Subscription server port
SubPath string `json:"subPath" form:"subPath"` // Base path for subscription URLs SubPath string `json:"subPath" form:"subPath"` // Base path for subscription URLs

View file

@ -772,7 +772,7 @@
<a-space direction="vertical" :size="10" :style="{ marginTop: '10px' }"> <a-space direction="vertical" :size="10" :style="{ marginTop: '10px' }">
<a-input addon-before='{{ i18n "pages.xray.outbound.link" }}' <a-input addon-before='{{ i18n "pages.xray.outbound.link" }}'
v-model.trim="outModal.link" v-model.trim="outModal.link"
placeholder="vmess:// vless:// trojan:// ss:// hysteria2://"> placeholder="vmess:// vless:// trojan:// ss://">
<a-icon slot="addonAfter" type="form" @click="convertLink"></a-icon> <a-icon slot="addonAfter" type="form" @click="convertLink"></a-icon>
</a-input> </a-input>
<textarea :style="{ position: 'absolute', left: '-800px' }" <textarea :style="{ position: 'absolute', left: '-800px' }"

View file

@ -15,6 +15,13 @@
<a-switch v-model="allSetting.subJsonEnable"></a-switch> <a-switch v-model="allSetting.subJsonEnable"></a-switch>
</template> </template>
</a-setting-list-item> </a-setting-list-item>
<a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subTitle"}}</template>
<template #description>{{ i18n "pages.settings.subTitleDesc"}}</template>
<template #control>
<a-input type="text" v-model="allSetting.subTitle"></a-input>
</template>
</a-setting-list-item>
<a-setting-list-item paddings="small"> <a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subListen"}}</template> <template #title>{{ i18n "pages.settings.subListen"}}</template>
<template #description>{{ i18n "pages.settings.subListenDesc"}}</template> <template #description>{{ i18n "pages.settings.subListenDesc"}}</template>
@ -71,50 +78,6 @@
<a-switch v-model="allSetting.subShowInfo"></a-switch> <a-switch v-model="allSetting.subShowInfo"></a-switch>
</template> </template>
</a-setting-list-item> </a-setting-list-item>
<a-divider>{{ i18n "pages.xray.basicTemplate"}}</a-divider>
<a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subTitle"}}</template>
<template #description>{{ i18n "pages.settings.subTitleDesc"}}</template>
<template #control>
<a-input type="text" v-model="allSetting.subTitle"></a-input>
</template>
</a-setting-list-item>
<a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subSupportUrl"}}</template>
<template #description>{{ i18n "pages.settings.subSupportUrlDesc"}}</template>
<template #control>
<a-input type="text" v-model="allSetting.subSupportUrl" placeholder="https://example.com"></a-input>
</template>
</a-setting-list-item>
<a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subProfileUrl"}}</template>
<template #description>{{ i18n "pages.settings.subProfileUrlDesc"}}</template>
<template #control>
<a-input type="text" v-model="allSetting.subProfileUrl" placeholder="https://example.com"></a-input>
</template>
</a-setting-list-item>
<a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subAnnounce"}}</template>
<template #description>{{ i18n "pages.settings.subAnnounceDesc"}}</template>
<template #control>
<a-textarea v-model="allSetting.subAnnounce"></a-textarea>
</template>
</a-setting-list-item>
<a-divider>{{ i18n "pages.xray.advancedTemplate"}} (Happ)</a-divider>
<a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subEnableRouting"}}</template>
<template #description>{{ i18n "pages.settings.subEnableRoutingDesc"}}</template>
<template #control>
<a-switch v-model="allSetting.subEnableRouting"></a-switch>
</template>
</a-setting-list-item>
<a-setting-list-item paddings="small">
<template #title>{{ i18n "pages.settings.subRoutingRules"}}</template>
<template #description>{{ i18n "pages.settings.subRoutingRulesDesc"}}</template>
<template #control>
<a-textarea v-model="allSetting.subRoutingRules" placeholder="happ://routing/add/..."></a-textarea>
</template>
</a-setting-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel key="3" header='{{ i18n "pages.settings.certs" }}'> <a-collapse-panel key="3" header='{{ i18n "pages.settings.certs" }}'>
<a-setting-list-item paddings="small"> <a-setting-list-item paddings="small">

View file

@ -53,11 +53,6 @@ var defaultValueMap = map[string]string{
"subEnable": "true", "subEnable": "true",
"subJsonEnable": "false", "subJsonEnable": "false",
"subTitle": "", "subTitle": "",
"subSupportUrl": "",
"subProfileUrl": "",
"subAnnounce": "",
"subEnableRouting": "true",
"subRoutingRules": "",
"subListen": "", "subListen": "",
"subPort": "2096", "subPort": "2096",
"subPath": "/sub/", "subPath": "/sub/",
@ -464,26 +459,6 @@ func (s *SettingService) GetSubTitle() (string, error) {
return s.getString("subTitle") return s.getString("subTitle")
} }
func (s *SettingService) GetSubSupportUrl() (string, error) {
return s.getString("subSupportUrl")
}
func (s *SettingService) GetSubProfileUrl() (string, error) {
return s.getString("subProfileUrl")
}
func (s *SettingService) GetSubAnnounce() (string, error) {
return s.getString("subAnnounce")
}
func (s *SettingService) GetSubEnableRouting() (bool, error) {
return s.getBool("subEnableRouting")
}
func (s *SettingService) GetSubRoutingRules() (string, error) {
return s.getString("subRoutingRules")
}
func (s *SettingService) GetSubListen() (string, error) { func (s *SettingService) GetSubListen() (string, error) {
return s.getString("subListen") return s.getString("subListen")
} }

View file

@ -2267,8 +2267,6 @@ func (t *Tgbot) buildSubscriptionURLs(email string) (string, string, error) {
} }
// Gather settings to construct absolute URLs // Gather settings to construct absolute URLs
subURI, _ := t.settingService.GetSubURI()
subJsonURI, _ := t.settingService.GetSubJsonURI()
subDomain, _ := t.settingService.GetSubDomain() subDomain, _ := t.settingService.GetSubDomain()
subPort, _ := t.settingService.GetSubPort() subPort, _ := t.settingService.GetSubPort()
subPath, _ := t.settingService.GetSubPath() subPath, _ := t.settingService.GetSubPath()
@ -2316,29 +2314,8 @@ func (t *Tgbot) buildSubscriptionURLs(email string) (string, string, error) {
subJsonPath = subJsonPath + "/" subJsonPath = subJsonPath + "/"
} }
var subURL string subURL := fmt.Sprintf("%s://%s%s%s", scheme, host, subPath, client.SubID)
var subJsonURL string subJsonURL := fmt.Sprintf("%s://%s%s%s", scheme, host, subJsonPath, client.SubID)
// If pre-configured URIs are available, use them directly
if subURI != "" {
if !strings.HasSuffix(subURI, "/") {
subURI = subURI + "/"
}
subURL = fmt.Sprintf("%s%s", subURI, client.SubID)
} else {
subURL = fmt.Sprintf("%s://%s%s%s", scheme, host, subPath, client.SubID)
}
if subJsonURI != "" {
if !strings.HasSuffix(subJsonURI, "/") {
subJsonURI = subJsonURI + "/"
}
subJsonURL = fmt.Sprintf("%s%s", subJsonURI, client.SubID)
} else {
subJsonURL = fmt.Sprintf("%s://%s%s%s", scheme, host, subJsonPath, client.SubID)
}
if !subJsonEnable { if !subJsonEnable {
subJsonURL = "" subJsonURL = ""
} }

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "تمكين/تعطيل نقطة نهاية اشتراك JSON بشكل مستقل." "subJsonEnable" = "تمكين/تعطيل نقطة نهاية اشتراك JSON بشكل مستقل."
"subTitle" = "عنوان الاشتراك" "subTitle" = "عنوان الاشتراك"
"subTitleDesc" = "العنوان اللي هيظهر في عميل VPN" "subTitleDesc" = "العنوان اللي هيظهر في عميل VPN"
"subSupportUrl" = "رابط الدعم"
"subSupportUrlDesc" = "رابط الدعم الفني المعروض في عميل VPN"
"subProfileUrl" = "رابط الملف الشخصي"
"subProfileUrlDesc" = "رابط لموقعك الإلكتروني يظهر في عميل VPN"
"subAnnounce" = "إعلان"
"subAnnounceDesc" = "نص الإعلان المعروض في عميل VPN"
"subEnableRouting" = "تفعيل التوجيه"
"subEnableRoutingDesc" = "إعداد عام لتمكين التوجيه (Routing) في عميل VPN. (فقط لـ Happ)"
"subRoutingRules" = "قواعد التوجيه"
"subRoutingRulesDesc" = "قواعد التوجيه العامة لعميل VPN. (فقط لـ Happ)"
"subListen" = "IP الاستماع" "subListen" = "IP الاستماع"
"subListenDesc" = "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)" "subListenDesc" = "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)"
"subPort" = "بورت الاستماع" "subPort" = "بورت الاستماع"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "Enable/Disable the JSON subscription endpoint independently." "subJsonEnable" = "Enable/Disable the JSON subscription endpoint independently."
"subTitle" = "Subscription Title" "subTitle" = "Subscription Title"
"subTitleDesc" = "Title shown in VPN client" "subTitleDesc" = "Title shown in VPN client"
"subSupportUrl" = "Support URL"
"subSupportUrlDesc" = "Technical support link shown in the VPN client"
"subProfileUrl" = "Profile URL"
"subProfileUrlDesc" = "A link to your website displayed in the VPN client"
"subAnnounce" = "Announce"
"subAnnounceDesc" = "The text of the announce displayed in the VPN client"
"subEnableRouting" = "Enable routing"
"subEnableRoutingDesc" = "Global setting to enable routing in the VPN client. (Only for Happ)"
"subRoutingRules" = "Routing rules"
"subRoutingRulesDesc" = "Global routing rules for the VPN client. (Only for Happ)"
"subListen" = "Listen IP" "subListen" = "Listen IP"
"subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)" "subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)"
"subPort" = "Listen Port" "subPort" = "Listen Port"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "Habilitar/Deshabilitar el endpoint de suscripción JSON de forma independiente." "subJsonEnable" = "Habilitar/Deshabilitar el endpoint de suscripción JSON de forma independiente."
"subTitle" = "Título de la Suscripción" "subTitle" = "Título de la Suscripción"
"subTitleDesc" = "Título mostrado en el cliente de VPN" "subTitleDesc" = "Título mostrado en el cliente de VPN"
"subSupportUrl" = "URL de soporte"
"subSupportUrlDesc" = "Enlace de soporte técnico mostrado en el cliente VPN"
"subProfileUrl" = "URL del perfil"
"subProfileUrlDesc" = "Un enlace a tu sitio web mostrado en el cliente VPN"
"subAnnounce" = "Anuncio"
"subAnnounceDesc" = "El texto del anuncio mostrado en el cliente VPN"
"subEnableRouting" = "Habilitar enrutamiento"
"subEnableRoutingDesc" = "Configuración global para habilitar el enrutamiento en el cliente VPN. (Solo para Happ)"
"subRoutingRules" = "Reglas de enrutamiento"
"subRoutingRulesDesc" = "Reglas de enrutamiento globales para el cliente VPN. (Solo para Happ)"
"subListen" = "Listening IP" "subListen" = "Listening IP"
"subListenDesc" = "Dejar en blanco por defecto para monitorear todas las IPs." "subListenDesc" = "Dejar en blanco por defecto para monitorear todas las IPs."
"subPort" = "Puerto de Suscripción" "subPort" = "Puerto de Suscripción"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "فعال/غیرفعال‌سازی مستقل نقطه دسترسی سابسکریپشن JSON." "subJsonEnable" = "فعال/غیرفعال‌سازی مستقل نقطه دسترسی سابسکریپشن JSON."
"subTitle" = "عنوان اشتراک" "subTitle" = "عنوان اشتراک"
"subTitleDesc" = "عنوان نمایش داده شده در کلاینت VPN" "subTitleDesc" = "عنوان نمایش داده شده در کلاینت VPN"
"subSupportUrl" = "آدرس پشتیبانی"
"subSupportUrlDesc" = "لینک پشتیبانی فنی که در کلاینت VPN نمایش داده می‌شود"
"subProfileUrl" = "آدرس پروفایل"
"subProfileUrlDesc" = "لینک وب‌سایت شما که در کلاینت VPN نمایش داده می‌شود"
"subAnnounce" = "اعلان"
"subAnnounceDesc" = "متن اعلانی که در کلاینت VPN نمایش داده می‌شود"
"subEnableRouting" = "فعال‌سازی مسیریابی"
"subEnableRoutingDesc" = "تنظیمات سراسری برای فعال‌سازی مسیریابی در کلاینت VPN. (فقط برای Happ)"
"subRoutingRules" = "قوانین مسیریابی"
"subRoutingRulesDesc" = "قوانین مسیریابی سراسری برای کلاینت VPN. (فقط برای Happ)"
"subListen" = "آدرس آی‌پی" "subListen" = "آدرس آی‌پی"
"subListenDesc" = "آدرس آی‌پی برای سرویس سابسکریپشن. برای گوش دادن به‌تمام آی‌پی‌ها خالی‌بگذارید" "subListenDesc" = "آدرس آی‌پی برای سرویس سابسکریپشن. برای گوش دادن به‌تمام آی‌پی‌ها خالی‌بگذارید"
"subPort" = "پورت" "subPort" = "پورت"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "Aktifkan/Nonaktifkan endpoint langganan JSON secara mandiri." "subJsonEnable" = "Aktifkan/Nonaktifkan endpoint langganan JSON secara mandiri."
"subTitle" = "Judul Langganan" "subTitle" = "Judul Langganan"
"subTitleDesc" = "Judul yang ditampilkan di klien VPN" "subTitleDesc" = "Judul yang ditampilkan di klien VPN"
"subSupportUrl" = "URL Dukungan"
"subSupportUrlDesc" = "Tautan dukungan teknis yang ditampilkan di klien VPN"
"subProfileUrl" = "URL Profil"
"subProfileUrlDesc" = "Tautan ke situs web Anda yang ditampilkan di klien VPN"
"subAnnounce" = "Pengumuman"
"subAnnounceDesc" = "Teks pengumuman yang ditampilkan di klien VPN"
"subEnableRouting" = "Aktifkan perutean"
"subEnableRoutingDesc" = "Pengaturan global untuk mengaktifkan perutean (routing) di klien VPN. (Hanya untuk Happ)"
"subRoutingRules" = "Aturan routing"
"subRoutingRulesDesc" = "Aturan routing global untuk klien VPN. (Hanya untuk Happ)"
"subListen" = "IP Pendengar" "subListen" = "IP Pendengar"
"subListenDesc" = "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)" "subListenDesc" = "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)"
"subPort" = "Port Pendengar" "subPort" = "Port Pendengar"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "JSON サブスクリプションのエンドポイントを個別に有効/無効にする。" "subJsonEnable" = "JSON サブスクリプションのエンドポイントを個別に有効/無効にする。"
"subTitle" = "サブスクリプションタイトル" "subTitle" = "サブスクリプションタイトル"
"subTitleDesc" = "VPNクライアントに表示されるタイトル" "subTitleDesc" = "VPNクライアントに表示されるタイトル"
"subSupportUrl" = "サポートURL"
"subSupportUrlDesc" = "VPNクライアントに表示されるテクニカルサポートへのリンク"
"subProfileUrl" = "プロフィールURL"
"subProfileUrlDesc" = "VPNクライアントに表示されるWebサイトへのリンク"
"subAnnounce" = "お知らせ"
"subAnnounceDesc" = "VPNクライアントに表示されるお知らせのテキスト"
"subEnableRouting" = "ルーティングを有効化"
"subEnableRoutingDesc" = "VPNクライアントでルーティングを有効にするためのグローバル設定。(Happのみ)"
"subRoutingRules" = "ルーティングルール"
"subRoutingRulesDesc" = "VPNクライアントのグローバルルーティングルール。(Happのみ)"
"subListen" = "監視IP" "subListen" = "監視IP"
"subListenDesc" = "サブスクリプションサービスが監視するIPアドレス空白にするとすべてのIPを監視" "subListenDesc" = "サブスクリプションサービスが監視するIPアドレス空白にするとすべてのIPを監視"
"subPort" = "監視ポート" "subPort" = "監視ポート"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "Ativar/Desativar o endpoint de assinatura JSON de forma independente." "subJsonEnable" = "Ativar/Desativar o endpoint de assinatura JSON de forma independente."
"subTitle" = "Título da Assinatura" "subTitle" = "Título da Assinatura"
"subTitleDesc" = "Título exibido no cliente VPN" "subTitleDesc" = "Título exibido no cliente VPN"
"subSupportUrl" = "URL de Suporte"
"subSupportUrlDesc" = "Link de suporte técnico exibido no cliente VPN"
"subProfileUrl" = "URL de Perfil"
"subProfileUrlDesc" = "Um link para o seu site exibido no cliente VPN"
"subAnnounce" = "Anúncio"
"subAnnounceDesc" = "O texto do anúncio exibido no cliente VPN"
"subEnableRouting" = "Ativar roteamento"
"subEnableRoutingDesc" = "Configuração global para habilitar o roteamento no cliente VPN. (Apenas para Happ)"
"subRoutingRules" = "Regras de roteamento"
"subRoutingRulesDesc" = "Regras de roteamento globais para o cliente VPN. (Apenas para Happ)"
"subListen" = "IP de Escuta" "subListen" = "IP de Escuta"
"subListenDesc" = "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)" "subListenDesc" = "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)"
"subPort" = "Porta de Escuta" "subPort" = "Porta de Escuta"

View file

@ -373,17 +373,7 @@
"subEnableDesc" = "Функция подписки с отдельной конфигурацией" "subEnableDesc" = "Функция подписки с отдельной конфигурацией"
"subJsonEnable" = "Включить/отключить JSON-эндпоинт подписки независимо." "subJsonEnable" = "Включить/отключить JSON-эндпоинт подписки независимо."
"subTitle" = "Заголовок подписки" "subTitle" = "Заголовок подписки"
"subTitleDesc" = "Название подписки, которое видит клиент в VPN-клиенте" "subTitleDesc" = "Название подписки, которое видит клиент в VPN клиенте"
"subSupportUrl" = "URL поддержки"
"subSupportUrlDesc" = "Ссылка на техническую поддержку, отображаемая в VPN-клиенте"
"subProfileUrl" = "URL профиля"
"subProfileUrlDesc" = "Ссылка на ваш сайт, отображаемая в VPN-клиенте"
"subAnnounce" = "Объявление"
"subAnnounceDesc" = "Текст объявления, отображаемый в VPN-клиенте"
"subEnableRouting" = "Включить маршрутизацию"
"subEnableRoutingDesc" = "Глобальная настройка для включения маршрутизации в VPN-клиенте. (Только для Happ)"
"subRoutingRules" = "Правила маршрутизации"
"subRoutingRulesDesc" = "Глобальные правила маршрутизации для VPN-клиента. (Только для Happ)"
"subListen" = "Прослушивание IP" "subListen" = "Прослушивание IP"
"subListenDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса" "subListenDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса"
"subPort" = "Порт подписки" "subPort" = "Порт подписки"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "JSON abonelik uç noktasını bağımsız olarak Etkinleştir/Devre Dışı bırak." "subJsonEnable" = "JSON abonelik uç noktasını bağımsız olarak Etkinleştir/Devre Dışı bırak."
"subTitle" = "Abonelik Başlığı" "subTitle" = "Abonelik Başlığı"
"subTitleDesc" = "VPN istemcisinde gösterilen başlık" "subTitleDesc" = "VPN istemcisinde gösterilen başlık"
"subSupportUrl" = "Destek URL'si"
"subSupportUrlDesc" = "VPN istemcisinde gösterilen teknik destek bağlantısı"
"subProfileUrl" = "Profil URL'si"
"subProfileUrlDesc" = "VPN istemcisinde görüntülenen web sitenize giden bağlantı"
"subAnnounce" = "Duyuru"
"subAnnounceDesc" = "VPN istemcisinde görüntülenen duyuru metni"
"subEnableRouting" = "Yönlendirmeyi etkinleştir"
"subEnableRoutingDesc" = "VPN istemcisinde yönlendirmeyi etkinleştirmek için genel ayar. (Yalnızca Happ için)"
"subRoutingRules" = "Yönlendirme kuralları"
"subRoutingRulesDesc" = "VPN istemcisi için genel yönlendirme kuralları. (Yalnızca Happ için)"
"subListen" = "Dinleme IP" "subListen" = "Dinleme IP"
"subListenDesc" = "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)" "subListenDesc" = "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)"
"subPort" = "Dinleme Portu" "subPort" = "Dinleme Portu"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "Увімкнути/вимкнути JSON-кінець підписки незалежно." "subJsonEnable" = "Увімкнути/вимкнути JSON-кінець підписки незалежно."
"subTitle" = "Назва Підписки" "subTitle" = "Назва Підписки"
"subTitleDesc" = "Назва, яка відображається у VPN-клієнті" "subTitleDesc" = "Назва, яка відображається у VPN-клієнті"
"subSupportUrl" = "URL підтримки"
"subSupportUrlDesc" = "Посилання на технічну підтримку, що відображається у VPN-клієнті"
"subProfileUrl" = "URL профілю"
"subProfileUrlDesc" = "Посилання на ваш вебсайт, що відображається у VPN-клієнті"
"subAnnounce" = "Оголошення"
"subAnnounceDesc" = "Текст оголошення, що відображається у VPN-клієнті"
"subEnableRouting" = "Увімкнути маршрутизацію"
"subEnableRoutingDesc" = "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті. (Тільки для Happ)"
"subRoutingRules" = "Правила маршрутизації"
"subRoutingRulesDesc" = "Глобальні правила маршрутизації для VPN-клієнта. (Тільки для Happ)"
"subListen" = "Слухати IP" "subListen" = "Слухати IP"
"subListenDesc" = "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)" "subListenDesc" = "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)"
"subPort" = "Слухати порт" "subPort" = "Слухати порт"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "Bật/Tắt điểm cuối đăng ký JSON độc lập." "subJsonEnable" = "Bật/Tắt điểm cuối đăng ký JSON độc lập."
"subTitle" = "Tiêu đề Đăng ký" "subTitle" = "Tiêu đề Đăng ký"
"subTitleDesc" = "Tiêu đề hiển thị trong ứng dụng VPN" "subTitleDesc" = "Tiêu đề hiển thị trong ứng dụng VPN"
"subSupportUrl" = "URL Hỗ trợ"
"subSupportUrlDesc" = "Liên kết hỗ trợ kỹ thuật hiển thị trong ứng dụng VPN"
"subProfileUrl" = "URL Hồ sơ"
"subProfileUrlDesc" = "Liên kết đến trang web của bạn hiển thị trong ứng dụng VPN"
"subAnnounce" = "Thông báo"
"subAnnounceDesc" = "Văn bản thông báo hiển thị trong ứng dụng VPN"
"subEnableRouting" = "Bật định tuyến"
"subEnableRoutingDesc" = "Cài đặt toàn cục để bật định tuyến trong ứng dụng khách VPN. (Chỉ dành cho Happ)"
"subRoutingRules" = "Quy tắc định tuyến"
"subRoutingRulesDesc" = "Quy tắc định tuyến toàn cầu cho client VPN. (Chỉ dành cho Happ)"
"subListen" = "Listening IP" "subListen" = "Listening IP"
"subListenDesc" = "Mặc định để trống để nghe tất cả các IP" "subListenDesc" = "Mặc định để trống để nghe tất cả các IP"
"subPort" = "Cổng gói đăng ký" "subPort" = "Cổng gói đăng ký"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "单独启用/禁用 JSON 订阅端点。" "subJsonEnable" = "单独启用/禁用 JSON 订阅端点。"
"subTitle" = "订阅标题" "subTitle" = "订阅标题"
"subTitleDesc" = "在VPN客户端中显示的标题" "subTitleDesc" = "在VPN客户端中显示的标题"
"subSupportUrl" = "支持链接"
"subSupportUrlDesc" = "VPN 客户端中显示的技术支持链接"
"subProfileUrl" = "个人资料链接"
"subProfileUrlDesc" = "VPN 客户端中显示的网站链接"
"subAnnounce" = "公告"
"subAnnounceDesc" = "VPN 客户端中显示的公告文本"
"subEnableRouting" = "启用路由"
"subEnableRoutingDesc" = "在 VPN 客户端中启用路由的全局设置。(僅限 Happ"
"subRoutingRules" = "路由規則"
"subRoutingRulesDesc" = "VPN 用戶端的全域路由規則。(僅限 Happ"
"subListen" = "监听 IP" "subListen" = "监听 IP"
"subListenDesc" = "订阅服务监听的 IP 地址(留空表示监听所有 IP" "subListenDesc" = "订阅服务监听的 IP 地址(留空表示监听所有 IP"
"subPort" = "监听端口" "subPort" = "监听端口"

View file

@ -374,16 +374,6 @@
"subJsonEnable" = "獨立啟用/停用 JSON 訂閱端點。" "subJsonEnable" = "獨立啟用/停用 JSON 訂閱端點。"
"subTitle" = "訂閱標題" "subTitle" = "訂閱標題"
"subTitleDesc" = "在VPN客戶端中顯示的標題" "subTitleDesc" = "在VPN客戶端中顯示的標題"
"subSupportUrl" = "支援連結"
"subSupportUrlDesc" = "VPN 用戶端中顯示的技術支援連結"
"subProfileUrl" = "個人資料連結"
"subProfileUrlDesc" = "VPN 用戶端中顯示的網站連結"
"subAnnounce" = "公告"
"subAnnounceDesc" = "VPN 用戶端中顯示的公告文字"
"subEnableRouting" = "啟用路由"
"subEnableRoutingDesc" = "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ"
"subRoutingRules" = "路由規則"
"subRoutingRulesDesc" = "VPN 用戶端的全域路由規則。(僅限 Happ"
"subListen" = "監聽 IP" "subListen" = "監聽 IP"
"subListenDesc" = "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP" "subListenDesc" = "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP"
"subPort" = "監聽埠" "subPort" = "監聽埠"