diff --git a/README.md b/README.md index e0224c5..61e31a7 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,8 @@ JWT Payload 由 `atob(token.split('.')[1])` 解析,无需额外请求。 ## 运营管理平台(ops-platform)`:5174` > 内部使用,管理所有租户、查看统计数据。独立账号体系。 +> +> 生产环境独立域名:`https://ops.xuqinmin.com/` ### 路由结构 @@ -153,10 +155,10 @@ API 请求通过 `src/api/client.ts` 中的 axios 实例统一附加,401 跳 ```env # tenant-platform -VITE_API_BASE_URL=https://api.xuqm.com/api +VITE_API_BASE_URL=https://dev.xuqinmin.com/api # ops-platform -VITE_API_BASE_URL=https://api.xuqm.com/api +VITE_API_BASE_URL=https://ops.xuqinmin.com/api ``` 未设置时默认代理到 `http://localhost:8081`(Vite dev server proxy)。 diff --git a/docs-site/docs/guide/concepts.md b/docs-site/docs/guide/concepts.md index 7a625e6..b4ef950 100644 --- a/docs-site/docs/guide/concepts.md +++ b/docs-site/docs/guide/concepts.md @@ -21,9 +21,12 @@ UserSig 是 XuqmGroup 的登录鉴权凭证,由业务服务端用 `appSecret` ### 特点 -- 当前版本不过期,只校验 `userId + UserSig` 是否匹配 +- 客户端 IM 登录使用 `userId + UserSig` +- 服务端 SDK 可本地生成和校验 UserSig +- 当前版本支持过期时间,签发时可按业务控制有效期 - `appSecret` **绝不下发到客户端** - 若需撤销权限,可在租户平台重置 `appSecret` 或拉黑账号 +- 注册用户可标记为管理员;管理员账号可用于服务端 SDK / 管理端 REST API ### 签发方式(示例) @@ -32,9 +35,9 @@ UserSig 是 XuqmGroup 的登录鉴权凭证,由业务服务端用 `appSecret` import jwt from 'jsonwebtoken' const userSig = jwt.sign( - { userId: 'user_001', appKey: 'your_app_key' }, + { userId: 'user_001', appKey: 'your_app_key', iat: Math.floor(Date.now() / 1000) }, 'your_app_secret', - { algorithm: 'HS256' } + { algorithm: 'HS256', expiresIn: '180d' } ) ``` diff --git a/docs-site/docs/guide/flow.md b/docs-site/docs/guide/flow.md index b001bf5..0cb4aa2 100644 --- a/docs-site/docs/guide/flow.md +++ b/docs-site/docs/guide/flow.md @@ -43,6 +43,8 @@ XuqmSDK.shared.initialize(config: config) ## 4. 服务端签发 UserSig +服务端可以通过 SDK 本地生成 `UserSig`,也可以通过 IM 管理页生成并校验。 +如果账号需要用于服务端 SDK / 管理端 REST API,请把该注册用户标记为管理员。 ### 签发逻辑(示例) @@ -51,8 +53,9 @@ XuqmSDK.shared.initialize(config: config) import jwt from 'jsonwebtoken' return jwt.sign( - { userId, appKey }, - { algorithm: 'HS256' } + { userId, appKey, iat: Math.floor(Date.now() / 1000) }, + appSecret, + { algorithm: 'HS256', expiresIn: '180d' } ) } ``` @@ -60,10 +63,11 @@ import jwt from 'jsonwebtoken' ```python # Python import jwt +import time def generate_user_sig(user_id: str, app_key: str, app_secret: str) -> str: return jwt.encode( - {"userId": user_id, "appKey": app_key}, + {"userId": user_id, "appKey": app_key, "iat": int(time.time())}, app_secret, algorithm="HS256" ) @@ -72,10 +76,12 @@ def generate_user_sig(user_id: str, app_key: str, app_secret: str) -> str: ```go // Go import "github.com/golang-jwt/jwt/v5" +import "time" token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "userId": userID, "appKey": appKey, + "iat": time.Now().Unix(), }) } ``` diff --git a/docs-site/docs/server/api.md b/docs-site/docs/server/api.md index cd1ae5c..9faeebb 100644 --- a/docs-site/docs/server/api.md +++ b/docs-site/docs/server/api.md @@ -16,9 +16,14 @@ XuqmGroup 的服务端能力分成三类: ### IM 服务 -- `POST /api/im/auth/login` 之前的所有请求,客户端和服务端均需携带 `Authorization: Bearer ` -- `im_jwt` 通过 `POST /api/im/auth/login` 颁发 -- 登录请求需要 `X-App-Timestamp`、`X-App-Nonce`、`X-App-Signature` +- `POST /api/im/auth/login` 返回 `im_jwt` +- 客户端登录使用 `userId + userSig` +- `userSig` 由服务端或 IM 管理页生成,服务端 SDK 可本地生成和校验 +- `GET /api/im/platform-events/token` 由 IM 服务返回平台事件账号 `platform` 的登录 token,用于租户平台实时刷新 +- 服务端管理类调用使用具备管理员权限的 IM 账号登录后获得的 `im_jwt` +- 服务端如果不想先走 SDK,也可以直接调用 `/api/im/admin/users/{userId}/usersig` 生成 `userSig`,再用 `/api/im/auth/login?userSig=...` 换取 `im_jwt` +- 只有被标记为管理员的 IM 注册账号,才允许用于服务端 SDK / 管理端 REST API +- IM 管理页里的 `UserSig` 生成 / 校验工具也只对管理员账号开放 ### Push / Update / Tenant 相关服务 @@ -34,7 +39,9 @@ XuqmGroup 的服务端能力分成三类: | 方法 | 路径 | 说明 | |------|------|------| -| `POST` | `/api/im/auth/login` | IM 登录,返回 JWT Token | +| `POST` | `/api/im/auth/login` | IM 登录,返回 JWT Token;支持 `userSig` | +| `POST` | `/api/im/admin/users/{userId}/usersig` | 生成 UserSig | +| `POST` | `/api/im/admin/users/{userId}/usersig/verify` | 校验 UserSig | | `GET` | `/api/im/accounts/{userId}` | 获取账号资料 | | `PUT` | `/api/im/accounts/{userId}` | 更新账号资料 | | `GET` | `/api/im/accounts/search` | 搜索账号 | @@ -177,6 +184,9 @@ XuqmGroup 的服务端能力分成三类: | `GET` | `/api/push/admin/device-logs` | 设备日志 | | `POST` | `/api/push/admin/test-offline` | 离线推送测试 | +> Push 服务不使用 UserSig;公开接口面向业务服务,管理端接口继续使用平台登录态或服务端管理态。 +> Push SDK 与 REST API 的公开调用面向业务系统,管理员诊断能力通过平台 JWT 或服务端管理态访问。 + ### 内部接口 | 方法 | 路径 | 说明 | diff --git a/docs-site/docs/server/go-sdk.md b/docs-site/docs/server/go-sdk.md index cecdf13..4e210f5 100644 --- a/docs-site/docs/server/go-sdk.md +++ b/docs-site/docs/server/go-sdk.md @@ -2,7 +2,7 @@ XuqmGroup 服务端 Go SDK,按照腾讯云服务端 API 的分类方式封装了: -- IM 账号、消息、群组、好友、会话、黑名单 +- IM 账号、UserSig 生成与登录、消息、群组、好友、会话、黑名单 - 管理端 Webhook、统计、操作日志 - Push 注册与推送 - Update 版本管理、RN Bundle、应用商店提审 @@ -36,11 +36,23 @@ if err != nil { --- +## UserSig + +```go +userSig := client.GenerateUserSig("user_001") +valid := client.VerifyUserSig("user_001", userSig) +login, err := client.LoginWithUserSig("user_001", userSig) +``` + +管理员账号可用于服务端 SDK / 管理端 REST API。 + +--- + ## 能力分类 | 分类 | 主要方法 | |------|----------| -| 登录 | `Login` | +| 登录 | `GenerateUserSig`, `VerifyUserSig`, `Login`, `LoginWithUserSig` | | 账号 | `ImportAccount`, `ImportAccounts`, `DeleteAccount`, `GetProfile`, `UpdateProfile`, `SearchAccounts`, `CheckAccount` | | 消息 | `SendMessage`, `RevokeMessage`, `EditMessage`, `FetchHistory`, `FetchGroupHistory`, `SearchMessages` | | 会话 | `ListConversations`, `SetConversationPinned`, `SetConversationMuted`, `MarkRead`, `SetDraft`, `SetConversationHidden`, `SetConversationGroup`, `DeleteConversation` | @@ -49,7 +61,7 @@ if err != nil { | 群组 | `ListGroups`, `ListPublicGroups`, `SearchGroups`, `GetGroup`, `ListGroupMembers`, `SearchGroupMembers`, `CreateGroup`, `UpdateGroup`, `AddGroupMember`, `AddGroupMembers`, `RemoveGroupMember`, `RemoveGroupMembers`, `SetGroupRole`, `TransferGroupOwner`, `LeaveGroup`, `UpdateGroupAttributes`, `RemoveGroupAttributes`, `MuteGroupMember`, `DismissGroup`, `SendGroupJoinRequest`, `ListGroupJoinRequests`, `AcceptGroupJoinRequest`, `RejectGroupJoinRequest`, `AcceptGroupJoinRequests`, `RejectGroupJoinRequests` | | 管理端 | `QueryUserState`, `KickUsers`, `BatchSendMessage`, `AdminSetMsgRead`, `ImportMessages`, `AdminTransferGroupOwner`, `AdminUpdateGroupAttributes`, `AdminRemoveGroupAttributes`, `AdminGroupReadReceipts` | | Webhook | `ListWebhooks`, `CreateWebhook`, `UpdateWebhook`, `DeleteWebhook`, `VerifyCallbackSignature`, `ParseCallbackEnvelope` | -| Push | `RegisterPushToken`, `SendPush` | +| Push | `RegisterPushToken`, `SendPush`, `PushUserStatus`, `PushDeviceLogs`, `TestOfflinePush` | | Update | `CheckAppUpdate`, `UploadAppVersion`, `PublishAppVersion`, `UnpublishAppVersion`, `GrayAppVersion`, `ListAppVersions`, `CheckRnUpdate`, `UploadRnBundle`, `PublishRnBundle`, `UnpublishRnBundle`, `ListRnBundles` | --- diff --git a/docs-site/docs/server/java-sdk.md b/docs-site/docs/server/java-sdk.md index 1cc1c26..39738a5 100644 --- a/docs-site/docs/server/java-sdk.md +++ b/docs-site/docs/server/java-sdk.md @@ -2,7 +2,7 @@ XuqmGroup 服务端 Java SDK,按腾讯云服务端 API 的思路封装了以下能力: -- IM 账号与登录 +- IM 账号、UserSig 生成与登录 - 消息发送、编辑、撤回、历史查询 - 好友、黑名单、会话、群组 - Webhook、管理端操作、统计 @@ -58,6 +58,18 @@ XuqmImServerSdk client = XuqmImServerSdk.builder() --- +## UserSig + +```java +String userSig = client.generateUserSig("user_001"); +boolean valid = client.verifyUserSig("user_001", userSig); +var login = client.loginWithUserSig("user_001", userSig); +``` + +管理员账号可用于服务端 SDK / 管理端 REST API;建议在 IM 管理页先给对应注册用户打上管理员标记。 + +--- + ## 发送消息 ```java @@ -100,7 +112,7 @@ client.revokeMessage("message_id"); | 分类 | 主要方法 | |------|----------| -| IM 账号 | `login`, `importAccount`, `importAccounts`, `deleteAccount`, `getProfile`, `updateProfile`, `searchAccounts` | +| IM 账号 | `generateUserSig`, `verifyUserSig`, `login`, `loginWithUserSig`, `importAccount`, `importAccounts`, `deleteAccount`, `getProfile`, `updateProfile`, `searchAccounts` | | 消息 | `sendMessage`, `revokeMessage`, `editMessage`, `fetchHistory`, `fetchGroupHistory`, `searchMessages` | | 会话 | `listConversations`, `setConversationPinned`, `setConversationMuted`, `markRead`, `setDraft`, `setConversationHidden`, `setConversationGroup`, `deleteConversation` | | 好友 | `listFriends`, `addFriend`, `addFriends`, `removeFriend`, `removeFriends`, `removeAllFriends`, `setFriendGroup`, `listFriendGroups`, `listFriendsByGroup`, `checkFriends` | @@ -108,7 +120,7 @@ client.revokeMessage("message_id"); | 群组 | `listGroups`, `createGroup`, `updateGroup`, `addGroupMember`, `addGroupMembers`, `removeGroupMember`, `removeGroupMembers`, `setGroupRole`, `transferGroupOwner`, `leaveGroup`, `updateGroupAttributes`, `removeGroupAttributes`, `muteGroupMember`, `dismissGroup`, `sendGroupJoinRequest`, `listGroupJoinRequests`, `acceptGroupJoinRequest`, `rejectGroupJoinRequest`, `acceptGroupJoinRequests`, `rejectGroupJoinRequests` | | 管理端 | `queryUserState`, `kickUsers`, `batchSendMessage`, `adminSetMsgRead`, `importMessages`, `adminTransferGroupOwner`, `adminUpdateGroupAttributes`, `adminRemoveGroupAttributes`, `adminGroupReadReceipts` | | Webhook | `listWebhooks`, `createWebhook`, `updateWebhook`, `deleteWebhook`, `verifyCallbackSignature`, `parseCallbackEnvelope`, 各类 `parse*CallbackPayload` | -| Push | `registerPushToken`, `sendPush` | +| Push | `registerPushToken`, `sendPush`, `pushUserStatus`, `pushDeviceLogs`, `testOfflinePush` | | Update | `checkAppUpdate`, `uploadAppVersion`, `publishAppVersion`, `unpublishAppVersion`, `grayAppVersion`, `listAppVersions`, `checkRnUpdate`, `uploadRnBundle`, `publishRnBundle`, `unpublishRnBundle`, `listRnBundles` | ## 群管理示例 diff --git a/docs-site/docs/server/python-sdk.md b/docs-site/docs/server/python-sdk.md index 7b811e5..19d6e15 100644 --- a/docs-site/docs/server/python-sdk.md +++ b/docs-site/docs/server/python-sdk.md @@ -2,7 +2,7 @@ XuqmGroup 服务端 Python SDK,按照腾讯云服务端 API 的分类方式封装了: -- IM 账号、消息、群组、好友、会话、黑名单 +- IM 账号、UserSig 生成与登录、消息、群组、好友、会话、黑名单 - 管理端 Webhook、统计、操作日志 - Push 注册与推送 - Update 版本管理、RN Bundle、应用商店提审 @@ -33,11 +33,23 @@ sdk = XuqmImServerSdk( --- +## UserSig + +```python +user_sig = sdk.generate_user_sig("user_001") +valid = sdk.verify_user_sig("user_001", user_sig) +login = sdk.login_with_user_sig("user_001", user_sig) +``` + +管理员账号可用于服务端 SDK / 管理端 REST API。 + +--- + ## 能力分类 | 分类 | 主要方法 | |------|----------| -| 登录 | `login` | +| 登录 | `generate_user_sig`, `verify_user_sig`, `login`, `login_with_user_sig` | | 账号 | `import_account`, `import_accounts`, `delete_account`, `get_profile`, `update_profile`, `search_accounts`, `check_account` | | 消息 | `send_message`, `revoke_message`, `edit_message`, `fetch_history`, `fetch_group_history`, `search_messages` | | 会话 | `list_conversations`, `set_conversation_pinned`, `set_conversation_muted`, `mark_read`, `set_draft`, `set_conversation_hidden`, `set_conversation_group`, `delete_conversation` | @@ -46,7 +58,7 @@ sdk = XuqmImServerSdk( | 群组 | `list_groups`, `list_public_groups`, `search_groups`, `get_group`, `list_group_members`, `search_group_members`, `create_group`, `update_group`, `add_group_member`, `add_group_members`, `remove_group_member`, `remove_group_members`, `set_group_role`, `transfer_group_owner`, `leave_group`, `update_group_attributes`, `remove_group_attributes`, `mute_group_member`, `dismiss_group`, `send_group_join_request`, `list_group_join_requests`, `accept_group_join_request`, `reject_group_join_request`, `accept_group_join_requests`, `reject_group_join_requests` | | 管理端 | `query_user_state`, `kick_users`, `batch_send_message`, `admin_set_msg_read`, `import_messages`, `admin_transfer_group_owner`, `admin_update_group_attributes`, `admin_remove_group_attributes`, `admin_group_read_receipts` | | Webhook | `list_webhooks`, `create_webhook`, `update_webhook`, `delete_webhook`, `verify_callback_signature`, `parse_callback_envelope` | -| Push | `register_push_token`, `send_push` | +| Push | `register_push_token`, `send_push`, `push_user_status`, `push_device_logs`, `test_offline_push` | | Update | `check_app_update`, `upload_app_version`, `publish_app_version`, `unpublish_app_version`, `gray_app_version`, `list_app_versions`, `check_rn_update`, `upload_rn_bundle`, `publish_rn_bundle`, `unpublish_rn_bundle`, `list_rn_bundles` | --- diff --git a/docs/ACCESS.md b/docs/ACCESS.md index 2c9298e..76333e6 100644 --- a/docs/ACCESS.md +++ b/docs/ACCESS.md @@ -7,7 +7,7 @@ | 系统 | 地址 | 说明 | |------|------|------| | 租户开放平台 | `https://dev.xuqinmin.com/` | 主账号注册、登录、应用管理 | -| 运营管理平台 | `https://dev.xuqinmin.com/ops/` | 内部运营管理入口 | +| 运营管理平台 | `https://ops.xuqinmin.com/` | 内部运营管理入口 | ## 初始化管理员账号 @@ -41,9 +41,9 @@ | 路径 | 说明 | |------|------| -| `/ops/login` | 运营管理员登录 | -| `/ops/tenants` | 租户列表 | -| `/ops/statistics` | 统计面板 | +| `/login` | 运营管理员登录 | +| `/tenants` | 租户列表 | +| `/statistics` | 统计面板 | ## 主要联调接口 @@ -75,4 +75,7 @@ ```env VITE_API_BASE_URL=https://dev.xuqinmin.com/api + +# ops-platform +VITE_API_BASE_URL=https://ops.xuqinmin.com/api ``` diff --git a/ops-platform/vite.config.ts b/ops-platform/vite.config.ts index d513d99..4f6d892 100644 --- a/ops-platform/vite.config.ts +++ b/ops-platform/vite.config.ts @@ -8,7 +8,7 @@ export default defineConfig(({ mode }) => { const env = loadEnv(mode, new URL('.', import.meta.url).pathname, '') return { - base: env.VITE_APP_BASE || '/ops/', + base: env.VITE_APP_BASE || '/', plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), diff --git a/package.json b/package.json index c697eed..3fd1feb 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "dev:tenant": "yarn workspace tenant-platform dev", "dev:ops": "yarn workspace ops-platform dev", "dev:docs": "yarn workspace docs-site dev", + "build:tenant": "yarn workspace tenant-platform build", + "build:ops": "yarn workspace ops-platform build", "build": "yarn workspaces run build" }, "devDependencies": { diff --git a/tenant-platform/src/api/app.ts b/tenant-platform/src/api/app.ts index eaddb47..29450c5 100644 --- a/tenant-platform/src/api/app.ts +++ b/tenant-platform/src/api/app.ts @@ -147,13 +147,13 @@ export interface PushServiceConfig { export const appApi = { list: () => client.get<{ data: App[] }>('/apps'), - get: (id: string) => client.get<{ data: App }>(`/apps/${id}`), + get: (appKey: string) => client.get<{ data: App }>(`/apps/${appKey}`), create: (data: CreateAppRequest) => client.post<{ data: App }>('/apps', data), - update: (id: string, data: CreateAppRequest) => client.put<{ data: App }>(`/apps/${id}`, data), + update: (appKey: string, data: CreateAppRequest) => client.put<{ data: App }>(`/apps/${appKey}`, data), - delete: (id: string) => client.delete(`/apps/${id}`), + delete: (appKey: string) => client.delete(`/apps/${appKey}`), getServices: (appKey: string) => client.get<{ data: FeatureService[] }>(`/apps/${appKey}/services`), diff --git a/tenant-platform/src/api/im.ts b/tenant-platform/src/api/im.ts index 5708477..234287c 100644 --- a/tenant-platform/src/api/im.ts +++ b/tenant-platform/src/api/im.ts @@ -72,6 +72,7 @@ export interface ImUser { userId: string nickname: string avatar?: string + admin?: boolean status: 'ACTIVE' | 'BANNED' gender: 'UNKNOWN' | 'MALE' | 'FEMALE' createdAt: number @@ -252,7 +253,7 @@ export const imAdminApi = { updateUser( appKey: string, userId: string, - form: { nickname?: string; avatar?: string; gender?: string; status?: string }, + form: { nickname?: string; avatar?: string; gender?: string; status?: string; admin?: boolean }, ) { return imClient.put<{ data: ImUser }>( `/api/im/admin/users/${encodeURIComponent(userId)}`, @@ -261,6 +262,30 @@ export const imAdminApi = { ) }, + generateUserSig( + appKey: string, + userId: string, + form?: { expireSeconds?: number; userBuf?: string }, + ) { + return imClient.post<{ data: { appKey: string; userId: string; userSig: string; issuedAt: number; expiresAt: number; userBuf?: string } }>( + `/api/im/admin/users/${encodeURIComponent(userId)}/usersig`, + form ?? {}, + { params: { appKey } }, + ) + }, + + verifyUserSig( + appKey: string, + userId: string, + userSig: string, + ) { + return imClient.post<{ data: { valid: boolean; appKey: string; userId: string; issuedAt: number; expiresAt: number; userBuf?: string } }>( + `/api/im/admin/users/${encodeURIComponent(userId)}/usersig/verify`, + { userSig }, + { params: { appKey } }, + ) + }, + updateWebhook(appKey: string, webhookId: string, form: WebhookConfigForm) { return imClient.put<{ data: WebhookConfig }>(`/api/im/admin/webhooks/${encodeURIComponent(webhookId)}`, form, { params: { appKey }, @@ -444,6 +469,7 @@ export const imAdminApi = { avatar?: string, gender?: string, status?: string, + admin?: boolean, ) { return imClient.post<{ data: ImUser }>( '/api/im/admin/users', @@ -453,6 +479,7 @@ export const imAdminApi = { avatar, ...(gender ? { gender } : {}), ...(status ? { status } : {}), + ...(admin !== undefined ? { admin } : {}), }, { params: { appKey } }, ) diff --git a/tenant-platform/src/services/storeReviewRealtime.ts b/tenant-platform/src/services/storeReviewRealtime.ts index 1e68557..c6680a1 100644 --- a/tenant-platform/src/services/storeReviewRealtime.ts +++ b/tenant-platform/src/services/storeReviewRealtime.ts @@ -33,12 +33,25 @@ function sdkWsUrl() { } function parseEvent(message: ImMessage): StoreReviewRefreshEvent | null { - if (!['NOTIFY', 'CUSTOM'].includes(message.msgType)) return null try { - const payload = JSON.parse(message.content) as Partial - if (!payload || payload.event !== 'store_review_update') return null - if (!payload.appKey) return null - return payload as StoreReviewRefreshEvent + const raw = JSON.parse(message.content) as Record + const payload = (raw?.payload && typeof raw.payload === 'object' ? raw.payload : raw) as Record + const event = String(payload.event ?? raw?.event ?? '') + const appKey = String(payload.appKey ?? raw?.appKey ?? '') + if (event !== 'store_review_update' || !appKey) return null + return { + event, + appKey, + versionId: String(payload.versionId ?? raw?.versionId ?? ''), + storeType: String(payload.storeType ?? raw?.storeType ?? ''), + reviewState: String(payload.reviewState ?? raw?.reviewState ?? ''), + reviewReason: String(payload.reviewReason ?? raw?.reviewReason ?? ''), + stage: String(payload.stage ?? raw?.stage ?? ''), + batchId: String(payload.batchId ?? raw?.batchId ?? ''), + publishStatus: String(payload.publishStatus ?? raw?.publishStatus ?? ''), + source: String(payload.source ?? raw?.source ?? ''), + timestamp: Number(payload.timestamp ?? raw?.timestamp ?? Date.now()), + } } catch { return null } @@ -52,7 +65,7 @@ export async function connectStoreReviewRealtime(appKey: string, onEvent: (event const res = await client.get<{ data: PlatformEventTokenResponse }>('/im/platform-events/token', { params: { appKey }, }) - const token = res.data.data + const token = res.data.data ?? (res.data as unknown as PlatformEventTokenResponse) init({ appKey, baseUrl: sdkBaseUrl(), @@ -60,6 +73,12 @@ export async function connectStoreReviewRealtime(appKey: string, onEvent: (event debug: import.meta.env.DEV, }) login(token.userId, token.token) + if (import.meta.env.DEV) { + console.debug('[tenant-platform][IM] store review realtime connected token acquired', { + appKey, + userId: token.userId, + }) + } const clientInstance = new ImClient() clientInstance.on('message', (message) => { diff --git a/tenant-platform/src/views/docs/DocsCenterView.vue b/tenant-platform/src/views/docs/DocsCenterView.vue index 8c19aac..3f4782d 100644 --- a/tenant-platform/src/views/docs/DocsCenterView.vue +++ b/tenant-platform/src/views/docs/DocsCenterView.vue @@ -32,8 +32,9 @@ PushSDK.initialize(userId) -
POST /api/auth/login
-POST /api/im/auth/login
+          
POST /api/im/admin/users/{userId}/usersig
+POST /api/im/admin/users/{userId}/usersig/verify
+POST /api/im/auth/login?userSig=...
 POST /api/ops/service-requests/{id}/approve
@@ -46,6 +47,13 @@ POST /api/ops/service-requests/{id}/approve
+
diff --git a/tenant-platform/src/views/im/ImManagementView.vue b/tenant-platform/src/views/im/ImManagementView.vue index 3f01a0d..f0111a2 100644 --- a/tenant-platform/src/views/im/ImManagementView.vue +++ b/tenant-platform/src/views/im/ImManagementView.vue @@ -52,6 +52,12 @@ + + +