From 7d128900cd38cba8f89607fc86c7071659ccef21 Mon Sep 17 00:00:00 2001 From: sunnylqm Date: Sat, 28 Oct 2023 14:36:04 +0800 Subject: [PATCH] feat: improve backup endpoints --- lib/endpoint.ts | 82 ++++++++++--------------------------------------- lib/main.ts | 70 +++++++++++++++++++++-------------------- lib/utils.ts | 9 ++++++ package.json | 4 +-- yarn.lock | 8 ++--- 5 files changed, 68 insertions(+), 105 deletions(-) create mode 100644 lib/utils.ts diff --git a/lib/endpoint.ts b/lib/endpoint.ts index 48dc34e..98a14c9 100644 --- a/lib/endpoint.ts +++ b/lib/endpoint.ts @@ -1,75 +1,26 @@ +import { logger } from './utils'; + let currentEndpoint = 'https://update.react-native.cn/api'; +let backupEndpoints: string[] = ['https://update.reactnative.cn/api']; +let backupEndpointsQueryUrl: string | null = null; -function ping(url: string, rejectImmediate?: boolean) { - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.onreadystatechange = (e) => { - if (xhr.readyState !== 4) { - return; - } - if (xhr.status === 200) { - resolve(url); - } else { - rejectImmediate ? reject() : setTimeout(reject, 5000); - } - }; - xhr.open('HEAD', url); - xhr.send(); - xhr.timeout = 5000; - xhr.ontimeout = reject; - }); -} - -function logger(...args: any[]) { - console.log('Pushy: ', ...args); -} - -let backupEndpoints: string[] = []; -let backupEndpointsQueryUrl: string | null = - 'https://cdn.jsdelivr.net/gh/reactnativecn/react-native-pushy@master/endpoints.json'; - -export async function tryBackupEndpoints() { - if (!backupEndpoints.length && !backupEndpointsQueryUrl) { - return; - } - try { - await ping(getStatusUrl(), true); - logger('current endpoint ok', currentEndpoint); - return; - } catch (e) { - logger('current endpoint failed', currentEndpoint); - } - if (!backupEndpoints.length && backupEndpointsQueryUrl) { +export async function updateBackupEndpoints() { + if (backupEndpointsQueryUrl) { try { const resp = await fetch(backupEndpointsQueryUrl); - backupEndpoints = await resp.json(); - logger('get remote endpoints:', backupEndpoints); + const remoteEndpoints = await resp.json(); + if (Array.isArray(remoteEndpoints)) { + backupEndpoints = Array.from( + new Set([...backupEndpoints, ...remoteEndpoints]), + ); + logger('fetch remote endpoints:', remoteEndpoints); + logger('merged backup endpoints:', backupEndpoints); + } } catch (e) { - logger('get remote endpoints failed'); - return; + logger('fetch remote endpoints failed'); } } - await pickFatestAvailableEndpoint(); -} - -async function pickFatestAvailableEndpoint(endpoints = backupEndpoints) { - const fastestEndpoint = await Promise.race( - endpoints.map(pingAndReturnEndpoint), - ); - if (typeof fastestEndpoint === 'string') { - logger(`pick endpoint: ${fastestEndpoint}`); - currentEndpoint = fastestEndpoint; - } else { - logger('all remote endpoints failed'); - } -} - -async function pingAndReturnEndpoint(endpoint = currentEndpoint) { - return ping(getStatusUrl(endpoint)).then(() => endpoint); -} - -function getStatusUrl(endpoint = currentEndpoint) { - return `${endpoint}/status`; + return backupEndpoints; } export function getCheckUrl(APPKEY, endpoint = currentEndpoint) { @@ -95,7 +46,6 @@ export function setCustomEndpoints({ backupEndpointsQueryUrl = null; if (Array.isArray(backups) && backups.length > 0) { backupEndpoints = backups; - pickFatestAvailableEndpoint(); } if (typeof backupQueryUrl === 'string') { backupEndpointsQueryUrl = backupQueryUrl; diff --git a/lib/main.ts b/lib/main.ts index 5d0fd46..8c55176 100644 --- a/lib/main.ts +++ b/lib/main.ts @@ -1,5 +1,5 @@ import { - tryBackupEndpoints, + updateBackupEndpoints, getCheckUrl, setCustomEndpoints, } from './endpoint'; @@ -16,6 +16,7 @@ import { UpdateAvailableResult, UpdateEventsListener, } from './type'; +import { assertRelease, logger } from './utils'; export { setCustomEndpoints }; const { version: v, @@ -74,10 +75,6 @@ if (!uuid) { PushyModule.setUuid(uuid); } -function logger(...args: string[]) { - console.log('Pushy: ', ...args); -} - const noop = () => {}; let reporter: UpdateEventsListener = noop; @@ -125,16 +122,10 @@ export const cInfo = { uuid, }; -function assertRelease() { - if (__DEV__) { - throw new Error('react-native-update 只能在 RELEASE 版本中运行.'); - } -} - let lastChecking; const empty = {}; let lastResult: CheckResult; -export async function checkUpdate(APPKEY: string, isRetry?: boolean) { +export async function checkUpdate(APPKEY: string) { assertRelease(); const now = Date.now(); if (lastResult && lastChecking && now - lastChecking < 1000 * 60) { @@ -152,31 +143,44 @@ export async function checkUpdate(APPKEY: string, isRetry?: boolean) { return lastResult || empty; } report({ type: 'checking' }); + const fetchPayload = { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + packageVersion, + hash: currentVersion, + buildTime, + cInfo, + }), + }; let resp; try { - resp = await fetch(getCheckUrl(APPKEY), { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - packageVersion, - hash: currentVersion, - buildTime, - cInfo, - }), - }); + resp = await fetch(getCheckUrl(APPKEY), fetchPayload); } catch (e) { - if (isRetry) { - report({ - type: 'errorChecking', - message: '无法连接更新服务器,请检查网络连接后重试', - }); - return lastResult || empty; + report({ + type: 'errorChecking', + message: '无法连接主更新服务器,尝试备用节点', + }); + const backupEndpoints = await updateBackupEndpoints(); + if (backupEndpoints) { + try { + resp = await Promise.race( + backupEndpoints.map((endpoint) => + fetch(getCheckUrl(APPKEY, endpoint), fetchPayload), + ), + ); + } catch {} } - await tryBackupEndpoints(); - return checkUpdate(APPKEY, true); + } + if (!resp) { + report({ + type: 'errorChecking', + message: '无法连接更新服务器,请检查网络连接后重试', + }); + return lastResult || empty; } const result: CheckResult = await resp.json(); diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..9b024de --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,9 @@ +export function logger(...args: any[]) { + console.log('Pushy: ', ...args); +} + +export function assertRelease() { + if (__DEV__) { + throw new Error('react-native-update 只能在 RELEASE 版本中运行.'); + } +} diff --git a/package.json b/package.json index 27843e8..57773c9 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "url": "https://github.com/reactnativecn/react-native-pushy/issues" }, "peerDependencies": { - "react-native": ">=0.27.0" + "react-native": ">=0.57.0" }, "homepage": "https://github.com/reactnativecn/react-native-pushy#readme", "dependencies": { @@ -63,6 +63,6 @@ "jest": "^29.2.1", "pod-install": "^0.1.37", "ts-jest": "^29.0.3", - "typescript": "^4.1.3" + "typescript": "^5.2.2" } } diff --git a/yarn.lock b/yarn.lock index ff25d0c..a79a488 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6023,10 +6023,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.1.3: - version "4.9.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6"