@alltiptop/geoip-3xui-rules
Version:
Middleware server to set routing rules by countries for XRAY
69 lines (68 loc) • 2.29 kB
JavaScript
const normalizeRuleField = (value) => {
if (Array.isArray(value)) {
return JSON.stringify([...value].sort());
}
if (value && typeof value === 'object') {
return JSON.stringify(value);
}
return JSON.stringify(value ?? null);
};
export const removeDuplicateRules = (json) => {
const rules = json.routing?.rules;
if (!rules)
return json;
const inbounds = {
default: new Set(),
};
const finalRules = [];
for (const originalRule of rules) {
const rule = JSON.parse(JSON.stringify(originalRule));
const dedupeKey = [
['inboundTag', rule.inboundTag],
['user', rule.user],
['port', rule.port],
['localPort', rule.localPort],
['vlessRoute', rule.vlessRoute],
['process', rule.process],
['localIP', rule.localIP],
['network', rule.network],
]
.map(([key, value]) => `${key}:${normalizeRuleField(value)}`)
.join('|');
const seenRules = inbounds[dedupeKey] || (inbounds[dedupeKey] = new Set());
const hadDomains = Array.isArray(rule.domain);
const hadIps = Array.isArray(rule.ip);
if (hadDomains) {
rule.domain = rule.domain.filter((d) => {
if (seenRules.has(d))
return false;
seenRules.add(d);
return true;
});
}
if (hadIps) {
rule.ip = rule.ip.filter((i) => {
if (seenRules.has(i))
return false;
seenRules.add(i);
return true;
});
}
const hasDomainsNow = hadDomains && rule.domain.length > 0;
const hasIpsNow = hadIps && rule.ip.length > 0;
// A rule object that originally had domain/ip rules is removed if both lists become empty.
// A rule object that never had domain/ip rules is kept.
if ((hadDomains || hadIps) && !hasDomainsNow && !hasIpsNow) {
// This rule became empty, so we skip it.
continue;
}
finalRules.push(rule);
}
return {
...json,
routing: {
...json.routing,
rules: finalRules,
},
};
};