XuqmGroup-RNSDK/packages/common/src/sdk.ts
XuqmGroup 0cf264cdf6 feat(core): 添加请求签名功能并扩展更新接口
- 在 SDKConfig 中新增 signingKey 配置项
- 在 ConfigFile 中添加 signingKey 序列化支持
- 实现 API 客户端请求签名拦截器,支持 HMAC-SHA256 签名
- 在 XuqmSDK 中管理 signingKey 的存储和读取
- 扩展 UpdateApi 接口增加 currentVersionName 参数
- 在 UpdateSDK 中集成设备信息和版本号上报功能
- 为 RN SDK 添加签名密钥管理和 HTTP 请求签名注入
- 实现跨平台的 HMAC-SHA256 签名算法支持
2026-06-19 01:27:56 +08:00

134 行
4.6 KiB
TypeScript

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

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()
},
/**
* 用户认证核心枢纽。登录成功后调用一次,所有子 SDKPush、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
}