dns域名添加
This commit is contained in:
@@ -6,3 +6,4 @@ VITE_BUILD_VCONSOLE = false # 是否开启调试工具 vconsole
|
|||||||
VITE_PARAMETER_ENCRYPTION = 0 # 是否开启参数加密 0关闭 1开启
|
VITE_PARAMETER_ENCRYPTION = 0 # 是否开启参数加密 0关闭 1开启
|
||||||
# VITE_BASE_URL= ["http://172.104.39.32:25508"] # api选线域名,这里必须为数组
|
# VITE_BASE_URL= ["http://172.104.39.32:25508"] # api选线域名,这里必须为数组
|
||||||
VITE_BASE_URL= ["https://d1s84171319hi1.cloudfront.net","https://d2j97wqe01j5pa.cloudfront.net"] # api选线域名,这里必须为数组
|
VITE_BASE_URL= ["https://d1s84171319hi1.cloudfront.net","https://d2j97wqe01j5pa.cloudfront.net"] # api选线域名,这里必须为数组
|
||||||
|
VITE_DNS_DOMAIN = hgdj.mfcg.it.com # DNS 兜底域名:上面线路全挂时解析它的 TXT 记录拿线路
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ VITE_BUILD_DROP_CONSOLE = true # 是否在打包时删除 console 代码
|
|||||||
VITE_BUILD_VCONSOLE = false # 是否开启调试工具 vconsole
|
VITE_BUILD_VCONSOLE = false # 是否开启调试工具 vconsole
|
||||||
VITE_PARAMETER_ENCRYPTION = 1 # 是否开启参数加密 0关闭 1开启
|
VITE_PARAMETER_ENCRYPTION = 1 # 是否开启参数加密 0关闭 1开启
|
||||||
VITE_BASE_URL= ["https://d1s84171319hi1.cloudfront.net","https://d2j97wqe01j5pa.cloudfront.net"] # api选线域名,这里必须为数组
|
VITE_BASE_URL= ["https://d1s84171319hi1.cloudfront.net","https://d2j97wqe01j5pa.cloudfront.net"] # api选线域名,这里必须为数组
|
||||||
|
VITE_DNS_DOMAIN = hgdj.mfcg.it.com # DNS 兜底域名:上面线路全挂时解析它的 TXT 记录拿线路
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ VITE_BUILD_DROP_CONSOLE = true # 是否在打包时删除 console 代码
|
|||||||
VITE_BUILD_VCONSOLE = false # 是否开启调试工具 vconsole
|
VITE_BUILD_VCONSOLE = false # 是否开启调试工具 vconsole
|
||||||
VITE_PARAMETER_ENCRYPTION = 0 # 是否开启参数加密 0关闭 1开启
|
VITE_PARAMETER_ENCRYPTION = 0 # 是否开启参数加密 0关闭 1开启
|
||||||
VITE_BASE_URL= ["https://d1s84171319hi1.cloudfront.net","https://d2j97wqe01j5pa.cloudfront.net"] # api选线域名,这里必须为数组
|
VITE_BASE_URL= ["https://d1s84171319hi1.cloudfront.net","https://d2j97wqe01j5pa.cloudfront.net"] # api选线域名,这里必须为数组
|
||||||
|
VITE_DNS_DOMAIN = hgdj.mfcg.it.com # DNS 兜底域名:上面线路全挂时解析它的 TXT 记录拿线路
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选线:本地缓存线路 → 内置兜底线路 → DNS 解析,同安卓 DetectLineManager。
|
||||||
|
*
|
||||||
|
* 内置线路全挂时,去解析 VITE_DNS_DOMAIN 的 TXT 记录拿线路(运维提供,多条用 _ 分隔),
|
||||||
|
* 解析出来的线路存本地,下次启动优先用它们。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** DNS 解析出来的线路缓存 key(同安卓 StoreKeys.DETECT_LINE) */
|
||||||
|
const LINE_CACHE_KEY = 'detect_line';
|
||||||
|
|
||||||
|
/** 单次 ping / DNS 请求超时 */
|
||||||
|
const TIMEOUT = 1000 * 10;
|
||||||
|
|
||||||
|
/** DoH 服务商,按顺序试:阿里(安卓用的也是这家)→ Cloudflare 兜底 */
|
||||||
|
const DNS_PROVIDERS = ['https://dns.alidns.com/resolve', 'https://cloudflare-dns.com/dns-query'];
|
||||||
|
|
||||||
|
/** 内置兜底线路(.env 的 VITE_BASE_URL) */
|
||||||
|
const builtinLines = (): string[] => {
|
||||||
|
try {
|
||||||
|
const lines = JSON.parse(process.env.VITE_BASE_URL || '[]');
|
||||||
|
return Array.isArray(lines) ? lines.filter(Boolean) : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 上次 DNS 解析出来的线路 */
|
||||||
|
const cachedLines = (): string[] => {
|
||||||
|
try {
|
||||||
|
const lines = JSON.parse(localStorage.getItem(LINE_CACHE_KEY) || '[]');
|
||||||
|
return Array.isArray(lines) ? lines.filter(Boolean) : [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveCachedLines = (lines: string[]): void => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(LINE_CACHE_KEY, JSON.stringify(lines));
|
||||||
|
} catch {
|
||||||
|
// 无痕模式等写不进去,不影响本次选线
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** ping 单条线路,不抛错:通了返回 true */
|
||||||
|
const pingLine = async (line: string): Promise<boolean> => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(line + '/api/app/ping/check', { timeout: TIMEOUT });
|
||||||
|
return data?.code == 200;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 并发 ping 一批线路:谁先成功用谁;全失败返回 '' */
|
||||||
|
const pingBatch = (lines: string[]): Promise<string> =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
if (!lines.length) return resolve('');
|
||||||
|
let failCount = 0;
|
||||||
|
let settled = false;
|
||||||
|
lines.forEach((line) => {
|
||||||
|
void pingLine(line).then((ok) => {
|
||||||
|
if (settled) return;
|
||||||
|
if (ok) {
|
||||||
|
settled = true;
|
||||||
|
resolve(line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (++failCount >= lines.length) {
|
||||||
|
settled = true;
|
||||||
|
resolve('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 DNS TXT 记录拿线路。
|
||||||
|
* TXT 值形如 "https://a.com_https://b.com",带引号,按 _ 拆成多条
|
||||||
|
*/
|
||||||
|
const dnsSolve = async (): Promise<string[]> => {
|
||||||
|
const domain = process.env.VITE_DNS_DOMAIN;
|
||||||
|
if (!domain) return [];
|
||||||
|
for (const provider of DNS_PROVIDERS) {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(provider, {
|
||||||
|
params: { name: domain, type: 'TXT' },
|
||||||
|
headers: { accept: 'application/dns-json' },
|
||||||
|
timeout: TIMEOUT,
|
||||||
|
});
|
||||||
|
const lines: string[] = (data?.Answer || [])
|
||||||
|
.flatMap((record: any) => String(record?.data || '').split('_'))
|
||||||
|
.map((line: string) => line.replace(/"/g, '').trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
if (lines.length) return lines;
|
||||||
|
} catch {
|
||||||
|
// 这家解析不出来就换下一家
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完整选线:缓存线路(没有就用内置线路)全挂时回退 DNS 解析。
|
||||||
|
* 返回通了的线路,全都不通返回 ''
|
||||||
|
*/
|
||||||
|
export const detectLine = async (): Promise<string> => {
|
||||||
|
const cached = cachedLines();
|
||||||
|
const line = await pingBatch(cached.length ? cached : builtinLines());
|
||||||
|
if (line) return line;
|
||||||
|
|
||||||
|
// 本地线路全部失败,回退 DNS 解析
|
||||||
|
const dnsLines = await dnsSolve();
|
||||||
|
if (!dnsLines.length) return '';
|
||||||
|
saveCachedLines(dnsLines);
|
||||||
|
return await pingBatch(dnsLines);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default detectLine;
|
||||||
+5
-49
@@ -1,57 +1,13 @@
|
|||||||
import { useConfigStore } from './../stores/config';
|
import { useConfigStore } from './../stores/config';
|
||||||
import axios from 'axios';
|
import { detectLine } from './detectLine';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试域名
|
* 选线:本地线路 → 全挂则 DNS 解析兜底(实现见 utils/detectLine)
|
||||||
* @param url
|
* @returns 是否选到可用线路
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async function testPing(url: string): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
const ret = await axios({
|
|
||||||
method: 'GET',
|
|
||||||
url: url + '/api/app/ping/check',
|
|
||||||
timeout: 1000 * 10, // 10秒超时
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ret.data.code == 200) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将数组乱序
|
|
||||||
* @param arr
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
function shuffle(arr: Array<string>): Array<string> {
|
|
||||||
let m = arr.length;
|
|
||||||
while (m > 1) {
|
|
||||||
const index = Math.floor(Math.random() * m--);
|
|
||||||
[arr[m], arr[index]] = [arr[index], arr[m]];
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 选线
|
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
export default async function pingCheck(): Promise<boolean> {
|
export default async function pingCheck(): Promise<boolean> {
|
||||||
const baseUrls: string = process.env.VITE_BASE_URL || '[]';
|
const line = await detectLine();
|
||||||
const hosts = JSON.parse(baseUrls);
|
if (!line) return false;
|
||||||
const allLine = shuffle(hosts);
|
|
||||||
for (const line of allLine) {
|
|
||||||
const isOk = await testPing(line);
|
|
||||||
if (isOk) {
|
|
||||||
//选线成功
|
|
||||||
useConfigStore().saveBaseUrl(line);
|
useConfigStore().saveBaseUrl(line);
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-34
@@ -33,9 +33,9 @@ import { reactive } from 'vue';
|
|||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { jumpAdv } from '@/utils/index';
|
import { jumpAdv } from '@/utils/index';
|
||||||
import { useGlobalStore } from '@/stores/global';
|
import { useGlobalStore } from '@/stores/global';
|
||||||
import axios from 'axios';
|
|
||||||
import setTheme from '@/assets/theme/index';
|
import setTheme from '@/assets/theme/index';
|
||||||
import { markHomeOldUserGuidePendingFromSplash } from '@/utils/paymentGuide';
|
import { markHomeOldUserGuidePendingFromSplash } from '@/utils/paymentGuide';
|
||||||
|
import { detectLine } from '@/utils/detectLine';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -50,7 +50,6 @@ const state = reactive({
|
|||||||
checkTimer: <any>null, //检测图片是否加载成功定时器
|
checkTimer: <any>null, //检测图片是否加载成功定时器
|
||||||
checkTime: 3, //图片加载成功检测时间
|
checkTime: 3, //图片加载成功检测时间
|
||||||
isResLock: false, // 请求结果锁
|
isResLock: false, // 请求结果锁
|
||||||
lineFailCount: 0, // 已失败的线路数
|
|
||||||
loading: true, // 选线loading
|
loading: true, // 选线loading
|
||||||
isUpgrade: true, //是否升级
|
isUpgrade: true, //是否升级
|
||||||
});
|
});
|
||||||
@@ -349,16 +348,6 @@ const getConfig = async (): Promise<boolean> => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* @description 选线失败:同安卓,失败不做处理(不弹重试/线路异常弹窗)。
|
|
||||||
* 全部线路都不通就收起「选线中...」,启动流程停在启动页
|
|
||||||
*/
|
|
||||||
const onLineCheckFail = (hosts: any) => {
|
|
||||||
state.lineFailCount++;
|
|
||||||
if (state.lineFailCount >= hosts.length && !state.isResLock) {
|
|
||||||
state.loading = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* 初始化
|
* 初始化
|
||||||
*/
|
*/
|
||||||
@@ -367,30 +356,18 @@ const init = async () => {
|
|||||||
route.query.isAppStore
|
route.query.isAppStore
|
||||||
? localStorage.setItem('isAppStore', route.query.isAppStore.toString())
|
? localStorage.setItem('isAppStore', route.query.isAppStore.toString())
|
||||||
: localStorage.setItem('isAppStore', ''); // 保存是否来自商店包,用于判断是否跳转外链
|
: localStorage.setItem('isAppStore', ''); // 保存是否来自商店包,用于判断是否跳转外链
|
||||||
const baseUrls: string = process.env.VITE_BASE_URL || '[]';
|
// 选线:本地线路 → 全挂则 DNS 解析兜底,同安卓 DetectLineManager
|
||||||
const hosts = JSON.parse(baseUrls);
|
const line = await detectLine();
|
||||||
hosts.forEach((url: any) => {
|
// 选线失败:同安卓,失败不做处理(不弹重试/线路异常弹窗),
|
||||||
axios({
|
// 收起「选线中...」,启动流程停在启动页
|
||||||
method: 'GET',
|
if (!line) {
|
||||||
url: url + '/api/app/ping/check',
|
state.loading = false;
|
||||||
timeout: 1000 * 10, // 10秒超时
|
return;
|
||||||
})
|
}
|
||||||
.then(async (res) => {
|
if (state.isResLock) return;
|
||||||
if (res.data.code == 200) {
|
|
||||||
if (!state.isResLock) {
|
|
||||||
state.isResLock = true;
|
state.isResLock = true;
|
||||||
//选线成功
|
configStore.saveBaseUrl(line);
|
||||||
useConfigStore().saveBaseUrl(url);
|
|
||||||
await ensureAuthAndConfig();
|
await ensureAuthAndConfig();
|
||||||
}
|
|
||||||
} else {
|
|
||||||
onLineCheckFail(hosts);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
onLineCheckFail(hosts);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Vendored
+1
@@ -5,6 +5,7 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_BUILD_SOURCEMAP: string;
|
readonly VITE_BUILD_SOURCEMAP: string;
|
||||||
readonly VITE_BUILD_DROP_CONSOLE: string;
|
readonly VITE_BUILD_DROP_CONSOLE: string;
|
||||||
readonly VITE_BASE_URL: string;
|
readonly VITE_BASE_URL: string;
|
||||||
|
readonly VITE_DNS_DOMAIN: string;
|
||||||
readonly VITE_PARAMETER_ENCRYPTION: string;
|
readonly VITE_PARAMETER_ENCRYPTION: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user