2026-06-15 10:57:55 +08:00
|
|
|
|
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'
|
2026-05-08 09:27:38 +08:00
|
|
|
|
import { configureHttp } from './http'
|
2026-06-15 01:44:20 +08:00
|
|
|
|
import { decryptConfigFile } from './configCrypto'
|
2026-04-24 16:16:31 +08:00
|
|
|
|
|
2026-05-22 17:57:01 +08:00
|
|
|
|
let _initPromise: Promise<void> | null = null
|
|
|
|
|
|
let _initResolve: (() => void) | null = null
|
2026-06-15 10:04:37 +08:00
|
|
|
|
let _initReject: ((e: unknown) => void) | null = null
|
2026-05-22 17:57:01 +08:00
|
|
|
|
|
|
|
|
|
|
function ensureInitPromise(): Promise<void> {
|
|
|
|
|
|
if (!_initPromise) {
|
2026-06-15 10:04:37 +08:00
|
|
|
|
_initPromise = new Promise((resolve, reject) => {
|
|
|
|
|
|
_initResolve = resolve
|
|
|
|
|
|
_initReject = reject
|
|
|
|
|
|
})
|
2026-05-22 17:57:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
return _initPromise
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function markInitialized(): void {
|
|
|
|
|
|
if (_initResolve) {
|
|
|
|
|
|
_initResolve()
|
|
|
|
|
|
_initResolve = null
|
2026-06-15 10:04:37 +08:00
|
|
|
|
_initReject = null
|
2026-05-22 17:57:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 10:04:37 +08:00
|
|
|
|
function markInitializationFailed(e: unknown): void {
|
|
|
|
|
|
const reject = _initReject
|
|
|
|
|
|
// Reset so a retry can call initialize() again
|
|
|
|
|
|
_initPromise = null
|
|
|
|
|
|
_initResolve = null
|
|
|
|
|
|
_initReject = null
|
|
|
|
|
|
reject?.(e)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-24 16:16:31 +08:00
|
|
|
|
export const XuqmSDK = {
|
|
|
|
|
|
/**
|
2026-06-15 10:57:55 +08:00
|
|
|
|
* 方式 B:手动初始化。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 请求平台获取该 appKey 的完整服务配置(IM、Push、文件服务等 URL 及开通状态)。
|
|
|
|
|
|
* 失败时直接抛出,不降级。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param options.appKey 应用标识
|
|
|
|
|
|
* @param options.platformUrl 平台地址;不传则使用内置默认公有平台地址
|
|
|
|
|
|
* @param options.debug 是否开启调试日志
|
2026-04-25 16:41:19 +08:00
|
|
|
|
*/
|
|
|
|
|
|
async initialize(options: XuqmInitOptions): Promise<void> {
|
|
|
|
|
|
if (isInitialized()) return
|
2026-06-15 10:04:37 +08:00
|
|
|
|
ensureInitPromise()
|
2026-06-15 10:37:57 +08:00
|
|
|
|
const baseUrl = options.platformUrl ?? DEFAULT_TENANT_PLATFORM_URL
|
|
|
|
|
|
const configUrl = `${baseUrl}/api/sdk/config?appKey=${options.appKey}`
|
2026-04-25 16:41:19 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const res = await fetch(configUrl)
|
2026-06-15 10:57:55 +08:00
|
|
|
|
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>
|
2026-04-25 16:41:19 +08:00
|
|
|
|
initConfigFromRemote(options, {
|
2026-06-15 10:57:55 +08:00
|
|
|
|
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,
|
2026-06-16 17:39:18 +08:00
|
|
|
|
bugCollectApiUrl: remote.bugCollectApiUrl as string | undefined,
|
|
|
|
|
|
bugCollectEnabled: remote.bugCollectEnabled as boolean | undefined,
|
2026-04-25 16:41:19 +08:00
|
|
|
|
})
|
2026-05-08 09:27:38 +08:00
|
|
|
|
configureHttp({
|
2026-06-15 10:57:55 +08:00
|
|
|
|
baseUrl: (remote.apiUrl as string | undefined) ?? baseUrl,
|
2026-05-08 09:27:38 +08:00
|
|
|
|
debug: options.debug,
|
|
|
|
|
|
})
|
2026-06-15 10:04:37 +08:00
|
|
|
|
markInitialized()
|
2026-04-25 16:41:19 +08:00
|
|
|
|
} catch (e) {
|
2026-06-15 10:04:37 +08:00
|
|
|
|
markInitializationFailed(e)
|
|
|
|
|
|
throw e
|
2026-04-25 16:41:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-15 10:57:55 +08:00
|
|
|
|
* 方式 A(内部):从加密配置文件初始化 SDK。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
*
|
2026-06-15 10:57:55 +08:00
|
|
|
|
* 宿主把 .xuqmconfig 文件内容通过 autoInit 模块传入,SDK 自动解密并调用 initialize()。
|
|
|
|
|
|
* 不对外暴露(不在 index.ts 中 export)。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
*
|
2026-06-15 10:57:55 +08:00
|
|
|
|
* @param encryptedContent 加密配置文件的完整内容(XUQM-CONFIG-V1 或 XUQM-LICENSE-V1 格式)
|
2026-06-15 01:44:20 +08:00
|
|
|
|
*/
|
|
|
|
|
|
async initWithConfigFile(encryptedContent: string, options?: { debug?: boolean }): Promise<void> {
|
|
|
|
|
|
if (isInitialized()) return
|
|
|
|
|
|
const file = await decryptConfigFile(encryptedContent)
|
2026-06-15 10:57:55 +08:00
|
|
|
|
// 配置文件解密后包含 appKey 和 platformUrl(字段名 serverUrl 或 baseUrl)
|
|
|
|
|
|
const platformUrl = file.serverUrl ?? file.baseUrl ?? undefined
|
|
|
|
|
|
await this.initialize({ appKey: file.appKey, platformUrl, debug: options?.debug })
|
2026-06-15 01:44:20 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-05-22 17:57:01 +08:00
|
|
|
|
/**
|
2026-06-15 10:57:55 +08:00
|
|
|
|
* 等待初始化完成。在任意子 SDK 内部调用以确保 XuqmSDK 已就绪。
|
2026-05-22 17:57:01 +08:00
|
|
|
|
*/
|
|
|
|
|
|
async awaitInitialization(): Promise<void> {
|
|
|
|
|
|
if (isInitialized()) return
|
|
|
|
|
|
await ensureInitPromise()
|
2026-04-24 16:16:31 +08:00
|
|
|
|
},
|
2026-04-28 16:55:12 +08:00
|
|
|
|
|
|
|
|
|
|
getUserId(): string | null {
|
|
|
|
|
|
return getCommonUserId()
|
|
|
|
|
|
},
|
2026-06-15 01:44:20 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-06-15 10:57:55 +08:00
|
|
|
|
* 用户认证核心枢纽。登录成功后调用一次,所有子 SDK(Push、IM、License 等)自动同步状态。
|
|
|
|
|
|
* 登出时传 null,触发所有子 SDK 登出。
|
2026-06-15 01:44:20 +08:00
|
|
|
|
*/
|
|
|
|
|
|
setUserInfo(info: XuqmUserInfo | null): void {
|
|
|
|
|
|
setCommonUserInfo(info)
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getUserInfo(): XuqmUserInfo | null {
|
|
|
|
|
|
return getCommonUserInfo()
|
|
|
|
|
|
},
|
2026-04-24 16:16:31 +08:00
|
|
|
|
}
|
2026-05-22 17:57:01 +08:00
|
|
|
|
|
|
|
|
|
|
export async function awaitInitialization(): Promise<void> {
|
|
|
|
|
|
if (isInitialized()) return
|
|
|
|
|
|
await ensureInitPromise()
|
|
|
|
|
|
}
|