Skip to content

Commit

Permalink
Refactor: Enhance and streamline configuration builders for improved …
Browse files Browse the repository at this point in the history
…readability and maintainability
  • Loading branch information
7Sageer committed Feb 22, 2025
1 parent e5d263f commit 4431513
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 174 deletions.
82 changes: 76 additions & 6 deletions src/BaseConfigBuilder.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { ProxyParser } from './ProxyParsers.js';
import { DeepCopy } from './utils.js';
import { t, setLanguage } from './i18n/index.js'
import { t, setLanguage } from './i18n/index.js';
import { generateRules, getOutbounds, PREDEFINED_RULE_SETS } from './config.js';

export class BaseConfigBuilder {
constructor(inputString, baseConfig, lang) {
this.inputString = inputString;
this.config = DeepCopy(baseConfig);
this.customRules = [];
this.selectedRules = [];
setLanguage(lang);
}

Expand Down Expand Up @@ -38,18 +40,86 @@ export class BaseConfigBuilder {
return parsedItems;
}

getOutboundsList() {
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);
}
return outbounds;
}

getProxyList() {
return this.getProxies().map(proxy => this.getProxyName(proxy));
}

getProxies() {
throw new Error('getProxies must be implemented in child class');
}

getProxyName(proxy) {
throw new Error('getProxyName must be implemented in child class');
}

convertProxy(proxy) {
throw new Error('convertProxy must be implemented in child class');
}

addProxyToConfig(proxy) {
throw new Error('addProxyToConfig must be implemented in child class');
}

addAutoSelectGroup(proxyList) {
throw new Error('addAutoSelectGroup must be implemented in child class');
}

addNodeSelectGroup(proxyList) {
throw new Error('addNodeSelectGroup must be implemented in child class');
}

addOutboundGroups(outbounds, proxyList) {
throw new Error('addOutboundGroups must be implemented in child class');
}

addCustomRuleGroups(proxyList) {
throw new Error('addCustomRuleGroups must be implemented in child class');
}

addFallBackGroup(proxyList) {
throw new Error('addFallBackGroup must be implemented in child class');
}

addCustomItems(customItems) {
// This method should be implemented in child classes
throw new Error('addCustomItems must be implemented in child class');
const validItems = customItems.filter(item => item != null);
validItems.forEach(item => {
if (item?.tag) {
const convertedProxy = this.convertProxy(item);
if (convertedProxy) {
this.addProxyToConfig(convertedProxy);
}
}
});
}

addSelectors() {
// This method should be implemented in child classes
throw new Error('addSelectors must be implemented in child class');
const outbounds = this.getOutboundsList();
const proxyList = this.getProxyList();

this.addAutoSelectGroup(proxyList);
this.addNodeSelectGroup(proxyList);
this.addOutboundGroups(outbounds, proxyList);
this.addCustomRuleGroups(proxyList);
this.addFallBackGroup(proxyList);
}

generateRules() {
return generateRules(this.selectedRules, this.customRules);
}

formatConfig() {
// This method should be implemented in child classes
throw new Error('formatConfig must be implemented in child class');
}
}
169 changes: 85 additions & 84 deletions src/ClashConfigBuilder.js
Original file line number Diff line number Diff line change
@@ -1,102 +1,28 @@
import yaml from 'js-yaml';
import { CLASH_CONFIG, generateRules, getOutbounds, PREDEFINED_RULE_SETS } from './config.js';
import { CLASH_CONFIG } from './config.js';
import { BaseConfigBuilder } from './BaseConfigBuilder.js';
import { DeepCopy } from './utils.js';
import { t } from './i18n/index.js';

export class ClashConfigBuilder extends BaseConfigBuilder {
constructor(inputString, selectedRules, customRules, baseConfig, lang) {
if (!baseConfig) {
baseConfig = CLASH_CONFIG
baseConfig = CLASH_CONFIG;
}
super(inputString, baseConfig, lang);
this.selectedRules = selectedRules;
this.customRules = customRules;
}

addCustomItems(customItems) {
customItems.forEach(item => {
if (item?.tag && !this.config.proxies.some(p => p.name === item.tag)) {
this.config.proxies.push(this.convertToClashProxy(item));
}
});
}

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.proxies.map(proxy => proxy.name);

this.config['proxy-groups'].push({
name: t('outboundNames.Auto Select'),
type: 'url-test',
proxies: DeepCopy(proxyList),
url: 'https://www.gstatic.com/generate_204',
interval: 300,
lazy: false
});

proxyList.unshift('DIRECT', 'REJECT', t('outboundNames.Auto Select'));
outbounds.unshift(t('outboundNames.Node Select'));

outbounds.forEach(outbound => {
if (outbound !== t('outboundNames.Node Select')) {
this.config['proxy-groups'].push({
type: "select",
name: t(`outboundNames.${outbound}`),
proxies: [t('outboundNames.Node Select'), ...proxyList]
});
} else {
this.config['proxy-groups'].unshift({
type: "select",
name: outbound,
proxies: proxyList
});
}
});

if (Array.isArray(this.customRules)) {
this.customRules.forEach(rule => {
this.config['proxy-groups'].push({
type: "select",
name: t(`outboundNames.${rule.name}`),
proxies: [t('outboundNames.Node Select'), ...proxyList]
});
});
}

this.config['proxy-groups'].push({
type: "select",
name: t('outboundNames.Fall Back'),
proxies: [t('outboundNames.Node Select'), ...proxyList]
});
getProxies() {
return this.config.proxies || [];
}
formatConfig() {
const rules = generateRules(this.selectedRules, this.customRules);

this.config.rules = rules.flatMap(rule => {
const siteRules = rule.site_rules[0] !== '' ? rule.site_rules.map(site => `GEOSITE,${site},${t('outboundNames.'+ rule.outbound)}`) : [];
const ipRules = rule.ip_rules[0] !== '' ? rule.ip_rules.map(ip => `GEOIP,${ip},${t('outboundNames.'+ rule.outbound)},no-resolve`) : [];
const domainSuffixRules = rule.domain_suffix ? rule.domain_suffix.map(suffix => `DOMAIN-SUFFIX,${suffix},${t('outboundNames.'+ rule.outbound)}`) : [];
const domainKeywordRules = rule.domain_keyword ? rule.domain_keyword.map(keyword => `DOMAIN-KEYWORD,${keyword},${t('outboundNames.'+ rule.outbound)}`) : [];
const ipCidrRules = rule.ip_cidr ? rule.ip_cidr.map(cidr => `IP-CIDR,${cidr},${t('outboundNames.'+ rule.outbound)},no-resolve`) : [];
return [...siteRules, ...ipRules, ...domainSuffixRules, ...domainKeywordRules, ...ipCidrRules];
});

// Add the final catch-all rule
this.config.rules.push(`MATCH,${t('outboundNames.Fall Back')}`);

return yaml.dump(this.config);
getProxyName(proxy) {
return proxy.name;
}

convertToClashProxy(proxy) {
convertProxy(proxy) {
switch(proxy.type) {
case 'shadowsocks':
return {
Expand Down Expand Up @@ -164,8 +90,8 @@ export class ClashConfigBuilder extends BaseConfigBuilder {
auth: proxy.password,
'skip-cert-verify': proxy.tls.insecure,
};
case 'trojan':
return {
case 'trojan':
return {
name: proxy.tag,
type: proxy.type,
server: proxy.server,
Expand All @@ -191,7 +117,7 @@ export class ClashConfigBuilder extends BaseConfigBuilder {
tfo : proxy.tcp_fast_open,
'skip-cert-verify': proxy.tls.insecure,
'flow': proxy.flow ?? undefined,
}
};
case 'tuic':
return {
name: proxy.tag,
Expand All @@ -211,4 +137,79 @@ export class ClashConfigBuilder extends BaseConfigBuilder {
return proxy; // Return as-is if no specific conversion is defined
}
}

addProxyToConfig(proxy) {
this.config.proxies = this.config.proxies || [];
this.config.proxies.push(proxy);
}

addAutoSelectGroup(proxyList) {
this.config['proxy-groups'] = this.config['proxy-groups'] || [];
this.config['proxy-groups'].push({
name: t('outboundNames.Auto Select'),
type: 'url-test',
proxies: DeepCopy(proxyList),
url: 'https://www.gstatic.com/generate_204',
interval: 300,
lazy: false
});
}

addNodeSelectGroup(proxyList) {
proxyList.unshift('DIRECT', 'REJECT', t('outboundNames.Auto Select'));
this.config['proxy-groups'].unshift({
type: "select",
name: t('outboundNames.Node Select'),
proxies: proxyList
});
}

addOutboundGroups(outbounds, proxyList) {
outbounds.forEach(outbound => {
if (outbound !== t('outboundNames.Node Select')) {
this.config['proxy-groups'].push({
type: "select",
name: t(`outboundNames.${outbound}`),
proxies: [t('outboundNames.Node Select'), ...proxyList]
});
}
});
}

addCustomRuleGroups(proxyList) {
if (Array.isArray(this.customRules)) {
this.customRules.forEach(rule => {
this.config['proxy-groups'].push({
type: "select",
name: t(`outboundNames.${rule.name}`),
proxies: [t('outboundNames.Node Select'), ...proxyList]
});
});
}
}

addFallBackGroup(proxyList) {
this.config['proxy-groups'].push({
type: "select",
name: t('outboundNames.Fall Back'),
proxies: [t('outboundNames.Node Select'), ...proxyList]
});
}

formatConfig() {
const rules = this.generateRules();

this.config.rules = rules.flatMap(rule => {
const siteRules = rule.site_rules[0] !== '' ? rule.site_rules.map(site => `GEOSITE,${site},${t('outboundNames.'+ rule.outbound)}`) : [];
const ipRules = rule.ip_rules[0] !== '' ? rule.ip_rules.map(ip => `GEOIP,${ip},${t('outboundNames.'+ rule.outbound)},no-resolve`) : [];
const domainSuffixRules = rule.domain_suffix ? rule.domain_suffix.map(suffix => `DOMAIN-SUFFIX,${suffix},${t('outboundNames.'+ rule.outbound)}`) : [];
const domainKeywordRules = rule.domain_keyword ? rule.domain_keyword.map(keyword => `DOMAIN-KEYWORD,${keyword},${t('outboundNames.'+ rule.outbound)}`) : [];
const ipCidrRules = rule.ip_cidr ? rule.ip_cidr.map(cidr => `IP-CIDR,${cidr},${t('outboundNames.'+ rule.outbound)},no-resolve`) : [];
return [...siteRules, ...ipRules, ...domainSuffixRules, ...domainKeywordRules, ...ipCidrRules];
});

this.config.rules.push(`MATCH,${t('outboundNames.Fall Back')}`);

return yaml.dump(this.config);
}
}
Loading

0 comments on commit 4431513

Please sign in to comment.