mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-11 01:18:59 +08:00
Added ResolveDomainOperator
This commit is contained in:
parent
71aaa824ec
commit
82ad8a5df8
4
backend/dist/sub-store-parser.loon.min.js
vendored
4
backend/dist/sub-store-parser.loon.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,7 +1,9 @@
|
||||
import { HTTP } from '../../vendor/open-api';
|
||||
import { isIPv4, isIPv6 } from '../../utils';
|
||||
import { FULL } from '../../utils/logical';
|
||||
import { getFlag } from '../../utils/geo';
|
||||
import lodash from 'lodash';
|
||||
import $ from '../app';
|
||||
|
||||
// force to set some properties (e.g., skip-cert-verify, udp, tfo, etc.)
|
||||
function SetPropertyOperator({ key, value }) {
|
||||
@ -222,6 +224,109 @@ function ScriptOperator(script, targetPlatform, $arguments) {
|
||||
};
|
||||
}
|
||||
|
||||
const DOMAIN_RESOLVERS = {
|
||||
Google: async function (domain) {
|
||||
const resp = await $.http.get({
|
||||
url: `https://8.8.4.4/resolve?name=${encodeURIComponent(
|
||||
domain,
|
||||
)}&type=A`,
|
||||
headers: {
|
||||
accept: 'application/dns-json',
|
||||
},
|
||||
});
|
||||
const body = JSON.parse(resp.body);
|
||||
if (body['Status'] !== 0) {
|
||||
throw new Error(`Status is ${body['Status']}`);
|
||||
}
|
||||
const answers = body['Answer'];
|
||||
if (answers.length === 0) {
|
||||
throw new Error('No answers');
|
||||
}
|
||||
return answers[answers.length - 1].data;
|
||||
},
|
||||
'IP-API': async function (domain) {
|
||||
const resp = await $.http.get({
|
||||
url: `http://ip-api.com/json/${encodeURIComponent(
|
||||
domain,
|
||||
)}?lang=zh-CN`,
|
||||
});
|
||||
const body = JSON.parse(resp.body);
|
||||
if (body['status'] !== 'success') {
|
||||
throw new Error(`Status is ${body['status']}`);
|
||||
}
|
||||
return body.query;
|
||||
},
|
||||
Cloudflare: async function (domain) {
|
||||
const resp = await $.http.get({
|
||||
url: `https://1.0.0.1/dns-query?name=${encodeURIComponent(
|
||||
domain,
|
||||
)}&type=A`,
|
||||
headers: {
|
||||
accept: 'application/dns-json',
|
||||
},
|
||||
});
|
||||
const body = JSON.parse(resp.body);
|
||||
if (body['Status'] !== 0) {
|
||||
throw new Error(`Status is ${body['Status']}`);
|
||||
}
|
||||
const answers = body['Answer'];
|
||||
if (answers.length === 0) {
|
||||
throw new Error('No answers');
|
||||
}
|
||||
return answers[answers.length - 1].data;
|
||||
},
|
||||
};
|
||||
|
||||
function ResolveDomainOperator({ provider }) {
|
||||
const resolver = DOMAIN_RESOLVERS[provider];
|
||||
if (!resolver) {
|
||||
throw new Error(`Cannot find resolver: ${provider}`);
|
||||
}
|
||||
return {
|
||||
name: 'Resolve Domain Operator',
|
||||
func: async (proxies) => {
|
||||
const results = {};
|
||||
const resolves = new Map();
|
||||
|
||||
for (const proxy of proxies) {
|
||||
const domain = proxy.server;
|
||||
if (isIP(domain)) continue;
|
||||
if (!resolves.has(domain)) {
|
||||
resolves.set(
|
||||
domain,
|
||||
resolver(domain)
|
||||
.then((ip) => {
|
||||
results[domain] = ip;
|
||||
$.info(
|
||||
`Successfully resolved domain: ${domain} ➟ ${ip}`,
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
$.error(
|
||||
`Failed to resolve domain: ${domain} with resolver [${provider}]: ${err}`,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// resolve domains
|
||||
await Promise.all([...resolves.values()]);
|
||||
proxies.forEach((proxy) => {
|
||||
proxy.server = results[proxy.server];
|
||||
});
|
||||
|
||||
return proxies;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function isIP(ip) {
|
||||
return isIPv4(ip) || isIPv6(ip);
|
||||
}
|
||||
|
||||
ResolveDomainOperator.resolver = DOMAIN_RESOLVERS;
|
||||
|
||||
/**************************** Filters ***************************************/
|
||||
// filter useless proxies
|
||||
function UselessFilter() {
|
||||
@ -305,7 +410,7 @@ function TypeFilter(types) {
|
||||
|
||||
function filter(proxies) {
|
||||
return proxies.map(p => {
|
||||
return p.name.indexOf("🇭🇰") !== -1;
|
||||
return p.name.indexOf('🇭🇰') !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
@ -347,6 +452,7 @@ export default {
|
||||
'Regex Delete Operator': RegexDeleteOperator,
|
||||
'Script Operator': ScriptOperator,
|
||||
'Handle Duplicate Operator': HandleDuplicateOperator,
|
||||
'Resolve Domain Operator': ResolveDomainOperator,
|
||||
};
|
||||
|
||||
async function ApplyFilter(filter, objs) {
|
||||
|
@ -2,6 +2,7 @@ export const SETTINGS_KEY = 'settings';
|
||||
export const SUBS_KEY = 'subs';
|
||||
export const COLLECTIONS_KEY = 'collections';
|
||||
export const ARTIFACTS_KEY = 'artifacts';
|
||||
export const RULES_KEY = 'rules';
|
||||
export const GIST_BACKUP_KEY = 'Auto Generated Sub-Store Backup';
|
||||
export const GIST_BACKUP_FILE_NAME = 'Sub-Store';
|
||||
export const ARTIFACT_REPOSITORY_KEY = 'Sub-Store Artifacts Repository';
|
||||
|
@ -3,9 +3,8 @@ import {
|
||||
GIST_BACKUP_KEY,
|
||||
GIST_BACKUP_FILE_NAME,
|
||||
} from './constants';
|
||||
import { ENV } from '../vendor/open-api';
|
||||
import { ENV, HTTP } from '../vendor/open-api';
|
||||
import express from '../vendor/express';
|
||||
import IP_API from '../utils/ip-api';
|
||||
import Gist from '../utils/gist';
|
||||
import $ from '../core/app';
|
||||
|
||||
@ -116,3 +115,12 @@ async function gistBackup(req, res) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function IP_API(req, res) {
|
||||
const server = decodeURIComponent(req.params.server);
|
||||
const $http = HTTP();
|
||||
const result = await $http
|
||||
.get(`http://ip-api.com/json/${server}?lang=zh-CN`)
|
||||
.then((resp) => JSON.parse(resp.body));
|
||||
res.json(result);
|
||||
}
|
||||
|
16
backend/src/utils/index.js
Normal file
16
backend/src/utils/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
// source: https://stackoverflow.com/a/36760050
|
||||
const IPV4_REGEX = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/;
|
||||
|
||||
// source: https://ihateregex.io/expr/ipv6/
|
||||
const IPV6_REGEX =
|
||||
/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
|
||||
|
||||
function isIPv4(ip) {
|
||||
return IPV4_REGEX.test(ip);
|
||||
}
|
||||
|
||||
function isIPv6(ip) {
|
||||
return IPV6_REGEX.test(ip);
|
||||
}
|
||||
|
||||
export { isIPv4, isIPv6 };
|
@ -1,10 +0,0 @@
|
||||
import { HTTP } from '../vendor/open-api';
|
||||
|
||||
export default async function IP_API(req, res) {
|
||||
const server = decodeURIComponent(req.params.server);
|
||||
const $http = HTTP();
|
||||
const result = await $http
|
||||
.get(`http://ip-api.com/json/${server}?lang=zh-CN`)
|
||||
.then((resp) => JSON.parse(resp.body));
|
||||
res.json(result);
|
||||
}
|
4
backend/sub-store.min.js
vendored
4
backend/sub-store.min.js
vendored
File diff suppressed because one or more lines are too long
86
web/src/components/ResolveDomainOperator.vue
Normal file
86
web/src/components/ResolveDomainOperator.vue
Normal file
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<v-card class="ml-1 mr-1 mb-1 mt-1">
|
||||
<v-card-title>
|
||||
<v-icon color="primary" left>dns</v-icon>
|
||||
节点域名解析
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="$emit('up', idx)">
|
||||
<v-icon>keyboard_arrow_up</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$emit('down', idx)">
|
||||
<v-icon>keyboard_arrow_down</v-icon>
|
||||
</v-btn>
|
||||
<v-btn icon @click="$emit('deleteProcess', idx)">
|
||||
<v-icon color="error">mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
<v-dialog>
|
||||
<template #activator="{on}">
|
||||
<v-btn v-on="on" icon>
|
||||
<v-icon>help</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title class="headline">
|
||||
节点域名解析
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
将节点域名解析成 IP 地址
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
服务提供商
|
||||
<v-radio-group v-model="provider">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-radio label="Google" value="Google"/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-radio label="IP-API" value="IP-API"/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-radio label="Cloudflare" value="Cloudflare"/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-radio-group>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ["args"],
|
||||
data: function () {
|
||||
return {
|
||||
idx: this.$vnode.key,
|
||||
provider: "Google"
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (typeof this.args !== "undefined") {
|
||||
this.provider = this.args.provider || "Google";
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
this.$emit("dataChanged", {
|
||||
idx: this.idx,
|
||||
args: {
|
||||
provider: this.provider
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
provider() {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -223,6 +223,7 @@ import ScriptFilter from "@/components/ScriptFilter";
|
||||
import ScriptOperator from "@/components/ScriptOperator";
|
||||
import RegexSortOperator from "@/components/RegexSortOperator";
|
||||
import HandleDuplicateOperator from "@/components/HandleDuplicateOperator";
|
||||
import ResolveDomainOperator from "@/components/ResolveDomainOperator";
|
||||
|
||||
const AVAILABLE_PROCESSORS = {
|
||||
"Flag Operator": {
|
||||
@ -261,6 +262,10 @@ const AVAILABLE_PROCESSORS = {
|
||||
component: "HandleDuplicateOperator",
|
||||
name: "节点去重"
|
||||
},
|
||||
"Resolve Domain Operator": {
|
||||
component: "ResolveDomainOperator",
|
||||
name: "节点域名解析"
|
||||
},
|
||||
"Script Filter": {
|
||||
component: "ScriptFilter",
|
||||
name: "脚本过滤器"
|
||||
@ -291,7 +296,8 @@ export default {
|
||||
RegexDeleteOperator,
|
||||
ScriptFilter,
|
||||
ScriptOperator,
|
||||
HandleDuplicateOperator
|
||||
HandleDuplicateOperator,
|
||||
ResolveDomainOperator
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
|
Loading…
x
Reference in New Issue
Block a user