Skip to content

Commit

Permalink
feat: use zod to validate node configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
geekdada committed Apr 17, 2023
1 parent ac68299 commit ab1d7fe
Show file tree
Hide file tree
Showing 39 changed files with 790 additions and 721 deletions.
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
"name": "surgio",
"description": "Generating rules for Surge, Clash, Quantumult like a PRO",
"version": "2.25.0",
"main": "./build/index.js",
"typings": "./build/index.d.ts",
"exports": {
".": {
"types": "./build/index.d.ts",
Expand Down Expand Up @@ -31,6 +29,7 @@
"homepage": "https://surgio.royli.dev",
"scripts": {
"test": "run-s build test:*",
"test:types": "tsc --noEmit",
"test:lint": "eslint -c .eslintrc.js --ext .js,.ts .",
"test:unit": "ava",
"test:cli": "mocha",
Expand Down Expand Up @@ -93,7 +92,6 @@
"hpagent": "^1.2.0",
"inquirer": "^8.2.5",
"ioredis": "^5.3.1",
"joi": "^17.9.1",
"lodash": "^4.17.21",
"micromatch": "^4.0.5",
"ms": "^2.1.3",
Expand All @@ -107,7 +105,9 @@
"type-fest": "^3.7.2",
"update-notifier": "^5.1.0",
"urlsafe-base64": "^1.0.0",
"yaml": "^2.2.1"
"yaml": "^2.2.1",
"zod": "^3.21.4",
"zod-validation-error": "^1.3.0"
},
"devDependencies": {
"@algolia/client-search": "^4.16.0",
Expand All @@ -120,7 +120,6 @@
"@types/chai": "^4.3.4",
"@types/debug": "^4.1.7",
"@types/fs-extra": "^11.0.1",
"@types/hapi__joi": "^17.1.9",
"@types/inquirer": "^8.2.6",
"@types/ioredis-mock": "^8.2.1",
"@types/lodash": "^4.14.192",
Expand Down
63 changes: 19 additions & 44 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion src/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ abstract class BaseCommand<T extends typeof Command> extends Command {
this.ora.fail();
}
await errorHandler.call(this, err);
return super.catch(err);
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/commands/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,17 @@ class CheckCommand extends BaseCommand<typeof CheckCommand> {
type: 'list',
name: 'node',
message: '请选择节点',
choices: nodeList.map((node) => ({
name: `${node.nodeName} - ${node.hostname}:${node.port}`,
value: node,
})),
choices: nodeList.map((node) => {
const name =
'hostname' in node && 'port' in node
? `${node.nodeName} - ${node.hostname}:${node.port}`
: node.nodeName;

return {
name,
value: node,
};
}),
},
]);

Expand Down
64 changes: 37 additions & 27 deletions src/generator/artifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import path from 'path';
import { EventEmitter } from 'events';

import { getProvider } from '../provider';
import { PossibleProviderType } from '../provider/types';
import {
ArtifactConfig,
CommandConfig,
Expand Down Expand Up @@ -291,48 +292,57 @@ export class Artifact extends EventEmitter {
throw new Error(`文件 ${filePath} 不存在`);
}

let provider: ThenArg<ReturnType<typeof getProvider>>;
let provider: PossibleProviderType;
let nodeConfigList: ReadonlyArray<PossibleNodeConfigType>;

try {
// eslint-disable-next-line prefer-const
provider = await getProvider(providerName, require(filePath));
this.providerMap.set(providerName, provider);
} catch (err) /* istanbul ignore next */ {
err.message = `处理 ${providerName} 时出现错误,相关文件 ${filePath} ,错误原因: ${err.message}`;
throw err;
throw new Error(
`处理 Provider ${providerName} 时出现错误\n文件地址: ${filePath}`,
{
cause: err,
},
);
}

try {
nodeConfigList = await provider.getNodeList({ requestUserAgent });
} catch (err) /* istanbul ignore next */ {
err.message = `获取 ${providerName} 节点时出现错误,相关文件 ${filePath} ,错误原因: ${err.message}`;
throw err;
throw new Error(
`获取 Provider ${providerName} 节点时出现错误\n文件地址: ${filePath}`,
{
cause: err,
},
);
}

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

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

nodeConfigList = (
Expand All @@ -343,12 +353,12 @@ export class Artifact extends EventEmitter {
return undefined;
}

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

Expand All @@ -364,38 +374,38 @@ export class Artifact extends EventEmitter {
nodeConfig.quantumultXConfig = config.quantumultXConfig;
nodeConfig.surfboardConfig = config.surfboardConfig;

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

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

if (provider.addFlag) {
if (provider.config.addFlag) {
// 给节点名加国旗
nodeConfig.nodeName = prependFlag(
nodeConfig.nodeName,
provider.removeExistingFlag,
provider.config.removeExistingFlag,
);
} else if (provider.removeExistingFlag) {
} else if (provider.config.removeExistingFlag) {
// 去掉名称中的国旗
nodeConfig.nodeName = removeFlag(nodeConfig.nodeName);
}

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

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

// Underlying Proxy
if (!nodeConfig.underlyingProxy && provider.underlyingProxy) {
nodeConfig.underlyingProxy = provider.underlyingProxy;
if (!nodeConfig.underlyingProxy && provider.config.underlyingProxy) {
nodeConfig.underlyingProxy = provider.config.underlyingProxy;
}

// check whether the hostname resolves in case of blocking clash's node heurestic
Expand Down
21 changes: 10 additions & 11 deletions src/provider/BlackSSLProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// istanbul ignore file

import Joi from 'joi';
import { z } from 'zod';
import assert from 'assert';

import {
Expand All @@ -20,20 +20,19 @@ export default class BlackSSLProvider extends Provider {
constructor(name: string, config: BlackSSLProviderConfig) {
super(name, config);

const schema = Joi.object({
username: Joi.string().required(),
password: Joi.string().required(),
}).unknown();

const { error } = schema.validate(config);
const schema = z.object({
username: z.string(),
password: z.string(),
});
const result = schema.safeParse(config);

// istanbul ignore next
if (error) {
throw error;
if (!result.success) {
throw result.error;
}

this.username = config.username;
this.password = config.password;
this.username = result.data.username;
this.password = result.data.password;
this.supportGetSubscriptionUserInfo = true;
}

Expand Down
Loading

0 comments on commit ab1d7fe

Please sign in to comment.