Files
TrueGrowth/apps/web/public/cdn-config.js

237 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* CDN 智能选择器 - 在主应用加载前运行
*
* 功能:
* 1. 检测可用的 CDN 源
* 2. 选择最快的 CDN
* 3. 将选择结果存储到 localStorage
* 4. 供 Service Worker 使用
*
* 使用方式:
* 在 index.html 的 <head> 中添加:
* <script src="cdn-config.js"></script>
*/
(function() {
var CDN_GLOBAL_KEY = '__OPENTU_CDN__';
var CDN_API_GLOBAL_KEY = '__OPENTU_CDN_API__';
var LEGACY_CDN_GLOBAL_KEY = '__AITU_CDN__';
var LEGACY_CDN_API_GLOBAL_KEY = '__AITU_CDN_API__';
var STORAGE_KEY = 'opentu-cdn-preference';
var LEGACY_STORAGE_KEY = 'aitu-cdn-preference';
function setCDNPreference(value) {
window[CDN_GLOBAL_KEY] = value;
window[LEGACY_CDN_GLOBAL_KEY] = value;
}
function getCDNPreference() {
return window[CDN_GLOBAL_KEY] || window[LEGACY_CDN_GLOBAL_KEY] || null;
}
function isSupportedCDNName(name) {
for (var i = 0; i < CDN_SOURCES.length; i++) {
if (CDN_SOURCES[i].name === name) {
return true;
}
}
return name === 'local';
}
function setCDNApi(api) {
window[CDN_API_GLOBAL_KEY] = api;
window[LEGACY_CDN_API_GLOBAL_KEY] = api;
}
// 开发模式检测 - 本地开发时跳过 CDN 逻辑
var isDevelopment = window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1' ||
window.location.hostname.endsWith('.localhost');
if (isDevelopment) {
setCDNPreference({ cdn: 'local', latency: 0, timestamp: Date.now(), isDevelopment: true });
setCDNApi({
selectBestCDN: function() { return Promise.resolve(getCDNPreference()); },
getCDNBaseUrl: function() { return null; },
clearCDNCache: function() {},
reselectCDN: function() { return Promise.resolve(getCDNPreference()); },
sources: [],
config: {},
isDevelopment: true,
});
return; // 直接返回,不执行 CDN 检测
}
// 配置
var CONFIG = {
packageName: 'aitu-app',
storageKey: STORAGE_KEY,
testTimeout: 5000, // 测试超时时间(毫秒)
cacheExpiry: 3600000, // 缓存过期时间1小时
};
// 运行时 CDN 候选:主 CDN 优先,备用 CDN 兜底
var CDN_SOURCES = [
{
name: 'jsdelivr',
baseUrl: 'https://cdn.jsdelivr.net/npm/' + CONFIG.packageName,
testPath: '/version.json',
},
];
/**
* 测试单个 CDN 的响应时间
*/
function testCDN(source) {
return new Promise(function(resolve) {
var startTime = Date.now();
var testUrl = source.baseUrl + source.testPath + '?t=' + startTime;
var xhr = new XMLHttpRequest();
xhr.timeout = CONFIG.testTimeout;
xhr.onload = function() {
if (xhr.status === 200) {
var latency = Date.now() - startTime;
resolve({ name: source.name, latency: latency, success: true });
} else {
resolve({ name: source.name, latency: Infinity, success: false });
}
};
xhr.onerror = function() {
resolve({ name: source.name, latency: Infinity, success: false });
};
xhr.ontimeout = function() {
resolve({ name: source.name, latency: Infinity, success: false });
};
xhr.open('GET', testUrl, true);
xhr.send();
});
}
/**
* 选择最快的 CDN
*/
function selectBestCDN() {
// 检查缓存
try {
var cached =
localStorage.getItem(CONFIG.storageKey) ||
localStorage.getItem(LEGACY_STORAGE_KEY);
if (cached) {
var data = JSON.parse(cached);
if (
Date.now() - data.timestamp < CONFIG.cacheExpiry &&
isSupportedCDNName(data.cdn)
) {
setCDNPreference(data);
return Promise.resolve(data);
}
if (!isSupportedCDNName(data.cdn)) {
clearCDNCache();
}
}
} catch (e) {
// 忽略解析错误
}
// 并行测试所有 CDN
var tests = CDN_SOURCES.map(testCDN);
return Promise.all(tests).then(function(results) {
// 过滤成功的结果并按延迟排序
var successfulResults = results
.filter(function(r) { return r.success; })
.sort(function(a, b) { return a.latency - b.latency; });
if (successfulResults.length === 0) {
return { cdn: 'local', latency: 0, timestamp: Date.now(), allResults: results };
}
var best = successfulResults[0];
var preference = {
cdn: best.name,
latency: best.latency,
timestamp: Date.now(),
allResults: results,
};
// 缓存结果
try {
localStorage.setItem(CONFIG.storageKey, JSON.stringify(preference));
} catch (e) {
// 忽略存储错误
}
// 暴露到全局变量供 SW 使用
setCDNPreference(preference);
return preference;
});
}
/**
* 获取 CDN 基础 URL
*/
function getCDNBaseUrl(cdnName, version) {
for (var i = 0; i < CDN_SOURCES.length; i++) {
if (CDN_SOURCES[i].name === cdnName) {
var baseUrl = CDN_SOURCES[i].baseUrl;
if (version) {
baseUrl = baseUrl.replace(CONFIG.packageName, CONFIG.packageName + '@' + version);
}
return baseUrl;
}
}
return null;
}
/**
* 清除 CDN 缓存(用于调试)
*/
function clearCDNCache() {
try {
localStorage.removeItem(CONFIG.storageKey);
localStorage.removeItem(LEGACY_STORAGE_KEY);
delete window[CDN_GLOBAL_KEY];
delete window[LEGACY_CDN_GLOBAL_KEY];
} catch (e) {
// 忽略
}
}
/**
* 强制重新选择 CDN
*/
function reselectCDN() {
clearCDNCache();
return selectBestCDN();
}
// 暴露 API
setCDNApi({
selectBestCDN: selectBestCDN,
getCDNBaseUrl: getCDNBaseUrl,
clearCDNCache: clearCDNCache,
reselectCDN: reselectCDN,
sources: CDN_SOURCES,
config: CONFIG,
});
// 页面加载时自动选择 CDN非阻塞
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
selectBestCDN();
});
} else {
// DOM 已加载完成
selectBestCDN();
}
})();