Skip to content

Commit

Permalink
refactor: 修改项目结构
Browse files Browse the repository at this point in the history
  • Loading branch information
geekdada committed Feb 26, 2020
1 parent a94eeab commit 28c39eb
Show file tree
Hide file tree
Showing 18 changed files with 398 additions and 304 deletions.
2 changes: 1 addition & 1 deletion lib/command/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import path from 'path';
import {
loadConfig
} from '../utils/config';
import getProvider from '../utils/get-provider';
import { getProvider } from '../provider';
import { errorHandler } from '../utils/error-helper';

class CheckCommand extends Command {
Expand Down
2 changes: 1 addition & 1 deletion lib/command/subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { CommandConfig } from '../types';
import {
loadConfig
} from '../utils/config';
import getProvider from '../utils/get-provider';
import { getProvider } from '../provider';
import { errorHandler } from '../utils/error-helper';
import { formatSubscriptionUserInfo } from '../utils/subscription';

Expand Down
2 changes: 1 addition & 1 deletion lib/gateway/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import legacyUrl from 'url';
import got from 'got';
import { transformQxRewriteRemote } from '../utils/qx-helper';

import getEngine from '../template';
import { getEngine } from '../generator/template';
import { ArtifactConfig, CommandConfig, RemoteSnippet } from '../types';
import { getDownloadUrl } from '../utils';
import { loadRemoteSnippetList } from '../utils/remote-snippet';
Expand Down
291 changes: 18 additions & 273 deletions lib/generate.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,18 @@
'use strict';

import assert from 'assert';
import Bluebird from 'bluebird';
import chalk from 'chalk';
import fs from 'fs-extra';
import _ from 'lodash';
import { Environment } from 'nunjucks';
import ora from 'ora';
import path from 'path';
import { logger } from '@surgio/logger';
import { Artifact } from './generator/artifact';

import getEngine from './template';
import { getEngine } from './generator/template';
import {
ArtifactConfig,
CommandConfig,
NodeTypeEnum,
PossibleNodeConfigType,
ProviderConfig,
RemoteSnippet,
SimpleNodeConfig,
CommandConfig, RemoteSnippet,
} from './types';
import {
getClashNodes,
getDownloadUrl,
getNodeNames,
getQuantumultNodes,
getV2rayNNodes,
getQuantumultXNodes,
getShadowsocksNodes,
getShadowsocksNodesJSON,
getShadowsocksrNodes,
getSurgeNodes,
getMellowNodes,
normalizeClashProxyGroupConfig,
toBase64,
toUrlSafeBase64,
getClashNodeNames,
} from './utils';
import { loadRemoteSnippetList } from './utils/remote-snippet';
import { isIp, resolveDomain } from './utils/dns';
import {
hkFilter, japanFilter, koreaFilter,
netflixFilter as defaultNetflixFilter,
singaporeFilter,
taiwanFilter,
usFilter,
validateFilter,
youtubePremiumFilter as defaultYoutubePremiumFilter,
} from './utils/filter';
import getProvider from './utils/get-provider';
import { prependFlag } from './utils/flag';
import { NETWORK_CONCURRENCY } from './utils/constant';
import Provider from './provider/Provider';

const spinner = ora();

Expand All @@ -69,7 +30,17 @@ async function run(config: CommandConfig): Promise<void> {
spinner.start(`正在生成规则 ${artifact.name}`);

try {
const result = await generate(config, artifact, remoteSnippetList, templateEngine);
const artifactInstance = new Artifact(config, artifact, {
remoteSnippetList,
});

artifactInstance.on('initProvider:end', () => {
spinner.text = `已处理 Provider ${artifactInstance.initProgress}/${artifactInstance.providerNameList.length}...`;
});

await artifactInstance.init();

const result = artifactInstance.render(templateEngine);
const destFilePath = path.join(config.output, artifact.name);

if (artifact.destDir) {
Expand All @@ -93,239 +64,13 @@ export async function generate(
remoteSnippetList: ReadonlyArray<RemoteSnippet>,
templateEngine: Environment,
): Promise<string> {
const {
name: artifactName,
template,
customParams,
templateString,
} = artifact;

assert(artifactName, '必须指定 artifact 的 name 属性');
assert(artifact.provider, '必须指定 artifact 的 provider 属性');
if (!templateString) {
assert(template, '必须指定 artifact 的 template 属性');
}

const gatewayConfig = config.gateway;
const gatewayHasToken: boolean = !!(gatewayConfig && gatewayConfig.accessToken);
const mainProviderName = artifact.provider;
const combineProviders = artifact.combineProviders || [];
const providerList = [mainProviderName].concat(combineProviders);
const nodeConfigListMap: Map<string, ReadonlyArray<PossibleNodeConfigType>> = new Map();
const nodeList: PossibleNodeConfigType[] = [];
const nodeNameList: SimpleNodeConfig[] = [];
let customFilters: ProviderConfig['customFilters'];
let netflixFilter: ProviderConfig['netflixFilter'];
let youtubePremiumFilter: ProviderConfig['youtubePremiumFilter'];
let progress = 0;

if (config.binPath && config.binPath.v2ray) {
config.binPath.vmess = config.binPath.v2ray;
}

const providerMapper = async (providerName: string): Promise<void> => {
const filePath = path.resolve(config.providerDir, `${providerName}.js`);

if (!fs.existsSync(filePath)) {
throw new Error(`文件 ${filePath} 不存在`);
}

let provider: Provider;
let nodeConfigList: ReadonlyArray<PossibleNodeConfigType>;

try {
provider = getProvider(providerName, require(filePath));
} catch (err) {
err.message = `处理 ${chalk.cyan(providerName)} 时出现错误,相关文件 ${filePath} ,错误原因: ${err.message}`;
throw err;
}

try {
nodeConfigList = await provider.getNodeList();
} catch (err) {
err.message = `获取 ${chalk.cyan(providerName)} 节点时出现错误,相关文件 ${filePath} ,错误原因: ${err.message}`;
throw err;
}

// Filter 仅使用第一个 Provider 中的定义
if (providerName === mainProviderName) {
if (!netflixFilter) {
netflixFilter = provider.netflixFilter || defaultNetflixFilter;
}
if (!youtubePremiumFilter) {
youtubePremiumFilter = provider.youtubePremiumFilter || defaultYoutubePremiumFilter;
}
if (!customFilters) {
customFilters = {
...config.customFilters,
...provider.customFilters,
};
}
}

if (
validateFilter(provider.nodeFilter) &&
typeof provider.nodeFilter === 'object' &&
provider.nodeFilter.supportSort
) {
nodeConfigList = provider.nodeFilter.filter(nodeConfigList);
}

nodeConfigList = await Bluebird.map(nodeConfigList, async nodeConfig => {
let isValid = false;

if (nodeConfig.enable === false) {
return null;
}

if (!provider.nodeFilter) {
isValid = true;
} else if (validateFilter(provider.nodeFilter)) {
isValid = typeof provider.nodeFilter === 'function' ?
provider.nodeFilter(nodeConfig) :
true;
}

if (isValid) {
if (config.binPath && config.binPath[nodeConfig.type]) {
nodeConfig.binPath = config.binPath[nodeConfig.type];
nodeConfig.localPort = provider.nextPort;
}

nodeConfig.provider = provider;
nodeConfig.surgeConfig = config.surgeConfig;

if (provider.renameNode) {
const newName = provider.renameNode(nodeConfig.nodeName);

if (newName) {
nodeConfig.nodeName = newName;
}
}

// 给节点名加国旗
if (provider.addFlag) {
nodeConfig.nodeName = prependFlag(nodeConfig.nodeName);
}

// TCP Fast Open
if (provider.tfo) {
nodeConfig.tfo = provider.tfo;
}

// MPTCP
if (provider.mptcp) {
nodeConfig.mptcp = provider.mptcp;
}

if (
config.surgeConfig.resolveHostname &&
!isIp(nodeConfig.hostname) &&
[NodeTypeEnum.Vmess, NodeTypeEnum.Shadowsocksr].includes(nodeConfig.type)
) {
try {
nodeConfig.hostnameIp = await resolveDomain(nodeConfig.hostname);
} /* istanbul ignore next */ catch (err) {
logger.warn(`${nodeConfig.hostname} 无法解析,将忽略该域名的解析结果`);
}
}

return nodeConfig;
}

return null;
})
.filter(item => !!item);


nodeConfigListMap.set(providerName, nodeConfigList);

spinner.text = `已处理 Provider ${++progress}/${providerList.length}...`;
};

await Bluebird.map(providerList, providerMapper, { concurrency: NETWORK_CONCURRENCY });

providerList.forEach(providerName => {
const nodeConfigList = nodeConfigListMap.get(providerName);

nodeConfigList.forEach(nodeConfig => {
if (nodeConfig) {
nodeNameList.push({
type: nodeConfig.type,
enable: nodeConfig.enable,
nodeName: nodeConfig.nodeName,
provider: nodeConfig.provider,
});
nodeList.push(nodeConfig);
}
});
const artifactInstance = new Artifact(config, artifact, {
remoteSnippetList,
});

const renderContext = {
proxyTestUrl: config.proxyTestUrl,
downloadUrl: getDownloadUrl(config.urlBase, artifactName, true, gatewayHasToken ? gatewayConfig.accessToken : undefined),
nodes: nodeList,
names: nodeNameList,
remoteSnippets: _.keyBy(remoteSnippetList, item => item.name),
nodeList,
provider: artifact.provider,
providerName: artifact.provider,
artifactName,
getDownloadUrl: (name: string) => getDownloadUrl(config.urlBase, name, true, gatewayHasToken ? gatewayConfig.accessToken : undefined),
getNodeNames,
getClashNodeNames,
getClashNodes,
getSurgeNodes,
getShadowsocksNodes,
getShadowsocksNodesJSON,
getShadowsocksrNodes,
getQuantumultNodes,
getV2rayNNodes,
getQuantumultXNodes,
getMellowNodes,
usFilter,
hkFilter,
japanFilter,
koreaFilter,
singaporeFilter,
taiwanFilter,
toUrlSafeBase64,
toBase64,
encodeURIComponent,
netflixFilter,
youtubePremiumFilter,
customFilters,
customParams: customParams || {},
...(artifact.proxyGroupModifier ? {
clashProxyConfig: {
Proxy: getClashNodes(nodeList),
'Proxy Group': normalizeClashProxyGroupConfig(
nodeList,
{
usFilter,
hkFilter,
japanFilter,
koreaFilter,
singaporeFilter,
taiwanFilter,
netflixFilter,
youtubePremiumFilter,
...customFilters,
},
artifact.proxyGroupModifier,
{
proxyTestUrl: config.proxyTestUrl,
proxyTestInterval: config.proxyTestInterval,
},
),
},
} : {}),
};
await artifactInstance.init();

if (templateString) {
return templateEngine.renderString(templateString, renderContext);
}
return templateEngine.render(`${template}.tpl`, renderContext);
return artifactInstance.render(templateEngine);
}

export default async function(config: CommandConfig): Promise<void> {
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// tslint:disable:no-expression-statement
import test from 'ava';
import fs from 'fs-extra';
import path from 'path';
import getEngine from '../lib/template';
import { join } from 'path';
import { getEngine } from '../template';

const templateEngine = getEngine(process.cwd(), 'https://example.com/');
const assetDir = join(__dirname, '../../../test/asset/');

test('clash #1', t => {
const body = `{{ str | patchYamlArray }}`;
Expand Down Expand Up @@ -96,7 +97,7 @@ test('quantumultx filter 1', t => {

test('quantumultx filter 2', t => {
const body = `{{ str | quantumultx }}`;
const str = fs.readFileSync(path.join(__dirname, './asset/surge-script-list.txt'), { encoding: 'utf8' });
const str = fs.readFileSync(join(assetDir, 'surge-script-list.txt'), { encoding: 'utf8' });
const result = templateEngine.renderString(body, {
str,
});
Expand Down Expand Up @@ -143,7 +144,7 @@ test('spaces in string', t => {
});

test('ForeignMedia', t => {
const str = fs.readFileSync(path.join(__dirname, './asset/ForeignMedia.list'), { encoding: 'utf8' });
const str = fs.readFileSync(join(assetDir, 'ForeignMedia.list'), { encoding: 'utf8' });

t.snapshot(templateEngine.renderString(`{{ str | quantumultx }}`, {
str,
Expand Down
Loading

0 comments on commit 28c39eb

Please sign in to comment.