- 在 SDKConfig 中新增 signingKey 配置项 - 在 ConfigFile 中添加 signingKey 序列化支持 - 实现 API 客户端请求签名拦截器,支持 HMAC-SHA256 签名 - 在 XuqmSDK 中管理 signingKey 的存储和读取 - 扩展 UpdateApi 接口增加 currentVersionName 参数 - 在 UpdateSDK 中集成设备信息和版本号上报功能 - 为 RN SDK 添加签名密钥管理和 HTTP 请求签名注入 - 实现跨平台的 HMAC-SHA256 签名算法支持
134 行
4.6 KiB
TypeScript
134 行
4.6 KiB
TypeScript
import { initConfigFromRemote, isInitialized, type XuqmInitOptions, type XuqmUserInfo, setUserInfo as setCommonUserInfo, getUserInfo as getCommonUserInfo, getUserId as getCommonUserId } from './config'
|
||
import { DEFAULT_TENANT_PLATFORM_URL } from './constants'
|
||
import { configureHttp } from './http'
|
||
import { decryptConfigFile } from './configCrypto'
|
||
|
||
let _initPromise: Promise<void> | null = null
|
||
let _initResolve: (() => void) | null = null
|
||
let _initReject: ((e: unknown) => void) | null = null
|
||
let _signingKey: string | undefined = undefined
|
||
|
||
function ensureInitPromise(): Promise<void> {
|
||
if (!_initPromise) {
|
||
_initPromise = new Promise((resolve, reject) => {
|
||
_initResolve = resolve
|
||
_initReject = reject
|
||
})
|
||
}
|
||
return _initPromise
|
||
}
|
||
|
||
function markInitialized(): void {
|
||
if (_initResolve) {
|
||
_initResolve()
|
||
_initResolve = null
|
||
_initReject = null
|
||
}
|
||
}
|
||
|
||
function markInitializationFailed(e: unknown): void {
|
||
const reject = _initReject
|
||
// Reset so a retry can call initialize() again
|
||
_initPromise = null
|
||
_initResolve = null
|
||
_initReject = null
|
||
reject?.(e)
|
||
}
|
||
|
||
export const XuqmSDK = {
|
||
/**
|
||
* 方式 B:手动初始化。
|
||
*
|
||
* 请求平台获取该 appKey 的完整服务配置(IM、Push、文件服务等 URL 及开通状态)。
|
||
* 失败时直接抛出,不降级。
|
||
*
|
||
* @param options.appKey 应用标识
|
||
* @param options.platformUrl 平台地址;不传则使用内置默认公有平台地址
|
||
* @param options.debug 是否开启调试日志
|
||
*/
|
||
async initialize(options: XuqmInitOptions): Promise<void> {
|
||
if (isInitialized()) return
|
||
ensureInitPromise()
|
||
const baseUrl = options.platformUrl ?? DEFAULT_TENANT_PLATFORM_URL
|
||
const configUrl = `${baseUrl}/api/sdk/config?appKey=${options.appKey}`
|
||
try {
|
||
const res = await fetch(configUrl)
|
||
if (!res.ok) throw new Error(`[XuqmSDK] Platform config request failed: ${res.status}`)
|
||
const json = await res.json() as Record<string, unknown>
|
||
const remote = (json.data ?? json) as Record<string, unknown>
|
||
initConfigFromRemote(options, {
|
||
apiUrl: remote.apiUrl as string | undefined,
|
||
imWsUrl: remote.imWsUrl as string | undefined,
|
||
fileServiceUrl: remote.fileServiceUrl as string | undefined,
|
||
licenseUrl: remote.licenseUrl as string | undefined,
|
||
imEnabled: remote.imEnabled as boolean | undefined,
|
||
pushEnabled: remote.pushEnabled as boolean | undefined,
|
||
licenseEnabled: remote.licenseEnabled as boolean | undefined,
|
||
bugCollectApiUrl: remote.bugCollectApiUrl as string | undefined,
|
||
bugCollectEnabled: remote.bugCollectEnabled as boolean | undefined,
|
||
}, _signingKey)
|
||
configureHttp({
|
||
baseUrl: (remote.apiUrl as string | undefined) ?? baseUrl,
|
||
debug: options.debug,
|
||
})
|
||
markInitialized()
|
||
} catch (e) {
|
||
markInitializationFailed(e)
|
||
throw e
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 方式 A(内部):从加密配置文件初始化 SDK。
|
||
*
|
||
* 宿主把 .xuqmconfig 文件内容通过 autoInit 模块传入,SDK 自动解密并调用 initialize()。
|
||
* 不对外暴露(不在 index.ts 中 export)。
|
||
*
|
||
* @param encryptedContent 加密配置文件的完整内容(XUQM-CONFIG-V1 或 XUQM-LICENSE-V1 格式)
|
||
*/
|
||
async initWithConfigFile(encryptedContent: string, options?: { debug?: boolean }): Promise<void> {
|
||
if (isInitialized()) return
|
||
const file = await decryptConfigFile(encryptedContent)
|
||
// 配置文件解密后包含 appKey 和 platformUrl(字段名 serverUrl 或 baseUrl)
|
||
const platformUrl = file.serverUrl ?? file.baseUrl ?? undefined
|
||
// 保存 signingKey 供后续请求签名使用
|
||
if (file.signingKey) {
|
||
_signingKey = file.signingKey
|
||
}
|
||
await this.initialize({ appKey: file.appKey, platformUrl, debug: options?.debug })
|
||
},
|
||
|
||
/**
|
||
* 等待初始化完成。在任意子 SDK 内部调用以确保 XuqmSDK 已就绪。
|
||
*/
|
||
async awaitInitialization(): Promise<void> {
|
||
if (isInitialized()) return
|
||
await ensureInitPromise()
|
||
},
|
||
|
||
getUserId(): string | null {
|
||
return getCommonUserId()
|
||
},
|
||
|
||
/**
|
||
* 用户认证核心枢纽。登录成功后调用一次,所有子 SDK(Push、IM、License 等)自动同步状态。
|
||
* 登出时传 null,触发所有子 SDK 登出。
|
||
*/
|
||
setUserInfo(info: XuqmUserInfo | null): void {
|
||
setCommonUserInfo(info)
|
||
},
|
||
|
||
getUserInfo(): XuqmUserInfo | null {
|
||
return getCommonUserInfo()
|
||
},
|
||
}
|
||
|
||
export async function awaitInitialization(): Promise<void> {
|
||
if (isInitialized()) return
|
||
await ensureInitPromise()
|
||
}
|
||
|
||
export function getSigningKey(): string | undefined {
|
||
return _signingKey
|
||
}
|