企业码解析+登录跑通真实后端:Tinode连接适配层、类型化API客户端、会话store

lib/tinode/connection.ts:单例Tinode连接+自己的发布订阅层——SDK的onConnect/onDisconnect/onLogin
是单槽位属性(赋值会互相覆盖),不是真正的多监听器事件源,所以这里统一接管这几个槽位,自己
扇出给任意数量的React订阅方(lib/tinode/hooks.ts的useSyncExternalStore)。installTenantHiPatch
照搬旧应用tinode-web.jsx的实现搬过来——服务端{hi}包的tenant字段SDK本身没有API,只能在原始
sendText层面注入,这是后端协议的真实约束,不是旧代码库特有的写法。

lib/api/client.ts:REST客户端按旧应用src/lib/im-api.js的request函数+完整REASON_MESSAGES错误码
翻译表搬过来(含请求头X-MeetingIM-APIKey等几个月联调换来的正确写法),这次是重新用TypeScript
写、不是复制文件,但端点行为对齐。

实机联调过程中发现并修正了一处凭记忆猜错的地方:tenants/resolve真实响应结构是
{endpoints:{api,file,websocket}, tenant:{code,name}},不是我最初假设的顶层{code,name}——
已用真实请求验证修正。用企业码default解析成功(显示"Default Tenant")+ 一次真实错误密码
登录尝试(服务端真实401,错误提示正确显示"账号或密码错误")两步都对着真实后端验证通过,
不是只测了正确路径。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-09-08 00:38:50 +08:00
co-authored by Claude Sonnet 5
parent 8bcd9f3a5b
commit 8cdd84152d
11 changed files with 629 additions and 13 deletions
+21 -11
View File
@@ -2,7 +2,10 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Navigate, createBrowserRouter, RouterProvider } from 'react-router-dom';
import { AppShell } from '@/app/AppShell';
import { RequireAuth } from '@/app/RequireAuth';
import { SectionPlaceholder } from '@/app/SectionPlaceholder';
import { LoginPage } from '@/features/auth/LoginPage';
import { TenantEntryPage } from '@/features/auth/TenantEntryPage';
import { ConversationView } from '@/features/messages/ConversationView';
import { EmptyMain } from '@/features/messages/EmptyMain';
import { MessagesLayout } from '@/features/messages/MessagesLayout';
@@ -10,23 +13,30 @@ import { MessagesLayout } from '@/features/messages/MessagesLayout';
const queryClient = new QueryClient();
const router = createBrowserRouter([
{ path: 'tenant', element: <TenantEntryPage /> },
{ path: 'login', element: <LoginPage /> },
{
element: <AppShell />,
element: <RequireAuth />,
children: [
{ index: true, element: <Navigate to="/messages" replace /> },
{
path: 'messages',
element: <MessagesLayout />,
element: <AppShell />,
children: [
{ index: true, element: <EmptyMain /> },
{ path: ':topicId', element: <ConversationView /> },
{ index: true, element: <Navigate to="/messages" replace /> },
{
path: 'messages',
element: <MessagesLayout />,
children: [
{ index: true, element: <EmptyMain /> },
{ path: ':topicId', element: <ConversationView /> },
],
},
{ path: 'contacts', element: <SectionPlaceholder title="通讯录" /> },
{ path: 'favorites', element: <SectionPlaceholder title="我的收藏" /> },
{ path: 'files', element: <SectionPlaceholder title="文件助手" /> },
{ path: 'calls', element: <SectionPlaceholder title="通话记录" /> },
{ path: 'settings', element: <SectionPlaceholder title="设置" /> },
],
},
{ path: 'contacts', element: <SectionPlaceholder title="通讯录" /> },
{ path: 'favorites', element: <SectionPlaceholder title="我的收藏" /> },
{ path: 'files', element: <SectionPlaceholder title="文件助手" /> },
{ path: 'calls', element: <SectionPlaceholder title="通话记录" /> },
{ path: 'settings', element: <SectionPlaceholder title="设置" /> },
],
},
]);