From ca026483dfab8de8aa1d9e3851c2017f16a43460 Mon Sep 17 00:00:00 2001
From: "Danil S." <135337715+sh1shd@users.noreply.github.com>
Date: Mon, 26 Jan 2026 06:25:52 +0000
Subject: [PATCH] feat: implement field for Happ custom routing rules
---
sub/sub.go | 7 ++++++-
sub/subController.go | 9 +++++++--
web/assets/js/model/setting.js | 1 +
web/entity/entity.go | 1 +
web/html/settings/panel/subscription/general.html | 14 +++++++++++---
web/service/setting.go | 5 +++++
web/translation/translate.ar_EG.toml | 4 +++-
web/translation/translate.en_US.toml | 4 +++-
web/translation/translate.es_ES.toml | 4 +++-
web/translation/translate.fa_IR.toml | 4 +++-
web/translation/translate.id_ID.toml | 4 +++-
web/translation/translate.ja_JP.toml | 4 +++-
web/translation/translate.pt_BR.toml | 4 +++-
web/translation/translate.ru_RU.toml | 4 +++-
web/translation/translate.tr_TR.toml | 4 +++-
web/translation/translate.uk_UA.toml | 4 +++-
web/translation/translate.vi_VN.toml | 4 +++-
web/translation/translate.zh_CN.toml | 4 +++-
web/translation/translate.zh_TW.toml | 4 +++-
19 files changed, 70 insertions(+), 19 deletions(-)
diff --git a/sub/sub.go b/sub/sub.go
index a8fa2524..1dcd9601 100644
--- a/sub/sub.go
+++ b/sub/sub.go
@@ -173,6 +173,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
return nil, err
}
+ SubRoutingRules, err := s.settingService.GetSubRoutingRules()
+ if err != nil {
+ SubRoutingRules = ""
+ }
+
// set per-request localizer from headers/cookies
engine.Use(locale.LocalizerMiddleware())
@@ -252,7 +257,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
s.sub = NewSUBController(
g, LinksPath, JsonPath, subJsonEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates,
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubTitle, SubSupportUrl,
- SubProfileUrl, SubAnnounce, SubEnableRouting)
+ SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules)
return engine, nil
}
diff --git a/sub/subController.go b/sub/subController.go
index a73dc309..7653a4e1 100644
--- a/sub/subController.go
+++ b/sub/subController.go
@@ -18,6 +18,7 @@ type SUBController struct {
subProfileUrl string
subAnnounce string
subEnableRouting bool
+ subRoutingRules string
subPath string
subJsonPath string
jsonEnabled bool
@@ -47,6 +48,7 @@ func NewSUBController(
subProfileUrl string,
subAnnounce string,
subEnableRouting bool,
+ subRoutingRules string,
) *SUBController {
sub := NewSubService(showInfo, rModel)
a := &SUBController{
@@ -55,6 +57,7 @@ func NewSUBController(
subProfileUrl: subProfileUrl,
subAnnounce: subAnnounce,
subEnableRouting: subEnableRouting,
+ subRoutingRules: subRoutingRules,
subPath: subPath,
subJsonPath: jsonPath,
jsonEnabled: jsonEnabled,
@@ -140,7 +143,7 @@ func (a *SUBController) subs(c *gin.Context) {
// Add headers
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.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, a.subProfileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules)
if a.subEncrypt {
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
@@ -159,7 +162,7 @@ func (a *SUBController) subJsons(c *gin.Context) {
c.String(400, "Error!")
} else {
// Add headers
- a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, a.subProfileUrl, a.subAnnounce, a.subEnableRouting)
+ a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, a.subProfileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules)
c.String(200, jsonSub)
}
@@ -175,6 +178,7 @@ func (a *SUBController) ApplyCommonHeaders(
profileUrl string,
profileAnnounce string,
profileEnableRouting bool,
+ profileRoutingRules string,
) {
c.Writer.Header().Set("Subscription-Userinfo", header)
c.Writer.Header().Set("Profile-Update-Interval", updateInterval)
@@ -183,4 +187,5 @@ func (a *SUBController) ApplyCommonHeaders(
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)
}
diff --git a/web/assets/js/model/setting.js b/web/assets/js/model/setting.js
index ac7a94ae..af80a63e 100644
--- a/web/assets/js/model/setting.js
+++ b/web/assets/js/model/setting.js
@@ -33,6 +33,7 @@ class AllSetting {
this.subProfileUrl = "";
this.subAnnounce = "";
this.subEnableRouting = true;
+ this.subRoutingRules = "";
this.subListen = "";
this.subPort = 2096;
this.subPath = "/sub/";
diff --git a/web/entity/entity.go b/web/entity/entity.go
index 25c8141c..40294925 100644
--- a/web/entity/entity.go
+++ b/web/entity/entity.go
@@ -61,6 +61,7 @@ type AllSetting struct {
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
SubPort int `json:"subPort" form:"subPort"` // Subscription server port
SubPath string `json:"subPath" form:"subPath"` // Base path for subscription URLs
diff --git a/web/html/settings/panel/subscription/general.html b/web/html/settings/panel/subscription/general.html
index 76fa4a03..5d83aa37 100644
--- a/web/html/settings/panel/subscription/general.html
+++ b/web/html/settings/panel/subscription/general.html
@@ -71,7 +71,7 @@
-
+ {{ i18n "pages.xray.basicTemplate"}}
{{ i18n "pages.settings.subTitle"}}
{{ i18n "pages.settings.subTitleDesc"}}
@@ -83,14 +83,14 @@
{{ i18n "pages.settings.subSupportUrl"}}
{{ i18n "pages.settings.subSupportUrlDesc"}}
-
+
{{ i18n "pages.settings.subProfileUrl"}}
{{ i18n "pages.settings.subProfileUrlDesc"}}
-
+
@@ -100,6 +100,7 @@
+ {{ i18n "pages.xray.advancedTemplate"}} (Happ)
{{ i18n "pages.settings.subEnableRouting"}}
{{ i18n "pages.settings.subEnableRoutingDesc"}}
@@ -107,6 +108,13 @@
+
+ {{ i18n "pages.settings.subRoutingRules"}}
+ {{ i18n "pages.settings.subRoutingRulesDesc"}}
+
+
+
+
diff --git a/web/service/setting.go b/web/service/setting.go
index d1586a5b..3fa37f44 100644
--- a/web/service/setting.go
+++ b/web/service/setting.go
@@ -57,6 +57,7 @@ var defaultValueMap = map[string]string{
"subProfileUrl": "",
"subAnnounce": "",
"subEnableRouting": "true",
+ "subRoutingRules": "",
"subListen": "",
"subPort": "2096",
"subPath": "/sub/",
@@ -479,6 +480,10 @@ 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) {
return s.getString("subListen")
}
diff --git a/web/translation/translate.ar_EG.toml b/web/translation/translate.ar_EG.toml
index ffcb690f..6d75d196 100644
--- a/web/translation/translate.ar_EG.toml
+++ b/web/translation/translate.ar_EG.toml
@@ -381,7 +381,9 @@
"subAnnounce" = "إعلان"
"subAnnounceDesc" = "نص الإعلان المعروض في عميل VPN"
"subEnableRouting" = "تفعيل التوجيه"
-"subEnableRoutingDesc" = "إعداد عام لتمكين التوجيه (Routing) في عميل VPN."
+"subEnableRoutingDesc" = "إعداد عام لتمكين التوجيه (Routing) في عميل VPN. (فقط لـ Happ)"
+"subRoutingRules" = "قواعد التوجيه"
+"subRoutingRulesDesc" = "قواعد التوجيه العامة لعميل VPN. (فقط لـ Happ)"
"subListen" = "IP الاستماع"
"subListenDesc" = "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)"
"subPort" = "بورت الاستماع"
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index 63f1a26d..244e6f2c 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -381,7 +381,9 @@
"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."
+"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"
"subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)"
"subPort" = "Listen Port"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index 6569a4b1..b0dde898 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -381,7 +381,9 @@
"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."
+"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"
"subListenDesc" = "Dejar en blanco por defecto para monitorear todas las IPs."
"subPort" = "Puerto de Suscripción"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 7324a11a..1eda5fb5 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -381,7 +381,9 @@
"subAnnounce" = "اعلان"
"subAnnounceDesc" = "متن اعلانی که در کلاینت VPN نمایش داده میشود"
"subEnableRouting" = "فعالسازی مسیریابی"
-"subEnableRoutingDesc" = "تنظیمات سراسری برای فعالسازی مسیریابی در کلاینت VPN."
+"subEnableRoutingDesc" = "تنظیمات سراسری برای فعالسازی مسیریابی در کلاینت VPN. (فقط برای Happ)"
+"subRoutingRules" = "قوانین مسیریابی"
+"subRoutingRulesDesc" = "قوانین مسیریابی سراسری برای کلاینت VPN. (فقط برای Happ)"
"subListen" = "آدرس آیپی"
"subListenDesc" = "آدرس آیپی برای سرویس سابسکریپشن. برای گوش دادن بهتمام آیپیها خالیبگذارید"
"subPort" = "پورت"
diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml
index 493bc73f..8804ef04 100644
--- a/web/translation/translate.id_ID.toml
+++ b/web/translation/translate.id_ID.toml
@@ -381,7 +381,9 @@
"subAnnounce" = "Pengumuman"
"subAnnounceDesc" = "Teks pengumuman yang ditampilkan di klien VPN"
"subEnableRouting" = "Aktifkan perutean"
-"subEnableRoutingDesc" = "Pengaturan global untuk mengaktifkan perutean (routing) di klien VPN."
+"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"
"subListenDesc" = "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)"
"subPort" = "Port Pendengar"
diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml
index 16a1b9eb..8bba24c0 100644
--- a/web/translation/translate.ja_JP.toml
+++ b/web/translation/translate.ja_JP.toml
@@ -381,7 +381,9 @@
"subAnnounce" = "お知らせ"
"subAnnounceDesc" = "VPNクライアントに表示されるお知らせのテキスト"
"subEnableRouting" = "ルーティングを有効化"
-"subEnableRoutingDesc" = "VPNクライアントでルーティングを有効にするためのグローバル設定。"
+"subEnableRoutingDesc" = "VPNクライアントでルーティングを有効にするためのグローバル設定。(Happのみ)"
+"subRoutingRules" = "ルーティングルール"
+"subRoutingRulesDesc" = "VPNクライアントのグローバルルーティングルール。(Happのみ)"
"subListen" = "監視IP"
"subListenDesc" = "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)"
"subPort" = "監視ポート"
diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml
index c8bccfce..1b173e6e 100644
--- a/web/translation/translate.pt_BR.toml
+++ b/web/translation/translate.pt_BR.toml
@@ -381,7 +381,9 @@
"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."
+"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"
"subListenDesc" = "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)"
"subPort" = "Porta de Escuta"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index e2fe9876..895734f5 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -381,7 +381,9 @@
"subAnnounce" = "Объявление"
"subAnnounceDesc" = "Текст объявления, отображаемый в VPN-клиенте"
"subEnableRouting" = "Включить маршрутизацию"
-"subEnableRoutingDesc" = "Глобальная настройка для включения маршрутизации в VPN-клиенте."
+"subEnableRoutingDesc" = "Глобальная настройка для включения маршрутизации в VPN-клиенте. (Только для Happ)"
+"subRoutingRules" = "Правила маршрутизации"
+"subRoutingRulesDesc" = "Глобальные правила маршрутизации для VPN-клиента. (Только для Happ)"
"subListen" = "Прослушивание IP"
"subListenDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса"
"subPort" = "Порт подписки"
diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml
index ab1db3ad..50639358 100644
--- a/web/translation/translate.tr_TR.toml
+++ b/web/translation/translate.tr_TR.toml
@@ -381,7 +381,9 @@
"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."
+"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"
"subListenDesc" = "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)"
"subPort" = "Dinleme Portu"
diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml
index 9512dbc1..54d45889 100644
--- a/web/translation/translate.uk_UA.toml
+++ b/web/translation/translate.uk_UA.toml
@@ -381,7 +381,9 @@
"subAnnounce" = "Оголошення"
"subAnnounceDesc" = "Текст оголошення, що відображається у VPN-клієнті"
"subEnableRouting" = "Увімкнути маршрутизацію"
-"subEnableRoutingDesc" = "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті."
+"subEnableRoutingDesc" = "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті. (Тільки для Happ)"
+"subRoutingRules" = "Правила маршрутизації"
+"subRoutingRulesDesc" = "Глобальні правила маршрутизації для VPN-клієнта. (Тільки для Happ)"
"subListen" = "Слухати IP"
"subListenDesc" = "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)"
"subPort" = "Слухати порт"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index a8fd280d..3fa63bb1 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -381,7 +381,9 @@
"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."
+"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"
"subListenDesc" = "Mặc định để trống để nghe tất cả các IP"
"subPort" = "Cổng gói đăng ký"
diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml
index 33d5c09a..d6b82b93 100644
--- a/web/translation/translate.zh_CN.toml
+++ b/web/translation/translate.zh_CN.toml
@@ -381,7 +381,9 @@
"subAnnounce" = "公告"
"subAnnounceDesc" = "VPN 客户端中显示的公告文本"
"subEnableRouting" = "启用路由"
-"subEnableRoutingDesc" = "在 VPN 客户端中启用路由的全局设置。"
+"subEnableRoutingDesc" = "在 VPN 客户端中启用路由的全局设置。(僅限 Happ)"
+"subRoutingRules" = "路由規則"
+"subRoutingRulesDesc" = "VPN 用戶端的全域路由規則。(僅限 Happ)"
"subListen" = "监听 IP"
"subListenDesc" = "订阅服务监听的 IP 地址(留空表示监听所有 IP)"
"subPort" = "监听端口"
diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml
index a566bf09..616f2322 100644
--- a/web/translation/translate.zh_TW.toml
+++ b/web/translation/translate.zh_TW.toml
@@ -381,7 +381,9 @@
"subAnnounce" = "公告"
"subAnnounceDesc" = "VPN 用戶端中顯示的公告文字"
"subEnableRouting" = "啟用路由"
-"subEnableRoutingDesc" = "在 VPN 用戶端中啟用路由的全域設定。"
+"subEnableRoutingDesc" = "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ)"
+"subRoutingRules" = "路由規則"
+"subRoutingRulesDesc" = "VPN 用戶端的全域路由規則。(僅限 Happ)"
"subListen" = "監聽 IP"
"subListenDesc" = "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)"
"subPort" = "監聽埠"