@herbertgao/surgio
Version:
Generating rules for Surge, Clash, Quantumult like a PRO
236 lines • 11.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLoonNodeNames = exports.getLoonNodes = void 0;
const net_1 = require("net");
const logger_1 = require("@surgio/logger");
const lodash_1 = __importDefault(require("lodash"));
const types_1 = require("../types");
const constant_1 = require("../constant");
const filters_1 = require("../filters");
const index_1 = require("./index");
const { httpFilter, httpsFilter, shadowsocksFilter, shadowsocksrFilter, trojanFilter, vmessFilter, wireguardFilter, vlessFilter, } = filters_1.internalFilters;
const logger = (0, logger_1.createLogger)({ service: 'surgio:utils:loon' });
// https://nsloon.app/docs/Node/#%E8%8A%82%E7%82%B9%E6%A0%BC%E5%BC%8F
const getLoonNodes = function (list, filter) {
if (arguments.length === 2 && typeof filter === 'undefined') {
throw new Error(constant_1.ERR_INVALID_FILTER);
}
const result = (0, filters_1.applyFilter)(list, filter)
.map((nodeConfig) => {
switch (nodeConfig.type) {
case types_1.NodeTypeEnum.Shadowsocks: {
const config = [
`${nodeConfig.nodeName} = Shadowsocks`,
nodeConfig.hostname,
nodeConfig.port,
nodeConfig.method,
JSON.stringify(nodeConfig.password),
];
if (nodeConfig.obfs) {
if (['http', 'tls'].includes(nodeConfig.obfs)) {
config.push(nodeConfig.obfs, nodeConfig.obfsHost || nodeConfig.hostname);
}
else {
logger.warn(`不支持为 Loon 生成混淆为 ${nodeConfig.obfs} 的节点,节点 ${nodeConfig.nodeName} 会被省略`);
return void 0;
}
}
if (nodeConfig.tfo) {
config.push('fast-open=true');
}
if (nodeConfig.udpRelay) {
config.push('udp=true');
}
return config.join(',');
}
case types_1.NodeTypeEnum.Shadowsocksr: {
const config = [
`${nodeConfig.nodeName} = ShadowsocksR`,
nodeConfig.hostname,
nodeConfig.port,
nodeConfig.method,
JSON.stringify(nodeConfig.password),
`protocol=${nodeConfig.protocol}`,
`protocol-param=${nodeConfig.protoparam}`,
`obfs=${nodeConfig.obfs}`,
`obfs-param=${nodeConfig.obfsparam}`,
];
if (nodeConfig.tfo) {
config.push('fast-open=true');
}
if (nodeConfig.udpRelay) {
config.push('udp=true');
}
return config.join(',');
}
case types_1.NodeTypeEnum.Vless:
case types_1.NodeTypeEnum.Vmess: {
if (!constant_1.LOON_SUPPORTED_VMESS_NETWORK.includes(nodeConfig.network)) {
logger.warn(`Loon 不支持 ${nodeConfig.network} 的 ${nodeConfig.type.toUpperCase()} 节点,节点 ${nodeConfig.nodeName} 会被省略`);
return void 0;
}
const config = [
`${nodeConfig.nodeName} = ${nodeConfig.type === types_1.NodeTypeEnum.Vmess ? 'vmess' : 'VLESS'}`,
nodeConfig.hostname,
nodeConfig.port,
];
if (nodeConfig.type === types_1.NodeTypeEnum.Vmess) {
config.push(nodeConfig.method === 'auto'
? `chacha20-poly1305`
: nodeConfig.method);
}
config.push(JSON.stringify(nodeConfig.uuid), `transport=${nodeConfig.network}`);
if (nodeConfig.network === 'ws' && nodeConfig.wsOpts) {
const obfsHost = (0, index_1.getHeader)(nodeConfig.wsOpts.headers, 'Host');
config.push(`path=${nodeConfig.wsOpts.path || '/'}`);
if (obfsHost) {
config.push(`host=${obfsHost}`);
}
}
if (nodeConfig.network === 'http' && nodeConfig.httpOpts) {
const obfsHost = (0, index_1.getHeader)(nodeConfig.httpOpts.headers, 'Host');
config.push(`path=${nodeConfig.httpOpts.path || '/'}`);
if (obfsHost) {
config.push(`host=${obfsHost}`);
}
// istanbul ignore next
if (nodeConfig.httpOpts.method !== 'GET') {
logger.warn(`Loon 不支持自定义 VMESS+HTTP 节点的 method 属性,节点 ${nodeConfig.nodeName} 可能不可用`);
}
}
if ((nodeConfig.type === types_1.NodeTypeEnum.Vmess && nodeConfig.tls) ||
nodeConfig.type === types_1.NodeTypeEnum.Vless) {
config.push(`over-tls=true`);
if (nodeConfig.sni) {
config.push(`tls-name=${nodeConfig.sni}`);
}
if (nodeConfig.skipCertVerify) {
config.push(`skip-cert-verify=true`);
}
}
return config.join(',');
}
case types_1.NodeTypeEnum.Trojan: {
const config = [
`${nodeConfig.nodeName} = trojan`,
nodeConfig.hostname,
nodeConfig.port,
JSON.stringify(nodeConfig.password),
`tls-name=${nodeConfig.sni || nodeConfig.hostname}`,
`skip-cert-verify=${nodeConfig.skipCertVerify === true}`,
];
if (nodeConfig.network === 'ws') {
config.push('transport=ws', `path=${nodeConfig.wsPath || '/'}`);
if (nodeConfig.wsHeaders) {
if (lodash_1.default.get(nodeConfig, 'wsHeaders.host')) {
config.push(`host=${nodeConfig.wsHeaders.host}`);
}
if (Object.keys(lodash_1.default.omit(nodeConfig.wsHeaders, 'host')).length > 0) {
logger.warn(`Loon 不支持自定义额外的 Header 字段,节点 ${nodeConfig.nodeName} 可能不可用`);
}
}
}
if (nodeConfig.tfo) {
config.push('fast-open=true');
}
if (nodeConfig.udpRelay) {
config.push('udp=true');
}
return config.join(',');
}
case types_1.NodeTypeEnum.HTTPS: {
const config = [
`${nodeConfig.nodeName} = https`,
nodeConfig.hostname,
nodeConfig.port,
nodeConfig.username /* istanbul ignore next */ || '',
JSON.stringify(nodeConfig.password /* istanbul ignore next */ || ''),
`tls-name=${nodeConfig.sni || nodeConfig.hostname}`,
`skip-cert-verify=${nodeConfig.skipCertVerify === true}`,
];
return config.join(',');
}
case types_1.NodeTypeEnum.HTTP:
return [
`${nodeConfig.nodeName} = http`,
nodeConfig.hostname,
nodeConfig.port,
nodeConfig.username /* istanbul ignore next */ || '',
JSON.stringify(nodeConfig.password /* istanbul ignore next */ || ''),
].join(',');
case types_1.NodeTypeEnum.Wireguard: {
const config = [
`${nodeConfig.nodeName} = wireguard`,
`interface-ip=${nodeConfig.selfIp}`,
`private-key=${JSON.stringify(nodeConfig.privateKey)}`,
];
const peers = [];
if (nodeConfig.selfIpV6) {
config.push(`interface-ipV6=${nodeConfig.selfIpV6}`);
}
if (nodeConfig.mtu) {
config.push(`mtu=${nodeConfig.mtu}`);
}
if (nodeConfig.dnsServers) {
for (const dns of nodeConfig.dnsServers) {
if ((0, net_1.isIPv4)(dns)) {
config.push(`dns=${dns}`);
}
else {
config.push(`dnsV6=${dns}`);
}
}
}
if (nodeConfig.peers[0].keepalive) {
config.push(`keepalive=${nodeConfig.peers[0].keepalive}`);
}
for (const peer of nodeConfig.peers) {
const peerConfig = [
`public-key=${JSON.stringify(peer.publicKey)}`,
`endpoint=${peer.endpoint}`,
];
if (peer.allowedIps) {
peerConfig.push(`allowed-ips=${JSON.stringify(peer.allowedIps)}`);
}
if (peer.presharedKey) {
peerConfig.push(`preshared-key=${JSON.stringify(peer.presharedKey)}`);
}
if (peer.reservedBits) {
peerConfig.push(`reserved=${JSON.stringify(peer.reservedBits)}`);
}
peers.push(`{${peerConfig.join(',')}}`);
}
config.push(`peers=[${peers.join(',')}]`);
return config.join(',');
}
// istanbul ignore next
default:
logger.warn(`不支持为 Loon 生成 ${nodeConfig.type} 的节点,节点 ${nodeConfig.nodeName} 会被省略`);
return void 0;
}
})
.filter((item) => item !== undefined);
return result.join('\n');
};
exports.getLoonNodes = getLoonNodes;
const getLoonNodeNames = function (list, filter, separator) {
// istanbul ignore next
if (arguments.length === 2 && typeof filter === 'undefined') {
throw new Error(constant_1.ERR_INVALID_FILTER);
}
return (0, filters_1.applyFilter)(list.filter((item) => shadowsocksFilter(item) ||
shadowsocksrFilter(item) ||
vmessFilter(item) ||
httpFilter(item) ||
httpsFilter(item) ||
trojanFilter(item) ||
wireguardFilter(item) ||
vlessFilter(item)), filter)
.map((item) => item.nodeName)
.join(separator || ', ');
};
exports.getLoonNodeNames = getLoonNodeNames;
//# sourceMappingURL=loon.js.map