diff --git a/src/SingboxConfigBuilder.js b/src/SingboxConfigBuilder.js index a4f2ce55..778a7ced 100644 --- a/src/SingboxConfigBuilder.js +++ b/src/SingboxConfigBuilder.js @@ -1,104 +1,118 @@ -import { SING_BOX_CONFIG, generateRuleSets, generateRules, getOutbounds, PREDEFINED_RULE_SETS} from './config.js'; -import { BaseConfigBuilder } from './BaseConfigBuilder.js'; -import { DeepCopy } from './utils.js'; - -export class ConfigBuilder extends BaseConfigBuilder { - constructor(inputString, selectedRules, customRules, pin, baseConfig) { - if (baseConfig === undefined) { - baseConfig = SING_BOX_CONFIG - } - super(inputString, baseConfig); - this.selectedRules = selectedRules; - this.customRules = customRules; - this.pin = pin; - } - - addCustomItems(customItems) { - const validItems = customItems.filter(item => item != null); - this.config.outbounds.push(...validItems); - } - - addSelectors() { - let outbounds; - if (typeof this.selectedRules === 'string' && PREDEFINED_RULE_SETS[this.selectedRules]) { - outbounds = getOutbounds(PREDEFINED_RULE_SETS[this.selectedRules]); - } else if(this.selectedRules && Object.keys(this.selectedRules).length > 0) { - outbounds = getOutbounds(this.selectedRules); - } else { - outbounds = getOutbounds(PREDEFINED_RULE_SETS.minimal); - } - - const proxyList = this.config.outbounds.filter(outbound => outbound?.server != undefined).map(outbound => outbound.tag); - - this.config.outbounds.unshift({ - type: "urltest", - tag: "⚡ 自动选择", - outbounds: DeepCopy(proxyList), - }); - - proxyList.unshift('DIRECT', 'REJECT', '⚡ 自动选择'); - outbounds.unshift('🚀 节点选择','GLOBAL'); - - outbounds.forEach(outbound => { - if (outbound !== '🚀 节点选择') { - this.config.outbounds.push({ - type: "selector", - tag: outbound, - outbounds: ['🚀 节点选择', ...proxyList] - }); - } else { - this.config.outbounds.unshift({ - type: "selector", - tag: outbound, - outbounds: proxyList - }); - } - }); - - if (Array.isArray(this.customRules)) { - this.customRules.forEach(rule => { - this.config.outbounds.push({ - type: "selector", - tag: rule.name, - outbounds: ['🚀 节点选择', ...proxyList] - }); - }); - } - - this.config.outbounds.push({ - type: "selector", - tag: "🐟 漏网之鱼", - outbounds: ['🚀 节点选择', ...proxyList] - }); - } - - formatConfig() { - const rules = generateRules(this.selectedRules, this.customRules, this.pin); - const { site_rule_sets, ip_rule_sets } = generateRuleSets(this.selectedRules,this.customRules); - - this.config.route.rule_set = [...site_rule_sets, ...ip_rule_sets]; - - this.config.route.rules = rules.map(rule => ({ - rule_set: [ - ...(rule.site_rules.length > 0 && rule.site_rules[0] !== '' ? rule.site_rules : []), - ...(rule.ip_rules.filter(ip => ip.trim() !== '').map(ip => `${ip}-ip`)) - ], - domain_suffix: rule.domain_suffix, - domain_keyword: rule.domain_keyword, - ip_cidr: rule.ip_cidr, - protocol: rule.protocol, - outbound: rule.outbound - })); - // Add any default rules that should always be present - this.config.route.rules.unshift( - { protocol: 'dns', outbound: 'dns-out' }, - { clash_mode: 'direct', outbound: 'DIRECT' }, - { clash_mode: 'global', outbound: 'GLOBAL' } - ); - - this.config.route.auto_detect_interface = true; - this.config.route.final = '🐟 漏网之鱼'; - - return this.config; - } -} \ No newline at end of file +import { SING_BOX_CONFIG, generateRuleSets, generateRules, getOutbounds, PREDEFINED_RULE_SETS} from './config.js'; +import { BaseConfigBuilder } from './BaseConfigBuilder.js'; +import { DeepCopy } from './utils.js'; + +export class ConfigBuilder extends BaseConfigBuilder { + constructor(inputString, selectedRules, customRules, pin, baseConfig) { + if (baseConfig === undefined) { + baseConfig = SING_BOX_CONFIG + } + super(inputString, baseConfig); + this.selectedRules = selectedRules; + this.customRules = customRules; + this.pin = pin; + } + + addCustomItems(customItems) { + const validItems = customItems.filter(item => item != null); + this.config.outbounds.push(...validItems); + } + + addSelectors() { + let outbounds; + if (typeof this.selectedRules === 'string' && PREDEFINED_RULE_SETS[this.selectedRules]) { + outbounds = getOutbounds(PREDEFINED_RULE_SETS[this.selectedRules]); + } else if(this.selectedRules && Object.keys(this.selectedRules).length > 0) { + outbounds = getOutbounds(this.selectedRules); + } else { + outbounds = getOutbounds(PREDEFINED_RULE_SETS.minimal); + } + + const proxyList = this.config.outbounds.filter(outbound => outbound?.server != undefined).map(outbound => outbound.tag); + + this.config.outbounds.unshift({ + type: "urltest", + tag: "⚡ 自动选择", + outbounds: DeepCopy(proxyList), + }); + +proxyList.unshift('⚡ 自动选择', 'DIRECT'); +outbounds.unshift('🚀 节点选择','GLOBAL'); + +outbounds.forEach(outbound => { + if (outbound === '🔒 国内服务' || outbound === '🏠 私有网络') { + this.config.outbounds.push({ + type: "selector", + tag: outbound, + outbounds: ['DIRECT', '🚀 节点选择'] // DIRECT 优先 + }); + } else if (outbound !== '🚀 节点选择') { + this.config.outbounds.push({ + type: "selector", + tag: outbound, + outbounds: ['🚀 节点选择', ...proxyList] + }); + } else { + this.config.outbounds.unshift({ + type: "selector", + tag: outbound, + outbounds: proxyList + }); + } +}); + + if (Array.isArray(this.customRules)) { + this.customRules.forEach(rule => { + this.config.outbounds.push({ + type: "selector", + tag: rule.name, + outbounds: ['DIRECT', '🚀 节点选择'] // DIRECT 优先 + }); + }); + } + + this.config.outbounds.push({ + type: "selector", + tag: "🐟 漏网之鱼", + outbounds: ['🚀 节点选择', ...proxyList] + }); + } + + formatConfig() { + const rules = generateRules(this.selectedRules, this.customRules, this.pin); + const { site_rule_sets, ip_rule_sets } = generateRuleSets(this.selectedRules,this.customRules); + + this.config.route.rule_set = [...site_rule_sets, ...ip_rule_sets]; + + this.config.route.rules = rules.map(rule => ({ + rule_set: [ + ...(rule.site_rules.length > 0 && rule.site_rules[0] !== '' ? rule.site_rules : []), + ...(rule.ip_rules.filter(ip => ip.trim() !== '').map(ip => `${ip}-ip`)) + ], + domain_suffix: rule.domain_suffix, + domain_keyword: rule.domain_keyword, + ip_cidr: rule.ip_cidr, + protocol: rule.protocol, + outbound: rule.outbound + })); + // Add any default rules that should always be present + this.config.route.rules.unshift( + { action: 'sniff' }, + { type:'logical',mode:'or',rules:[{protocol:'dns'},{port:53}],action:'hijack-dns' }, + { ip_is_private:true,outbound:'DIRECT' }, + { "clash_mode":"Ad-block","rule_set":"category-ads-all","action":"reject","method":"default" }, + { clash_mode: 'Globl', outbound: 'GLOBAL' } + ); + //漏网域名解析为 IP ,若为国内 IP 则走直连 + this.config.route.rules.push( + { action: "resolve" }, + { rule_set: "cn-ip", outbound: "DIRECT" } + ); + + + this.config.route.auto_detect_interface = true; + this.config.route.final = '🐟 漏网之鱼'; + + return this.config; + } +} diff --git a/src/config.js b/src/config.js index 1751f3c7..c7708552 100644 --- a/src/config.js +++ b/src/config.js @@ -4,12 +4,7 @@ export const IP_RULE_SET_BASE_URL = 'https://raw.githubusercontent.com/lyc8503/s export const CUSTOM_RULES = []; // Unified rule structure export const UNIFIED_RULES = [ - { - name: 'Ad Block', - outbound: '🛑 广告拦截', - site_rules: ['category-ads-all'], - ip_rules: [] - }, + { name: 'AI Services', outbound: '💬 AI 服务', @@ -246,6 +241,16 @@ export function generateRuleSets(selectedRules = [], customRules = []) { download_detour: '⚡ 自动选择' })); + +// 添加 ad 规则集 + site_rule_sets.push({ + tag: 'category-ads-all', + type: 'remote', + format: 'binary', + url: `${SITE_RULE_SET_BASE_URL}geosite-category-ads-all.srs`, + download_detour: '⚡ 自动选择' + }); + if(!selectedRules.includes('Non-China')){ site_rule_sets.push({ tag: 'geolocation-!cn', @@ -311,14 +316,6 @@ export const SING_BOX_CONFIG = { address: "223.5.5.5", detour: "DIRECT" }, - { - tag: "dns_success", - address: "rcode://success" - }, - { - tag: "dns_refused", - address: "rcode://refused" - }, { tag: "dns_fakeip", address: "fakeip" @@ -328,34 +325,22 @@ export const SING_BOX_CONFIG = { { outbound: "any", server: "dns_resolver" + }, + { + rule_set: "geolocation-cn", + server: "dns_direct" }, { - rule_set: "geolocation-!cn", query_type: [ "A", "AAAA" ], + rewrite_ttl: 1, server: "dns_fakeip" - }, - { - rule_set: "geolocation-!cn", - query_type: [ - "CNAME" - ], - server: "dns_proxy" - }, - { - query_type: [ - "A", - "AAAA", - "CNAME" - ], - invert: true, - server: "dns_refused", - disable_cache: true } ], - final: "dns_direct", + strategy: "ipv4_only", + final: "dns_proxy", independent_cache: true, fakeip: { enabled: true, @@ -371,13 +356,10 @@ export const SING_BOX_CONFIG = { detour: 'DIRECT' }, inbounds: [ - { type: 'mixed', tag: 'mixed-in', listen: '0.0.0.0', listen_port: 2080 }, - { type: 'tun', tag: 'tun-in', address: '172.19.0.1/30', auto_route: true, strict_route: true, stack: 'mixed', sniff: true } +{"type":"mixed","tag":"mixed-in","listen":"0.0.0.0","listen_port":8888},{"type":"tun","tag":"tun-in","mtu":9000,"address":"172.18.0.1/30","auto_route":true,"strict_route":true,"stack":"mixed","platform":{"http_proxy":{"enabled":true,"server":"127.0.0.1","server_port":8888}}} ], outbounds: [ - { type: 'direct', tag: 'DIRECT' }, - { type: 'block', tag: 'REJECT' }, - { type: 'dns', tag: 'dns-out' } + { type: 'direct', tag: 'DIRECT' } ], route : { "rule_set": [ @@ -398,13 +380,19 @@ export const SING_BOX_CONFIG = { experimental: { cache_file: { enabled: true, + path: 'cache000.db', store_fakeip: true }, clash_api: { external_controller: '127.0.0.1:9090', external_ui: 'dashboard' } - } + }, + log: { + disabled: false, + level: "error", + output: "", + } }; export const CLASH_CONFIG = { @@ -439,4 +427,4 @@ export const CLASH_CONFIG = { }, proxies: [], 'proxy-groups': [], -}; +}; \ No newline at end of file diff --git a/src/htmlBuilder.js b/src/htmlBuilder.js index 7d084820..87399ba7 100644 --- a/src/htmlBuilder.js +++ b/src/htmlBuilder.js @@ -16,7 +16,7 @@ const generateHead = () => ` - +