feat(sub): add Incy app-management parameters (#6650)

* feat(sub): add Incy app-management parameters

The panel already pushes a set of Happ headers, but INCY documents its own
lowercase header names and its own value domains, so a Happ-shaped payload gets
ignored by the client (per-app mode is bypass|proxy, not on|bypass, and
per-app-proxy-enable has no Happ counterpart at all). Add a sibling Incy path
that emits exactly the documented headers.

Covered, per https://docs.incy.cc/en/app-management/:
- profile-description, sort-order, support-email, announce-url, premium-url
- banner text/button/URL and the two hex colours
- hide-url, hide-check, no-limit-enabled
- per-app split tunnelling (enable/mode/list)
- TCP fragmentation (enable/length/interval/packets)
- UDP noise packets (enable/type/packet/delay)
- DoH pre-resolution (enable/domain/IP)

Each string setting is tri-state: an empty value omits the header, so an
untouched panel never overrides the subscriber's own choice in the app. Values
are validated against the documented domains and dropped when they do not
match, and non-ASCII text is base64-wrapped the way the docs require for
Cyrillic. INCY identifies itself as INCY/<version>/<platform>, which gates the
headers behind the same auto-detect switch the Happ path uses.

Headers the panel already emits for every client (Profile-Title, Support-Url,
Profile-Web-Page-Url, Announce, Profile-Update-Interval, Subscription-Userinfo)
and Incy's routing line are left as they are.

The Premium API (theme, defaultPingProtocol, fallbackHosts, ...) is a separate
encrypted endpoint and stays out of scope here.

* fix(sub): keep Incy per-app list entries separate on the wire

The Incy settings textarea takes one package per line, as Incy documents for
per-app-proxy-list, but the header path ran the value through
sanitizeHeaderValue, which deletes CR/LF. "com.google.chrome\norg.telegram.messenger"
reached the client as the single bogus package
"com.google.chromeorg.telegram.messenger", so per-app split tunnelling silently
matched no app. Join comma- or line-separated entries as CSV instead.

Also drop three tests that could not fail: TestIncyExcludesHappOnlyHeaders
(ApplyIncyHeaders has no path that emits Happ headers, and the non-Happ UA
gate is already pinned by TestApplyHappHeaders_Gating) and two UI tests that
only asserted updateSetting received the key the JSX passes it.

---------

Co-authored-by: DIMFLIX <dimflix@users.noreply.github.com>
Co-authored-by: MHSanaei <ho3ein.sanaei@gmail.com>
This commit is contained in:
DIMFLIX
2026-09-27 02:06:17 +03:00
committed by GitHub
parent bd9ccde1f4
commit 71e38367c1
30 changed files with 2994 additions and 37 deletions
+70 -1
View File
@@ -1670,7 +1670,76 @@
"discordNotifyTimeDesc": "Discord botunun periyodik raporları gönderme sıklığı. Hazır bir aralık seçin veya bir crontab ifadesi girmek için Özel'i seçin.",
"discordNotifyBackup": "Veritabanı Yedeği",
"discordNotifyBackupDesc": "Bir rapor ile birlikte veritabanı yedek dosyasını gönderir.",
"discordEventBusNotifyDesc": "Hangi olayların Discord bildirimi tetikleyeceğini seçin"
"discordEventBusNotifyDesc": "Hangi olayların Discord bildirimi tetikleyeceğini seçin",
"subIncyAppAutoDetect": "Incy başlık otomatik algılama",
"subIncyAppAutoDetectDesc": "İstemci User-Agent değeri INCY ise Incy başlıklarını gönderir.",
"subIncyNotSet": "Ayarlanmadı",
"subIncyOn": "Açık",
"subIncyOff": "Kapalı",
"subIncyGroupApp": "Uygulama",
"subIncyProfileDescription": "Profil açıklaması",
"subIncyProfileDescriptionDesc": "Abonelik için ayrı açıklama. ASCII dışı metin base64 olarak gönderilir.",
"subIncySortOrder": "Sunucu sıralaması",
"subIncySortOrderDesc": "iOS/Android'de bu abonelik için, masaüstünde genel.",
"subIncySortNone": "Abonelik sırası",
"subIncySortPing": "Ping'e göre",
"subIncySortName": "İsme göre",
"subIncySupportEmail": "Destek e-postası",
"subIncySupportEmailDesc": "Abonelik kartına Email düğmesi ekler.",
"subIncyAnnounceUrl": "Duyuru bağlantısı",
"subIncyAnnounceUrlDesc": "Ana ekrandaki duyurunun hedef bağlantısı.",
"subIncyPremiumUrl": "Premium bağlantısı",
"subIncyPremiumUrlDesc": "Karttaki Premium düğmesi; banner düğmesinde de önceliklidir.",
"subIncyGroupBanners": "Bannerlar",
"subIncyBannerText": "Banner metni",
"subIncyBannerTextDesc": "Banner metnini geçersiz kılar. Yalnızca premium sağlayıcılar.",
"subIncyBannerButtonText": "Banner düğme metni",
"subIncyBannerButtonTextDesc": "Banner düğme etiketi. Boş bırakılırsa düğme gizlenir.",
"subIncyBannerButtonUrl": "Banner düğme bağlantısı",
"subIncyBannerButtonUrlDesc": "Banner düğmesinin URL'si.",
"subIncyBannerBgColor": "Banner arka planı",
"subIncyBannerBgColorDesc": "Hex renk, örn. #E53E3E.",
"subIncyBannerButtonColor": "Banner düğme rengi",
"subIncyBannerButtonColorDesc": "Hex renk, örn. #38A169.",
"subIncyGroupPrivacy": "Gizlilik",
"subIncyHideUrl": "Abonelik URL'sini gizle",
"subIncyHideUrlDesc": "URL'yi paylaşma, kopyalama, QR ve yedeklerden gizler.",
"subIncyHideCheck": "Check düğmesini gizle",
"subIncyHideCheckDesc": "Ana ekrandan bağlantı kontrolü düğmesini kaldırır.",
"subIncyNoLimit": "No-Limit modu (iOS)",
"subIncyNoLimitDesc": "Tünelin iOS 50 MB sınırında kalması için az bellek kullanan mod.",
"subIncyPerAppEnable": "Per-app modu (Android)",
"subIncyPerAppEnableDesc": "Uygulama bazlı split tüneli açar. Kapalı, kullanıcı seçimini geçersiz kılar.",
"subIncyPerAppMode": "Per-app mod tipi",
"subIncyPerAppModeDesc": "proxy: yalnızca listelenen uygulamalar VPN kullanır; bypass tersi.",
"subIncyPerAppModeProxy": "Yalnızca listelenenler",
"subIncyPerAppModeBypass": "Listelenenler atlar",
"subIncyPerAppList": "Uygulama listesi",
"subIncyPerAppListDesc": "Paket adları virgül veya satırla ayrılır, ya da bir metin dosyası URL'si.",
"subIncyGroupNetwork": "Ağ",
"subIncyFragmentationEnable": "Parçalama",
"subIncyFragmentationEnableDesc": "TCP parçalamayı açar ve genel ayarı geçersiz kılar.",
"subIncyFragmentLength": "Parça uzunluğu",
"subIncyFragmentLengthDesc": "Bayt aralığı, örn. 10-30.",
"subIncyFragmentInterval": "Parça aralığı",
"subIncyFragmentIntervalDesc": "Milisaniye aralığı, örn. 20-40.",
"subIncyFragmentPackets": "Parça paketleri",
"subIncyFragmentPacketsDesc": "Hangi TCP paketleri parçalanacak: tlshello, 1-3, 1 veya all.",
"subIncyNoisesEnable": "Gürültü paketleri",
"subIncyNoisesEnableDesc": "VPN el sıkışmasından önce rastgele UDP gürültüsü gönderir.",
"subIncyNoisesType": "Gürültü tipi",
"subIncyNoisesTypeDesc": "rand, str veya hex.",
"subIncyNoisesPacket": "Gürültü paket içeriği",
"subIncyNoisesPacketDesc": "İçerik; rand için uzunluk aralığı, örn. 10-20.",
"subIncyNoisesDelay": "Gürültü gecikmesi",
"subIncyNoisesDelayDesc": "Milisaniye cinsinden gecikme aralığı, örn. 10-50.",
"subIncyResolveEnable": "Sunucu adresini çöz (DoH)",
"subIncyResolveEnableDesc": "Bağlanmadan önce sunucu adresini DoH ile çözer.",
"subIncyResolveDnsDomain": "DoH uç noktası",
"subIncyResolveDnsDomainDesc": "DoH sunucu URL'si, genelde /dns-query ile biter.",
"subIncyResolveDnsIp": "DoH sunucu IP'si",
"subIncyResolveDnsIpDesc": "Bootstrap için DoH sunucu IP'si.",
"subIncyGroupRouting": "Yönlendirme"
},
"xray": {
"save": "Kaydet",