1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee
react-native-pushy/lib/main.js

339 lines
8.3 KiB
JavaScript
Raw Normal View History

2021-10-09 13:12:09 +08:00
import {
tryBackupEndpoints,
getCheckUrl,
setCustomEndpoints,
2021-10-21 09:29:37 +08:00
getReportUrl,
2021-10-09 13:12:09 +08:00
} from './endpoint';
import {
NativeEventEmitter,
NativeModules,
Platform,
PermissionsAndroid,
} from 'react-native';
export { setCustomEndpoints };
const {
version: v,
} = require('react-native/Libraries/Core/ReactNativeVersion');
const RNVersion = `${v.major}.${v.minor}.${v.patch}`;
2023-02-19 18:20:21 +08:00
const isTurboModuleEnabled = global.__turboModuleProxy != null;
2021-10-09 13:12:09 +08:00
2023-02-20 13:03:12 +08:00
export const PushyModule = isTurboModuleEnabled
? require('./NativeUpdate').default
: NativeModules.Pushy;
if (!PushyModule) {
2021-10-09 13:12:09 +08:00
throw new Error('react-native-update模块无法加载请对照安装文档检查配置。');
}
2023-02-20 13:03:12 +08:00
const PushyConstants = isTurboModuleEnabled
? PushyModule.getConstants()
: PushyModule;
2021-10-09 13:12:09 +08:00
2023-02-19 21:14:03 +08:00
export const downloadRootDir = PushyConstants.downloadRootDir;
export const packageVersion = PushyConstants.packageVersion;
export const currentVersion = PushyConstants.currentVersion;
export const isFirstTime = PushyConstants.isFirstTime;
const rolledBackVersion = PushyConstants.rolledBackVersion;
2021-10-21 09:29:37 +08:00
export const isRolledBack = typeof rolledBackVersion === 'string';
2023-02-19 21:14:03 +08:00
export const buildTime = PushyConstants.buildTime;
let blockUpdate = PushyConstants.blockUpdate;
let uuid = PushyConstants.uuid;
2021-10-09 13:12:09 +08:00
2023-02-19 21:14:03 +08:00
if (Platform.OS === 'android' && !PushyConstants.isUsingBundleUrl) {
2021-10-09 13:12:09 +08:00
throw new Error(
'react-native-update模块无法加载请对照文档检查Bundle URL的配置',
);
}
function setLocalHashInfo(hash, info) {
2023-02-20 13:03:12 +08:00
PushyModule.setLocalHashInfo(hash, JSON.stringify(info));
2021-10-09 13:12:09 +08:00
}
async function getLocalHashInfo(hash) {
2023-02-20 13:03:12 +08:00
return JSON.parse(await PushyModule.getLocalHashInfo(hash));
2021-10-09 13:12:09 +08:00
}
export async function getCurrentVersionInfo() {
2021-10-21 09:29:37 +08:00
return currentVersion ? (await getLocalHashInfo(currentVersion)) || {} : {};
2021-10-09 13:12:09 +08:00
}
2023-02-20 13:03:12 +08:00
const eventEmitter = new NativeEventEmitter(PushyModule);
2021-10-09 13:12:09 +08:00
if (!uuid) {
uuid = require('nanoid/non-secure').nanoid();
2023-02-20 13:03:12 +08:00
PushyModule.setUuid(uuid);
2021-10-09 13:12:09 +08:00
}
function logger(text) {
console.log(`Pushy: ${text}`);
}
2021-10-21 09:29:37 +08:00
function report(hash, type) {
logger(type);
fetch(getReportUrl(), {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
hash,
type,
cInfo,
packageVersion,
buildTime,
}),
}).catch((_e) => {});
}
2021-10-09 13:12:09 +08:00
logger('uuid: ' + uuid);
2021-10-21 09:29:37 +08:00
if (isRolledBack) {
report(rolledBackVersion, 'rollback');
2021-10-09 13:12:09 +08:00
}
export const cInfo = {
pushy: require('../package.json').version,
rn: RNVersion,
os: Platform.OS + ' ' + Platform.Version,
uuid,
};
function assertRelease() {
if (__DEV__) {
2022-04-27 21:52:38 +08:00
throw new Error('react-native-update 只能在 RELEASE 版本中运行.');
2021-10-09 13:12:09 +08:00
}
}
let checkingThrottling = false;
export async function checkUpdate(APPKEY, isRetry) {
assertRelease();
if (checkingThrottling) {
logger('repeated checking, ignored');
return;
}
checkingThrottling = true;
setTimeout(() => {
checkingThrottling = false;
}, 3000);
if (blockUpdate && blockUpdate.until > Date.now() / 1000) {
throw new Error(
`热更新已暂停,原因:${blockUpdate.reason}。请在"${new Date(
blockUpdate.until * 1000,
).toLocaleString()}"之后重试`,
);
}
if (typeof APPKEY !== 'string') {
throw new Error('未检查到合法的APPKEY请查看update.json文件是否正确生成');
}
logger('checking update');
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,
}),
});
} catch (e) {
if (isRetry) {
2022-04-27 21:52:38 +08:00
throw new Error('无法连接更新服务器,请检查网络连接后重试');
2021-10-09 13:12:09 +08:00
}
2022-11-23 18:23:48 +08:00
await tryBackupEndpoints();
2021-10-09 13:12:09 +08:00
return checkUpdate(APPKEY, true);
}
const result = await resp.json();
checkOperation(result.op);
if (resp.status !== 200) {
throw new Error(result.message);
}
return result;
}
function checkOperation(op) {
if (!Array.isArray(op)) {
return;
}
op.forEach((action) => {
if (action.type === 'block') {
blockUpdate = {
reason: action.reason,
until: Math.round((Date.now() + action.duration) / 1000),
};
2023-02-20 13:03:12 +08:00
PushyModule.setBlockUpdate(blockUpdate);
2021-10-09 13:12:09 +08:00
}
});
}
let downloadingThrottling = false;
let downloadedHash;
export async function downloadUpdate(options, eventListeners) {
assertRelease();
if (!options.update) {
return;
}
2021-10-30 14:56:23 +08:00
if (rolledBackVersion === options.hash) {
logger(`rolledback hash ${rolledBackVersion}, ignored`);
2021-10-21 09:29:37 +08:00
return;
}
2021-10-09 13:12:09 +08:00
if (downloadedHash === options.hash) {
logger(`duplicated downloaded hash ${downloadedHash}, ignored`);
return downloadedHash;
2021-10-09 13:12:09 +08:00
}
if (downloadingThrottling) {
logger('repeated downloading, ignored');
return;
}
downloadingThrottling = true;
setTimeout(() => {
downloadingThrottling = false;
}, 3000);
let progressHandler;
if (eventListeners) {
if (eventListeners.onDownloadProgress) {
const downloadCallback = eventListeners.onDownloadProgress;
progressHandler = eventEmitter.addListener(
'RCTPushyDownloadProgress',
(progressData) => {
if (progressData.hash === options.hash) {
downloadCallback(progressData);
}
},
);
}
}
2022-04-27 21:52:38 +08:00
let succeeded = false;
2021-10-09 13:12:09 +08:00
if (options.diffUrl) {
logger('downloading diff');
try {
2023-02-20 13:03:12 +08:00
await PushyModule.downloadPatchFromPpk({
2021-10-09 13:12:09 +08:00
updateUrl: options.diffUrl,
hash: options.hash,
originHash: currentVersion,
});
2022-04-27 21:52:38 +08:00
succeeded = true;
2021-10-09 13:12:09 +08:00
} catch (e) {
2021-10-24 16:19:17 +08:00
logger(`diff error: ${e.message}, try pdiff`);
}
2022-04-27 21:52:38 +08:00
}
if (!succeeded && options.pdiffUrl) {
2021-10-24 16:19:17 +08:00
logger('downloading pdiff');
try {
2023-02-20 13:03:12 +08:00
await PushyModule.downloadPatchFromPackage({
2021-10-09 13:12:09 +08:00
updateUrl: options.pdiffUrl,
hash: options.hash,
});
2022-04-27 21:52:38 +08:00
succeeded = true;
} catch (e) {
logger(`pdiff error: ${e.message}, try full patch`);
}
}
if (!succeeded && options.updateUrl) {
logger('downloading full patch');
try {
2023-02-20 13:03:12 +08:00
await PushyModule.downloadFullUpdate({
2022-04-27 21:52:38 +08:00
updateUrl: options.updateUrl,
hash: options.hash,
});
succeeded = true;
2021-10-24 16:19:17 +08:00
} catch (e) {
2022-04-27 21:52:38 +08:00
logger(`full patch error: ${e.message}`);
2021-10-09 13:12:09 +08:00
}
}
2022-04-27 21:52:38 +08:00
progressHandler && progressHandler.remove();
if (!succeeded) {
report(options.hash, 'error');
throw new Error('all update attempts failed');
}
2021-10-09 13:12:09 +08:00
setLocalHashInfo(options.hash, {
name: options.name,
description: options.description,
metaInfo: options.metaInfo,
});
downloadedHash = options.hash;
return options.hash;
}
function assertHash(hash) {
if (!downloadedHash) {
logger(`no downloaded hash`);
return;
}
if (hash !== downloadedHash) {
logger(`use downloaded hash ${downloadedHash} first`);
return;
}
2021-10-29 13:02:49 +08:00
return true;
2021-10-09 13:12:09 +08:00
}
export function switchVersion(hash) {
assertRelease();
2021-10-29 13:02:49 +08:00
if (assertHash(hash)) {
logger('switchVersion: ' + hash);
2023-02-20 13:03:12 +08:00
PushyModule.reloadUpdate({ hash });
2021-10-29 13:02:49 +08:00
}
2021-10-09 13:12:09 +08:00
}
export function switchVersionLater(hash) {
assertRelease();
2021-10-29 13:02:49 +08:00
if (assertHash(hash)) {
logger('switchVersionLater: ' + hash);
2023-02-20 13:03:12 +08:00
PushyModule.setNeedUpdate({ hash });
2021-10-29 13:02:49 +08:00
}
2021-10-09 13:12:09 +08:00
}
let marked = false;
export function markSuccess() {
assertRelease();
if (marked) {
logger('repeated markSuccess, ignored');
return;
}
marked = true;
2023-02-20 13:03:12 +08:00
PushyModule.markSuccess();
2021-10-21 09:29:37 +08:00
report(currentVersion, 'success');
2021-10-09 13:12:09 +08:00
}
export async function downloadAndInstallApk({ url, onDownloadProgress }) {
logger('downloadAndInstallApk');
if (Platform.OS === 'android' && Platform.Version <= 23) {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
return;
}
} catch (err) {
console.warn(err);
}
}
let hash = Date.now().toString();
let progressHandler;
if (onDownloadProgress) {
progressHandler = eventEmitter.addListener(
'RCTPushyDownloadProgress',
(progressData) => {
if (progressData.hash === hash) {
onDownloadProgress(progressData);
}
},
);
}
2023-02-20 13:03:12 +08:00
await PushyModule.downloadAndInstallApk({
2021-10-09 13:12:09 +08:00
url,
target: 'update.apk',
hash,
});
progressHandler && progressHandler.remove();
}