api更换

This commit is contained in:
谢宇宁
2026-10-08 19:27:51 +07:00
parent b4c48e7b46
commit c39daacc6d
7 changed files with 164 additions and 106 deletions
+2 -1
View File
@@ -5,6 +5,7 @@ VITE_BUILD_DROP_CONSOLE = true # 是否在打包时删除 console 代码
VITE_BUILD_VCONSOLE = false # 是否开启调试工具 vconsole
VITE_PARAMETER_ENCRYPTION = 0 # 是否开启参数加密 0关闭 1开启
# VITE_BASE_URL= ["https://91djht.remoces.com"] # api选线域名,这里必须为数组
VITE_BASE_URL= ["https://d2huxeowqcrhxe.cloudfront.net","https://dp539615g9mv7.cloudfront.net","https://u2j5x.com"] # api选线域名,这里必须为数组
VITE_BASE_URL= ["https://d2s4jk18c45pye.cloudfront.net","https://d1ofgr0kx88xsf.cloudfront.net"] # api选线域名,这里必须为数组
VITE_DNS_DOMAIN = hgmj.mfcg.it.com # DNS 兜底域名:上面线路全挂时解析它的 TXT 记录拿线路
# IM 路由域名(拉取 IM 网关配置,须与 imToken 环境一致)
VITE_IM_DYNAMIC_CONFIG_DOMAIN=https://sdk.api.cdflow.cn
+2 -1
View File
@@ -4,6 +4,7 @@ VITE_BUILD_SOURCEMAP = false # 是否在打包时生成 sourcemap
VITE_BUILD_DROP_CONSOLE = true # 是否在打包时删除 console 代码
VITE_BUILD_VCONSOLE = false # 是否开启调试工具 vconsole
VITE_PARAMETER_ENCRYPTION = 1 # 是否开启参数加密 0关闭 1开启
VITE_BASE_URL= ["https://d2huxeowqcrhxe.cloudfront.net","https://dp539615g9mv7.cloudfront.net","https://u2j5x.com"] # api选线域名,这里必须为数组
VITE_BASE_URL= ["https://d2s4jk18c45pye.cloudfront.net","https://d1ofgr0kx88xsf.cloudfront.net"] # api选线域名,这里必须为数组
VITE_DNS_DOMAIN = hgmj.mfcg.it.com # DNS 兜底域名:上面线路全挂时解析它的 TXT 记录拿线路
# IM 路由域名(拉取 IM 网关配置,须与 imToken 环境一致)
VITE_IM_DYNAMIC_CONFIG_DOMAIN=https://sdk.api.cdflow.cn
+1
View File
@@ -5,3 +5,4 @@ VITE_BUILD_DROP_CONSOLE = true # 是否在打包时删除 console 代码
VITE_BUILD_VCONSOLE = false # 是否开启调试工具 vconsole
VITE_PARAMETER_ENCRYPTION = 0 # 是否开启参数加密 0关闭 1开启
VITE_BASE_URL= ["https://91djht.remoces.com"] # api选线域名,这里必须为数组
VITE_DNS_DOMAIN = hgmj.mfcg.it.com # DNS 兜底域名:上面线路全挂时解析它的 TXT 记录拿线路
+121
View File
@@ -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;
+7 -51
View File
@@ -1,57 +1,13 @@
import { useConfigStore } from './../stores/config';
import axios from 'axios';
import { detectLine } from './detectLine';
/**
* 测试域名
* @param url
* @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
* 选线:本地线路 → 全挂则 DNS 解析兜底(实现见 utils/detectLine)
* @returns 是否选到可用线路
*/
export default async function pingCheck(): Promise<boolean> {
const baseUrls: string = process.env.VITE_BASE_URL || '[]';
const hosts = JSON.parse(baseUrls);
const allLine = shuffle(hosts);
for (const line of allLine) {
const isOk = await testPing(line);
if (isOk) {
//选线成功
useConfigStore().saveBaseUrl(line);
return true;
}
}
return false;
const line = await detectLine();
if (!line) return false;
useConfigStore().saveBaseUrl(line);
return true;
}
+30 -53
View File
@@ -39,10 +39,10 @@ import {
// analyticsEventUserRegister,
analyticsEventUserLogin,
} from '@/utils/eventReporter/analytics';
import axios from 'axios';
import setTheme from '@/assets/theme/index';
import { preloadImSdk, warmImIfNeeded } from '@/utils/im/sdk';
import { markHomeOldUserGuidePendingFromSplash } from '@/utils/paymentGuide';
import { detectLine } from '@/utils/detectLine';
const route = useRoute();
const router = useRouter();
@@ -59,8 +59,6 @@ const state = reactive({
isCheckError: false, //是否选线失败
checkLineErrorNum: 0, //选线失败次数
isResLock: false, // 请求结果锁
resResult: <any>[], // 响应结果
pingCheckCount: 0, // 请求的ping失败次数
loading: true, // 选线loading
isUpgrade: true, //是否升级
});
@@ -374,31 +372,26 @@ const getConfig = async (): Promise<boolean> => {
return false;
}
};
const pingErrorPop = (hosts: any) => {
// 只有最后一条域名出错时,判断响应结果不存在true的时候,说明全部不通,即弹出选线失败
state.pingCheckCount++;
if (state.pingCheckCount == hosts.length) {
state.pingCheckCount = 0;
let index = state.resResult.indexOf(true);
if (index == -1) {
if (state.checkLineErrorNum == 1) {
state.isCheckError = true;
return;
}
showDialog({
title: '温馨提示',
message: '选线失败',
confirmButtonText: '重试',
theme: 'round-button',
}).then(() => {
state.checkLineErrorNum++;
appStore.$reset();
userStore.$reset();
configStore.$reset();
init();
});
}
/**
* @description 选线失败:首次弹「选线失败」可重试,重试仍失败则弹进群引导
*/
const onLineCheckFail = () => {
if (state.checkLineErrorNum == 1) {
state.isCheckError = true;
return;
}
showDialog({
title: '温馨提示',
message: '选线失败',
confirmButtonText: '重试',
theme: 'round-button',
}).then(() => {
state.checkLineErrorNum++;
appStore.$reset();
userStore.$reset();
configStore.$reset();
init();
});
};
/**
* 初始化
@@ -409,32 +402,16 @@ const init = async () => {
route.query.isAppStore
? localStorage.setItem('isAppStore', route.query.isAppStore.toString())
: localStorage.setItem('isAppStore', ''); // 保存是否来自商店包,用于判断是否跳转外链
const baseUrls: string = import.meta.env.VITE_BASE_URL || '[]';
const hosts = JSON.parse(baseUrls);
state.resResult = new Array(hosts.length);
hosts.forEach((url: any, index: number) => {
axios({
method: 'GET',
url: url + '/api/app/ping/check',
timeout: 1000 * 10, // 10秒超时
})
.then(async (res) => {
if (res.data.code == 200) {
if (!state.isResLock) {
state.isResLock = true;
state.resResult[index] = true;
//选线成功
useConfigStore().saveBaseUrl(url);
await ensureAuthAndConfig();
}
} else {
pingErrorPop(hosts);
}
})
.catch(() => {
pingErrorPop(hosts);
});
});
// 选线:本地线路 → 全挂则 DNS 解析兜底,同安卓 DetectLineManager
const line = await detectLine();
if (!line) {
onLineCheckFail();
return;
}
if (state.isResLock) return;
state.isResLock = true;
configStore.saveBaseUrl(line);
await ensureAuthAndConfig();
};
/**
+1
View File
@@ -5,6 +5,7 @@ interface ImportMetaEnv {
readonly VITE_BUILD_SOURCEMAP: string;
readonly VITE_BUILD_DROP_CONSOLE: string;
readonly VITE_BASE_URL: string;
readonly VITE_DNS_DOMAIN: string;
readonly VITE_PARAMETER_ENCRYPTION: string;
readonly VITE_IM_DYNAMIC_CONFIG_DOMAIN?: string;
}