XuqmGroup-RNSDK/src/update/updateSDK.ts

122 行
3.3 KiB
TypeScript

import AsyncStorage from '@react-native-async-storage/async-storage'
import { Linking, Platform } from 'react-native'
2026-04-21 22:07:29 +08:00
import { getConfig } from '../core/config'
import { apiRequest } from '../core/http'
2026-04-21 22:07:29 +08:00
export interface AppUpdateInfo {
needsUpdate: boolean
versionName?: string
versionCode?: number
downloadUrl?: string
changeLog?: string
forceUpdate?: boolean
appStoreUrl?: string
marketUrl?: string
}
export interface RnUpdateInfo {
needsUpdate: boolean
latestVersion: string
downloadUrl: string
md5: string
minCommonVersion: string
note: string
}
export interface CachedRnBundle {
moduleId: string
version: string
md5: string
downloadedAt: string
source: string
}
function getBundleCacheKey(moduleId: string) {
return `@xuqm_sdk_rn_bundle:${moduleId}`
}
function normalizeDownloadUrl(rawUrl?: string) {
if (!rawUrl) return rawUrl
// Compatible with legacy server config where update base URL already includes /api/v1/updates.
if (rawUrl.includes('/api/v1/updates/api/v1/rn/files/')) {
return rawUrl.replace('/api/v1/updates/api/v1/rn/files/', '/api/v1/rn/files/')
}
if (rawUrl.includes('/files/apk/')) {
try {
const url = new URL(rawUrl)
if (url.pathname.startsWith('/files/apk/')) {
return `${url.origin}/api/v1/updates${url.pathname}${url.search}`
}
} catch {
return rawUrl
}
}
return rawUrl
}
2026-04-21 22:07:29 +08:00
export const UpdateSDK = {
async checkAppUpdate(currentVersionCode: number): Promise<AppUpdateInfo> {
const config = getConfig()
const result = await apiRequest<AppUpdateInfo>('/api/v1/updates/app/check', {
2026-04-21 22:07:29 +08:00
params: {
appId: config.appId,
platform: Platform.OS === 'android' ? 'ANDROID' : 'IOS',
currentVersionCode: String(currentVersionCode),
},
})
return {
...result,
downloadUrl: normalizeDownloadUrl(result.downloadUrl),
}
2026-04-21 22:07:29 +08:00
},
async openAppStore(appStoreUrl?: string, marketUrl?: string): Promise<void> {
const url = Platform.OS === 'ios' ? appStoreUrl : marketUrl
if (url) await Linking.openURL(url)
},
async checkRnUpdate(moduleId: string, currentVersion: string): Promise<RnUpdateInfo> {
const config = getConfig()
const result = await apiRequest<RnUpdateInfo>('/api/v1/rn/update/check', {
2026-04-21 22:07:29 +08:00
params: {
appId: config.appId,
moduleId,
platform: Platform.OS === 'android' ? 'ANDROID' : 'IOS',
currentVersion,
},
})
return {
...result,
downloadUrl: normalizeDownloadUrl(result.downloadUrl) ?? result.downloadUrl,
}
},
async downloadRnBundle(downloadUrl: string): Promise<string> {
const response = await fetch(downloadUrl)
if (!response.ok) {
throw new Error(`Failed to download bundle: ${response.status}`)
}
return response.text()
},
async cacheRnBundle(moduleId: string, version: string, md5: string, source: string): Promise<CachedRnBundle> {
const payload: CachedRnBundle = {
moduleId,
version,
md5,
source,
downloadedAt: new Date().toISOString(),
}
await AsyncStorage.setItem(getBundleCacheKey(moduleId), JSON.stringify(payload))
return payload
},
async getCachedRnBundle(moduleId: string): Promise<CachedRnBundle | null> {
const raw = await AsyncStorage.getItem(getBundleCacheKey(moduleId))
return raw ? JSON.parse(raw) as CachedRnBundle : null
2026-04-21 22:07:29 +08:00
},
}