mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-15 09:35:00 +03:00
feat(happ): add routing editor, optional ad blocking, and LAN bypass preset (#6545)
* feat(happ): make ad blocking optional in routing presets Add an independent AdBlock toggle for Iran, China, and global presets, applied only when generating routing rules. Update the China preset to Bypass-CN and cover preset behavior and localized controls. * feat: add visual routing editor with JSON support and localization updates - Implemented a new modal for editing routing profiles with basic and advanced tabs. - Added functionality to load, parse, and generate routing profiles in JSON format. - Enhanced user experience with validation and error handling for JSON input. - Updated translations for Russian, Turkish, Ukrainian, Vietnamese, Chinese (Simplified and Traditional) to include new routing editor terms. - Created helper functions for managing routing profiles and generating deep links. - Added unit tests for routing editor functionalities and JSON handling. * feat: update routing editor to preserve null lists in profiles and enhance validation messages
This commit is contained in:
@@ -7,16 +7,11 @@ export function toBase64Utf8(str: string): string {
|
||||
);
|
||||
}
|
||||
|
||||
// Splits multiline or comma-separated string into clean unique token arrays.
|
||||
export function parseList(input: string): string[] {
|
||||
return input
|
||||
.split(/[\n,]+/)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
// Build standard Happ routing deeplink or special state for curated presets.
|
||||
export function buildHappPresetDeeplink(preset: string): string {
|
||||
export function buildHappPresetDeeplink(preset: string, includeAdblock = false): string {
|
||||
// Ad blocking is opt-in for each generated profile, independent of the base routing rules.
|
||||
const blockSites = includeAdblock ? ['geosite:category-ads-all'] : [];
|
||||
|
||||
switch (preset) {
|
||||
case 'off':
|
||||
return 'happ://routing/off';
|
||||
@@ -27,9 +22,20 @@ export function buildHappPresetDeeplink(preset: string): string {
|
||||
JSON.stringify({
|
||||
Name: 'Iran Bypass',
|
||||
GlobalProxy: 'true',
|
||||
DirectSites: ['domain:ir', 'regexp:.*\\.ir$'],
|
||||
DirectIp: ['geoip:ir', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'],
|
||||
BlockSites: ['geosite:category-ads-all'],
|
||||
RouteOrder: 'block-proxy-direct',
|
||||
DirectSites: ['geosite:private', 'domain:ir', 'geosite:category-ir'],
|
||||
DirectIp: [
|
||||
'geoip:ir',
|
||||
'geoip:private',
|
||||
'127.0.0.0/8',
|
||||
'10.0.0.0/8',
|
||||
'172.16.0.0/12',
|
||||
'192.168.0.0/16',
|
||||
'169.254.0.0/16',
|
||||
'224.0.0.0/4',
|
||||
'255.255.255.255',
|
||||
],
|
||||
BlockSites: blockSites,
|
||||
BlockIp: [],
|
||||
ProxySites: [],
|
||||
ProxyIp: [],
|
||||
@@ -42,32 +48,38 @@ export function buildHappPresetDeeplink(preset: string): string {
|
||||
'happ://routing/onadd/' +
|
||||
toBase64Utf8(
|
||||
JSON.stringify({
|
||||
Name: 'China Direct',
|
||||
Name: 'Bypass-CN',
|
||||
GlobalProxy: 'true',
|
||||
DirectSites: ['geosite:cn', 'geosite:geolocation-cn'],
|
||||
DirectIp: ['geoip:cn', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'],
|
||||
BlockSites: ['geosite:category-ads-all'],
|
||||
BlockIp: [],
|
||||
RouteOrder: 'block-proxy-direct',
|
||||
RemoteDNSType: 'DoH',
|
||||
RemoteDNSDomain: 'https://cloudflare-dns.com/dns-query',
|
||||
RemoteDNSIP: '1.1.1.1',
|
||||
DomesticDNSType: 'DoH',
|
||||
DomesticDNSDomain: 'https://dns.alidns.com/dns-query',
|
||||
DomesticDNSIP: '223.5.5.5',
|
||||
DnsHosts: {
|
||||
'cloudflare-dns.com': '1.1.1.1',
|
||||
'dns.alidns.com': '223.5.5.5',
|
||||
},
|
||||
DirectSites: ['geosite:private', 'geosite:cn', 'geosite:geolocation-cn'],
|
||||
DirectIp: [
|
||||
'geoip:cn',
|
||||
'geoip:private',
|
||||
'127.0.0.0/8',
|
||||
'10.0.0.0/8',
|
||||
'172.16.0.0/12',
|
||||
'192.168.0.0/16',
|
||||
'169.254.0.0/16',
|
||||
'224.0.0.0/4',
|
||||
'255.255.255.255',
|
||||
],
|
||||
ProxySites: [],
|
||||
ProxyIp: [],
|
||||
DomainStrategy: 'IPIfNonMatch',
|
||||
}),
|
||||
)
|
||||
);
|
||||
case 'adblock':
|
||||
return (
|
||||
'happ://routing/onadd/' +
|
||||
toBase64Utf8(
|
||||
JSON.stringify({
|
||||
Name: 'AdBlock',
|
||||
GlobalProxy: 'true',
|
||||
DirectSites: [],
|
||||
DirectIp: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'],
|
||||
BlockSites: ['geosite:category-ads-all'],
|
||||
BlockSites: blockSites,
|
||||
BlockIp: [],
|
||||
ProxySites: [],
|
||||
ProxyIp: [],
|
||||
DomainStrategy: 'IPIfNonMatch',
|
||||
FakeDNS: 'false',
|
||||
UseChunkFiles: 'true',
|
||||
}),
|
||||
)
|
||||
);
|
||||
@@ -80,7 +92,25 @@ export function buildHappPresetDeeplink(preset: string): string {
|
||||
GlobalProxy: 'true',
|
||||
DirectSites: [],
|
||||
DirectIp: [],
|
||||
BlockSites: [],
|
||||
BlockSites: blockSites,
|
||||
BlockIp: [],
|
||||
ProxySites: [],
|
||||
ProxyIp: [],
|
||||
DomainStrategy: 'AsIs',
|
||||
}),
|
||||
)
|
||||
);
|
||||
// LAN bypass has its own identity so applying it does not overwrite the Global profile.
|
||||
case 'lan-bypass':
|
||||
return (
|
||||
'happ://routing/onadd/' +
|
||||
toBase64Utf8(
|
||||
JSON.stringify({
|
||||
Name: 'Global Bypass Local Network',
|
||||
GlobalProxy: 'true',
|
||||
DirectSites: ['geosite:private'],
|
||||
DirectIp: ['geoip:private'],
|
||||
BlockSites: blockSites,
|
||||
BlockIp: [],
|
||||
ProxySites: [],
|
||||
ProxyIp: [],
|
||||
|
||||
Reference in New Issue
Block a user