feat(sub): add option to hide server settings in subscription (happ) (#5433)

* feat(settings): add option to hide server settings in subscription

* chore: regenerate codegen and add translations for subHideSettings

- Update frontend/src/generated/{types,schemas,zod,examples}.ts to include
  subHideSettings (bool) in AllSetting and AllSettingView
- Add subHideSettings / subHideSettingsDesc translation keys to all 11
  remaining locales: ar-EG, fa-IR, es-ES, id-ID, ja-JP, pt-BR, uk-UA,
  tr-TR, zh-TW, zh-CN, vi-VN

Co-authored-by: IgorKha <IgorKha@users.noreply.github.com>
Co-authored-by: Sanaei <MHSanaei@users.noreply.github.com>

* fix(sub): add subHideSettings default to settings map

Every other sub* setting has an entry in defaultValueMap; subHideSettings was missing, so GetSubHideSettings hit the 'key not in defaultValueMap' error path on a fresh install (only masked by the false fallback in sub.go). Add the default for consistency.
This commit is contained in:
IgorKha
2026-06-21 03:32:56 +05:00
committed by GitHub
parent 1a4aef3353
commit ce1d348ece
24 changed files with 69 additions and 4 deletions
+2
View File
@@ -50,6 +50,7 @@ export const EXAMPLES: Record<string, unknown> = {
"subEnable": false,
"subEnableRouting": false,
"subEncrypt": false,
"subHideSettings": false,
"subJsonEnable": false,
"subJsonFinalMask": "",
"subJsonMux": "",
@@ -147,6 +148,7 @@ export const EXAMPLES: Record<string, unknown> = {
"subEnable": false,
"subEnableRouting": false,
"subEncrypt": false,
"subHideSettings": false,
"subJsonEnable": false,
"subJsonFinalMask": "",
"subJsonMux": "",
+10
View File
@@ -196,6 +196,10 @@ export const SCHEMAS: Record<string, unknown> = {
"description": "Encrypt subscription responses",
"type": "boolean"
},
"subHideSettings": {
"description": "Hide server settings in happ subscription (Only for Happ)",
"type": "boolean"
},
"subJsonEnable": {
"description": "Enable JSON subscription endpoint",
"type": "boolean"
@@ -413,6 +417,7 @@ export const SCHEMAS: Record<string, unknown> = {
"subEnable",
"subEnableRouting",
"subEncrypt",
"subHideSettings",
"subJsonEnable",
"subJsonFinalMask",
"subJsonMux",
@@ -672,6 +677,10 @@ export const SCHEMAS: Record<string, unknown> = {
"description": "Encrypt subscription responses",
"type": "boolean"
},
"subHideSettings": {
"description": "Hide server settings in happ subscription (Only for Happ)",
"type": "boolean"
},
"subJsonEnable": {
"description": "Enable JSON subscription endpoint",
"type": "boolean"
@@ -896,6 +905,7 @@ export const SCHEMAS: Record<string, unknown> = {
"subEnable",
"subEnableRouting",
"subEncrypt",
"subHideSettings",
"subJsonEnable",
"subJsonFinalMask",
"subJsonMux",
+2
View File
@@ -56,6 +56,7 @@ export interface AllSetting {
subEnable: boolean;
subEnableRouting: boolean;
subEncrypt: boolean;
subHideSettings: boolean;
subJsonEnable: boolean;
subJsonFinalMask: string;
subJsonMux: string;
@@ -154,6 +155,7 @@ export interface AllSettingView {
subEnable: boolean;
subEnableRouting: boolean;
subEncrypt: boolean;
subHideSettings: boolean;
subJsonEnable: boolean;
subJsonFinalMask: string;
subJsonMux: string;
+2
View File
@@ -68,6 +68,7 @@ export const AllSettingSchema = z.object({
subEnable: z.boolean(),
subEnableRouting: z.boolean(),
subEncrypt: z.boolean(),
subHideSettings: z.boolean(),
subJsonEnable: z.boolean(),
subJsonFinalMask: z.string(),
subJsonMux: z.string(),
@@ -167,6 +168,7 @@ export const AllSettingViewSchema = z.object({
subEnable: z.boolean(),
subEnableRouting: z.boolean(),
subEncrypt: z.boolean(),
subHideSettings: z.boolean(),
subJsonEnable: z.boolean(),
subJsonFinalMask: z.string(),
subJsonMux: z.string(),
+1
View File
@@ -57,6 +57,7 @@ export class AllSetting {
subJsonRules = '';
subJsonFinalMask = '';
subThemeDir = '';
subHideSettings = false;
timeLocation = 'Local';
@@ -153,6 +153,9 @@ export default function SubscriptionGeneralTab({ allSetting, updateSetting }: Su
<Input.TextArea value={allSetting.subRoutingRules} placeholder="happ://routing/add/..."
onChange={(e) => updateSetting({ subRoutingRules: e.target.value })} />
</SettingListItem>
<SettingListItem paddings="small" title={t('pages.settings.subHideSettings')} description={t('pages.settings.subHideSettingsDesc')}>
<Switch checked={allSetting.subHideSettings} onChange={(v) => updateSetting({ subHideSettings: v })} />
</SettingListItem>
</>
),
},
+1
View File
@@ -61,6 +61,7 @@ export const AllSettingSchema = z.object({
subJsonMux: z.string().optional(),
subJsonRules: z.string().optional(),
subJsonFinalMask: z.string().optional(),
subHideSettings: z.boolean().optional(),
timeLocation: z.string().optional(),
ldapEnable: z.boolean().optional(),
ldapHost: z.string().optional(),
+10 -3
View File
@@ -48,6 +48,7 @@ type SUBController struct {
subAnnounce string
subEnableRouting bool
subRoutingRules string
subHideSettings bool
subPath string
subJsonPath string
subClashPath string
@@ -87,6 +88,7 @@ func NewSUBController(
subAnnounce string,
subEnableRouting bool,
subRoutingRules string,
subHideSettings bool,
) *SUBController {
sub := NewSubService(remarkTemplate)
a := &SUBController{
@@ -96,6 +98,7 @@ func NewSUBController(
subAnnounce: subAnnounce,
subEnableRouting: subEnableRouting,
subRoutingRules: subRoutingRules,
subHideSettings: subHideSettings,
subPath: subPath,
subJsonPath: jsonPath,
subClashPath: clashPath,
@@ -178,7 +181,7 @@ func (a *SUBController) subs(c *gin.Context) {
if profileUrl == "" {
profileUrl = fmt.Sprintf("%s://%s%s", scheme, hostWithPort, c.Request.RequestURI)
}
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, profileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules)
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, profileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
if a.subEncrypt {
c.String(200, base64.StdEncoding.EncodeToString([]byte(result.String())))
@@ -357,7 +360,7 @@ func (a *SUBController) subJsons(c *gin.Context) {
if profileUrl == "" {
profileUrl = fmt.Sprintf("%s://%s%s", scheme, hostWithPort, c.Request.RequestURI)
}
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, profileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules)
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, profileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
c.String(200, jsonSub)
}
@@ -374,7 +377,7 @@ func (a *SUBController) subClashs(c *gin.Context) {
if profileUrl == "" {
profileUrl = fmt.Sprintf("%s://%s%s", scheme, hostWithPort, c.Request.RequestURI)
}
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, profileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules)
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, profileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules, a.subHideSettings)
if a.subTitle != "" {
// Clash clients commonly use Content-Disposition to choose the imported profile name.
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename*=UTF-8''%s`, url.PathEscape(a.subTitle)))
@@ -394,6 +397,7 @@ func (a *SUBController) ApplyCommonHeaders(
profileAnnounce string,
profileEnableRouting bool,
profileRoutingRules string,
profileHideSettings bool,
) {
c.Writer.Header().Set("Subscription-Userinfo", header)
c.Writer.Header().Set("Profile-Update-Interval", updateInterval)
@@ -417,4 +421,7 @@ func (a *SUBController) ApplyCommonHeaders(
if profileRoutingRules != "" {
c.Writer.Header().Set("Routing", profileRoutingRules)
}
if profileHideSettings {
c.Writer.Header().Set("Hide-Settings", "1")
}
}
+6 -1
View File
@@ -170,6 +170,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
SubRoutingRules = ""
}
SubHideSettings, err := s.settingService.GetSubHideSettings()
if err != nil {
SubHideSettings = false
}
// set per-request localizer from headers/cookies
engine.Use(locale.LocalizerMiddleware())
@@ -227,7 +232,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
s.sub = NewSUBController(
g, LinksPath, JsonPath, ClashPath, subJsonEnable, subClashEnable, Encrypt, RemarkTemplate, SubUpdates,
SubJsonMux, SubJsonRules, SubJsonFinalMask, SubClashEnableRouting, SubClashRules, SubTitle, SubSupportUrl,
SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules)
SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules, SubHideSettings)
return engine, nil
}
+1
View File
@@ -98,6 +98,7 @@ type AllSetting struct {
SubJsonRules string `json:"subJsonRules" form:"subJsonRules"`
SubJsonFinalMask string `json:"subJsonFinalMask" form:"subJsonFinalMask"` // JSON subscription global finalmask (tcp/udp masks + quicParams)
SubThemeDir string `json:"subThemeDir" form:"subThemeDir"` // Absolute path to a folder containing a custom subscription page template
SubHideSettings bool `json:"subHideSettings" form:"subHideSettings"` // Hide server settings in happ subscription (Only for Happ)
// LDAP settings
LdapEnable bool `json:"ldapEnable" form:"ldapEnable"`
+5
View File
@@ -74,6 +74,7 @@ var defaultValueMap = map[string]string{
"subAnnounce": "",
"subEnableRouting": "false",
"subRoutingRules": "",
"subHideSettings": "false",
"subListen": "",
"subPort": "2096",
"subPath": "/sub/",
@@ -692,6 +693,10 @@ func (s *SettingService) GetSubRoutingRules() (string, error) {
return s.getString("subRoutingRules")
}
func (s *SettingService) GetSubHideSettings() (bool, error) {
return s.getBool("subHideSettings")
}
func (s *SettingService) GetSubListen() (string, error) {
return s.getString("subListen")
}
+2
View File
@@ -1090,6 +1090,8 @@
"subEnableRoutingDesc": "إعداد عام لتمكين التوجيه (Routing) في عميل VPN. (فقط لـ Happ)",
"subRoutingRules": "قواعد التوجيه",
"subRoutingRulesDesc": "قواعد التوجيه العامة لعميل VPN. (فقط لـ Happ)",
"subHideSettings": "إخفاء إعدادات الخادم",
"subHideSettingsDesc": "إخفاء إمكانية عرض وتعديل إعدادات الخادم في عميل VPN. (فقط لـ Happ)",
"subClashEnableRouting": "تفعيل التوجيه",
"subClashEnableRoutingDesc": "تضمين قواعد توجيه Clash/Mihomo العامة في اشتراكات YAML المُنشأة.",
"subClashRoutingRules": "قواعد التوجيه العامة",
+2
View File
@@ -1200,6 +1200,8 @@
"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)",
"subHideSettings": "Hide server settings",
"subHideSettingsDesc": "Hide the ability to view and edit server configurations in the VPN client. (Only for Happ)",
"subClashEnableRouting": "Enable routing",
"subClashEnableRoutingDesc": "Include global Clash/Mihomo routing rules in generated YAML subscriptions.",
"subClashRoutingRules": "Global routing rules",
+2
View File
@@ -1090,6 +1090,8 @@
"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)",
"subHideSettings": "Ocultar configuración del servidor",
"subHideSettingsDesc": "Ocultar la posibilidad de ver y editar las configuraciones del servidor en el cliente VPN. (Solo para Happ)",
"subClashEnableRouting": "Habilitar enrutamiento",
"subClashEnableRoutingDesc": "Incluir reglas globales de enrutamiento Clash/Mihomo en las suscripciones YAML generadas.",
"subClashRoutingRules": "Reglas globales de enrutamiento",
+2
View File
@@ -1092,6 +1092,8 @@
"subEnableRoutingDesc": "تنظیمات سراسری برای فعال‌سازی مسیریابی در کلاینت VPN. (فقط برای Happ)",
"subRoutingRules": "قوانین مسیریابی",
"subRoutingRulesDesc": "قوانین مسیریابی سراسری برای کلاینت VPN. (فقط برای Happ)",
"subHideSettings": "پنهان کردن تنظیمات سرور",
"subHideSettingsDesc": "پنهان کردن توانایی مشاهده و ویرایش پیکربندی سرور در کلاینت VPN. (فقط برای Happ)",
"subClashEnableRouting": "فعال‌سازی مسیریابی",
"subClashEnableRoutingDesc": "قوانین مسیریابی سراسری Clash/Mihomo را در اشتراک‌های YAML تولیدشده وارد کن.",
"subClashRoutingRules": "قوانین مسیریابی سراسری",
+2
View File
@@ -1090,6 +1090,8 @@
"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)",
"subHideSettings": "Sembunyikan pengaturan server",
"subHideSettingsDesc": "Menyembunyikan kemampuan untuk melihat dan mengedit konfigurasi server di klien VPN. (Hanya untuk Happ)",
"subClashEnableRouting": "Aktifkan routing",
"subClashEnableRoutingDesc": "Sertakan aturan routing global Clash/Mihomo dalam langganan YAML yang dibuat.",
"subClashRoutingRules": "Aturan routing global",
+2
View File
@@ -1090,6 +1090,8 @@
"subEnableRoutingDesc": "VPNクライアントでルーティングを有効にするためのグローバル設定。(Happのみ)",
"subRoutingRules": "ルーティングルール",
"subRoutingRulesDesc": "VPNクライアントのグローバルルーティングルール。(Happのみ)",
"subHideSettings": "サーバー設定を非表示",
"subHideSettingsDesc": "VPNクライアントでサーバー設定の表示・編集機能を非表示にします。(Happのみ)",
"subClashEnableRouting": "ルーティングを有効化",
"subClashEnableRoutingDesc": "生成されたYAMLサブスクリプションにClash/Mihomoのグローバルルーティングルールを含めます。",
"subClashRoutingRules": "グローバルルーティングルール",
+2
View File
@@ -1090,6 +1090,8 @@
"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)",
"subHideSettings": "Ocultar configurações do servidor",
"subHideSettingsDesc": "Ocultar a capacidade de visualizar e editar as configurações do servidor no cliente VPN. (Apenas para Happ)",
"subClashEnableRouting": "Ativar roteamento",
"subClashEnableRoutingDesc": "Incluir regras globais de roteamento Clash/Mihomo nas assinaturas YAML geradas.",
"subClashRoutingRules": "Regras globais de roteamento",
+2
View File
@@ -1090,6 +1090,8 @@
"subEnableRoutingDesc": "Глобальная настройка для включения маршрутизации в VPN-клиенте. (Только для Happ)",
"subRoutingRules": "Правила маршрутизации",
"subRoutingRulesDesc": "Глобальные правила маршрутизации для VPN-клиента. (Только для Happ)",
"subHideSettings": "Скрыть настройки сервера",
"subHideSettingsDesc": "Скрыть возможность просмотра и редактирования конфигурации сервера в VPN-клиенте. (Только для Happ)",
"subClashEnableRouting": "Включить маршрутизацию",
"subClashEnableRoutingDesc": "Добавлять глобальные правила маршрутизации Clash/Mihomo в сгенерированные YAML-подписки.",
"subClashRoutingRules": "Глобальные правила маршрутизации",
+2
View File
@@ -1090,6 +1090,8 @@
"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)",
"subHideSettings": "Sunucu ayarlarını gizle",
"subHideSettingsDesc": "VPN istemcisinde sunucu yapılandırmalarını görüntüleme ve düzenleme özelliğini gizleyin. (Yalnızca Happ için)",
"subClashEnableRouting": "Yönlendirmeyi Etkinleştir",
"subClashEnableRoutingDesc": "Oluşturulan YAML aboneliklerine genel Clash/Mihomo yönlendirme kurallarını ekler.",
"subClashRoutingRules": "Genel Yönlendirme Kuralları",
+2
View File
@@ -1090,6 +1090,8 @@
"subEnableRoutingDesc": "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті. (Тільки для Happ)",
"subRoutingRules": "Правила маршрутизації",
"subRoutingRulesDesc": "Глобальні правила маршрутизації для VPN-клієнта. (Тільки для Happ)",
"subHideSettings": "Приховати налаштування сервера",
"subHideSettingsDesc": "Приховати можливість перегляду та редагування конфігурації сервера у VPN-клієнті. (Тільки для Happ)",
"subClashEnableRouting": "Увімкнути маршрутизацію",
"subClashEnableRoutingDesc": "Додавати глобальні правила маршрутизації Clash/Mihomo до згенерованих YAML-підписок.",
"subClashRoutingRules": "Глобальні правила маршрутизації",
+2
View File
@@ -1090,6 +1090,8 @@
"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)",
"subHideSettings": "Ẩn cài đặt máy chủ",
"subHideSettingsDesc": "Ẩn khả năng xem và chỉnh sửa cấu hình máy chủ trong ứng dụng khách VPN. (Chỉ dành cho Happ)",
"subClashEnableRouting": "Bật định tuyến",
"subClashEnableRoutingDesc": "Bao gồm quy tắc định tuyến Clash/Mihomo toàn cầu trong các đăng ký YAML được tạo.",
"subClashRoutingRules": "Quy tắc định tuyến toàn cầu",
+2
View File
@@ -1090,6 +1090,8 @@
"subEnableRoutingDesc": "在 VPN 客户端中启用路由的全局设置。(僅限 Happ)",
"subRoutingRules": "路由規則",
"subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ)",
"subHideSettings": "隐藏服务器设置",
"subHideSettingsDesc": "在 VPN 客户端中隐藏查看和编辑服务器配置的功能。(僅限 Happ)",
"subClashEnableRouting": "启用路由",
"subClashEnableRoutingDesc": "在生成的 YAML 订阅中包含 Clash/Mihomo 全局路由规则。",
"subClashRoutingRules": "全局路由规则",
+2
View File
@@ -1090,6 +1090,8 @@
"subEnableRoutingDesc": "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ)",
"subRoutingRules": "路由規則",
"subRoutingRulesDesc": "VPN 用戶端的全域路由規則。(僅限 Happ)",
"subHideSettings": "隱藏伺服器設定",
"subHideSettingsDesc": "在 VPN 用戶端中隱藏查看和編輯伺服器配置的功能。(僅限 Happ)",
"subClashEnableRouting": "啟用路由",
"subClashEnableRoutingDesc": "在產生的 YAML 訂閱中包含 Clash/Mihomo 全域路由規則。",
"subClashRoutingRules": "全域路由規則",