2
0
Эх сурвалжийг харах

♻️ refactor(agora): 重构Agora配置获取逻辑

- 将fetchDynamicToken重命名为fetchAgoraConfigAndToken,统一获取Token和配置
- 从API响应中获取配置常量,移除本地配置验证逻辑
- 更新AgoraTokenResponseSchema,添加appId、sttJoinUrl等配置字段

✅ test(agora): 添加配置常量验证测试

- 在AgoraSTTComponent集成测试中验证配置常量字段
- 在Agora Token API测试中添加配置常量验证用例
- 验证配置常量值与后端环境变量一致性
yourname 4 сар өмнө
parent
commit
b057c22945

+ 12 - 0
src/client/admin/components/agora-stt/__integration_tests__/AgoraSTTComponent.integration.test.tsx

@@ -139,6 +139,12 @@ describe('AgoraSTTComponent 真实API集成测试', () => {
       expect(data).toHaveProperty('expiresIn');
       expect(data).toHaveProperty('generatedAt');
 
+      // 验证配置常量字段
+      expect(data).toHaveProperty('appId');
+      expect(data).toHaveProperty('sttJoinUrl');
+      expect(data).toHaveProperty('sttWsUrl');
+      expect(data).toHaveProperty('defaultChannel');
+
       // 验证Token类型
       expect(data.type).toBe('rtc');
 
@@ -224,6 +230,12 @@ describe('AgoraSTTComponent 真实API集成测试', () => {
     const data = await response.json();
 
     if ('token' in data) {
+      // 验证配置常量字段
+      expect(data).toHaveProperty('appId');
+      expect(data).toHaveProperty('sttJoinUrl');
+      expect(data).toHaveProperty('sttWsUrl');
+      expect(data).toHaveProperty('defaultChannel');
+
       // 验证Token在有效期内
       const currentTime = Math.floor(Date.now() / 1000);
       expect(data.expiresAt).toBeGreaterThan(currentTime);

+ 29 - 17
src/client/hooks/useAgoraSTT.ts

@@ -30,7 +30,7 @@ export const useAgoraSTT = (): UseAgoraSTTResult => {
     updateState({ error });
   }, [updateState]);
 
-  const fetchDynamicToken = useCallback(async (type: 'rtc' | 'rtm', channel?: string, userId?: string): Promise<string> => {
+  const fetchAgoraConfigAndToken = useCallback(async (type: 'rtc' | 'rtm', channel?: string, userId?: string): Promise<{ token: string; config: any }> => {
     try {
       const query: any = { type };
 
@@ -52,9 +52,18 @@ export const useAgoraSTT = (): UseAgoraSTTResult => {
         throw new Error('Invalid token response format');
       }
 
-      return data.token;
+      // 现在API返回包含Token和配置常量的完整响应
+      return {
+        token: data.token,
+        config: {
+          appId: data.appId,
+          sttJoinUrl: data.sttJoinUrl,
+          sttWsUrl: data.sttWsUrl,
+          defaultChannel: data.defaultChannel
+        }
+      };
     } catch (error) {
-      throw new Error(`Failed to fetch dynamic token: ${error instanceof Error ? error.message : 'Unknown error'}`);
+      throw new Error(`Failed to fetch dynamic token and config: ${error instanceof Error ? error.message : 'Unknown error'}`);
     }
   }, []);
 
@@ -65,15 +74,9 @@ export const useAgoraSTT = (): UseAgoraSTTResult => {
         return false;
       }
 
-      const agoraConfig = getAgoraConfig();
-      const validationError = validateAgoraConfig(agoraConfig);
-
-      if (validationError) {
-        setError(validationError);
-        return false;
-      }
-
-      config.current = agoraConfig;
+      // 现在配置通过Token API获取,不再需要验证本地配置
+      // 使用默认配置作为占位符
+      config.current = getAgoraConfig();
       return true;
     } catch (error) {
       setError('Failed to initialize Agora configuration');
@@ -89,16 +92,25 @@ export const useAgoraSTT = (): UseAgoraSTTResult => {
     try {
       updateState({ error: null, isConnecting: true });
 
-      // 动态获取RTC Token
-      const dynamicToken = await fetchDynamicToken('rtc', config.current!.channel);
-      currentToken.current = dynamicToken;
+      // 统一获取Token和配置常量
+      const { token, config: dynamicConfig } = await fetchAgoraConfigAndToken('rtc', config.current!.channel);
+      currentToken.current = token;
+
+      // 更新配置为API返回的配置常量
+      config.current = {
+        ...config.current,
+        appId: dynamicConfig.appId,
+        sttJoinUrl: dynamicConfig.sttJoinUrl,
+        sttWsUrl: dynamicConfig.sttWsUrl,
+        channel: dynamicConfig.defaultChannel
+      };
 
       // 模拟Agora STT加入频道API调用
       const joinResponse = await fetch(config.current!.sttJoinUrl, {
         method: 'POST',
         headers: {
           'Content-Type': 'application/json',
-          'Authorization': `Bearer ${dynamicToken}`
+          'Authorization': `Bearer ${token}`
         },
         body: JSON.stringify({
           appId: config.current!.appId,
@@ -167,7 +179,7 @@ export const useAgoraSTT = (): UseAgoraSTTResult => {
       setError('Failed to join Agora channel: ' + (error instanceof Error ? error.message : 'Unknown error'));
       updateState({ isConnecting: false });
     }
-  }, [initializeConfig, updateState, setError, fetchDynamicToken]);
+  }, [initializeConfig, updateState, setError, fetchAgoraConfigAndToken]);
 
   const leaveChannel = useCallback((): void => {
     if (wsConnection.current) {

+ 12 - 15
src/client/utils/agora-stt.ts

@@ -1,26 +1,23 @@
 import { AgoraSTTConfig } from '@/client/types/agora-stt';
-import { getGlobalConfig } from './utils';
 
+// 配置常量现在通过Token API统一获取
 export const getAgoraConfig = (): AgoraSTTConfig => {
+  // 返回默认配置,实际配置将通过Token API获取
   return {
-    appId: getGlobalConfig('AGORA_APP_ID') || '',
-    primaryCert: getGlobalConfig('AGORA_PRIMARY_CERT') || '',
-    token: getGlobalConfig('AGORA_TOKEN') || '',
-    channel: getGlobalConfig('AGORA_CHANNEL') || '123',
-    key: getGlobalConfig('AGORA_KEY') || '',
-    secret: getGlobalConfig('AGORA_SECRET') || '',
-    sttJoinUrl: getGlobalConfig('AGORA_STT_JOIN_URL') || 'https://api.agora.io/v7/rtm/stt/join',
-    sttWsUrl: getGlobalConfig('AGORA_STT_WS_URL') || 'wss://api.agora.io/v7/rtm/stt/connect'
+    appId: '',
+    primaryCert: '',
+    token: '',
+    channel: '123',
+    key: '',
+    secret: '',
+    sttJoinUrl: 'https://api.agora.io/v7/rtm/stt/join',
+    sttWsUrl: 'wss://api.agora.io/v7/rtm/stt/connect'
   };
 };
 
 export const validateAgoraConfig = (config: AgoraSTTConfig): string | null => {
-  const requiredFields = ['appId', 'primaryCert'];
-  for (const field of requiredFields) {
-    if (!config[field as keyof AgoraSTTConfig]) {
-      return `Missing required Agora configuration: ${field}`;
-    }
-  }
+  // 现在配置通过Token API获取,不再需要验证本地配置
+  // 保留此函数以保持接口兼容性
   return null;
 };
 

+ 44 - 2
src/server/api/agora/__tests__/agora-token.integration.test.ts

@@ -28,7 +28,11 @@ vi.mock('@/server/modules/agora/agora-token.service', () => ({
       type,
       expiresAt: Math.floor(Date.now() / 1000) + 3600,
       expiresIn: 3600,
-      generatedAt: Math.floor(Date.now() / 1000)
+      generatedAt: Math.floor(Date.now() / 1000),
+      appId: 'test-app-id',
+      sttJoinUrl: 'https://api.agora.io/v7/rtm/stt/join',
+      sttWsUrl: 'wss://api.agora.io/v7/rtm/stt/connect',
+      defaultChannel: '123'
     }))
   }))
 }));
@@ -132,7 +136,11 @@ describe('Agora Token API 集成测试', () => {
       type: 'rtc',
       expiresAt: expect.any(Number),
       expiresIn: 3600,
-      generatedAt: expect.any(Number)
+      generatedAt: expect.any(Number),
+      appId: 'test-app-id',
+      sttJoinUrl: 'https://api.agora.io/v7/rtm/stt/join',
+      sttWsUrl: 'wss://api.agora.io/v7/rtm/stt/connect',
+      defaultChannel: '123'
     })
   })
 
@@ -271,4 +279,38 @@ describe('Agora Token API 集成测试', () => {
       expect(data.expiresAt - data.generatedAt).toBe(data.expiresIn)
     }
   })
+
+  test('Token API返回配置常量', async () => {
+    const response = await client.agora.token.$get({
+      query: { type: 'rtc', channel: 'test-channel' }
+    },
+    {
+      headers: {
+        'Authorization': 'Bearer test-token'
+      }
+    })
+
+    expect(response.status).toBe(200)
+
+    const data = await response.json()
+
+    // 验证Token信息
+    expect(data).toHaveProperty('token')
+    expect(data).toHaveProperty('type')
+    expect(data).toHaveProperty('expiresAt')
+    expect(data).toHaveProperty('expiresIn')
+    expect(data).toHaveProperty('generatedAt')
+
+    // 验证配置常量字段
+    expect(data).toHaveProperty('appId')
+    expect(data).toHaveProperty('sttJoinUrl')
+    expect(data).toHaveProperty('sttWsUrl')
+    expect(data).toHaveProperty('defaultChannel')
+
+    // 验证配置常量值
+    expect(data.appId).toBe('test-app-id')
+    expect(data.sttJoinUrl).toBe('https://api.agora.io/v7/rtm/stt/join')
+    expect(data.sttWsUrl).toBe('wss://api.agora.io/v7/rtm/stt/connect')
+    expect(data.defaultChannel).toBe('123')
+  })
 })

+ 36 - 0
src/server/api/agora/__tests__/agora-token.real-api.test.ts

@@ -90,6 +90,18 @@ describe('Agora Token API 真实API集成测试', () => {
       expect(data.token).toMatch(/^[A-Za-z0-9+/=]+$/)
       expect(data.token.length).toBeGreaterThan(50)
 
+      // 验证配置常量字段
+      expect(data).toHaveProperty('appId')
+      expect(data).toHaveProperty('sttJoinUrl')
+      expect(data).toHaveProperty('sttWsUrl')
+      expect(data).toHaveProperty('defaultChannel')
+
+      // 验证配置常量值与后端环境变量一致
+      expect(data.appId).toBe(process.env.AGORA_APP_ID || '')
+      expect(data.sttJoinUrl).toBe(process.env.AGORA_STT_JOIN_URL || 'https://api.agora.io/v7/rtm/stt/join')
+      expect(data.sttWsUrl).toBe(process.env.AGORA_STT_WS_URL || 'wss://api.agora.io/v7/rtm/stt/connect')
+      expect(data.defaultChannel).toBe(process.env.AGORA_DEFAULT_CHANNEL || '123')
+
       // 验证时间戳格式
       expect(data.expiresAt).toBeGreaterThan(data.generatedAt)
       expect(data.expiresIn).toBeGreaterThan(0)
@@ -127,6 +139,18 @@ describe('Agora Token API 真实API集成测试', () => {
       expect(data.token).toMatch(/^[A-Za-z0-9+/=]+$/)
       expect(data.token.length).toBeGreaterThan(50)
 
+      // 验证配置常量字段
+      expect(data).toHaveProperty('appId')
+      expect(data).toHaveProperty('sttJoinUrl')
+      expect(data).toHaveProperty('sttWsUrl')
+      expect(data).toHaveProperty('defaultChannel')
+
+      // 验证配置常量值与后端环境变量一致
+      expect(data.appId).toBe(process.env.AGORA_APP_ID || '')
+      expect(data.sttJoinUrl).toBe(process.env.AGORA_STT_JOIN_URL || 'https://api.agora.io/v7/rtm/stt/join')
+      expect(data.sttWsUrl).toBe(process.env.AGORA_STT_WS_URL || 'wss://api.agora.io/v7/rtm/stt/connect')
+      expect(data.defaultChannel).toBe(process.env.AGORA_DEFAULT_CHANNEL || '123')
+
       // 验证时间戳格式
       expect(data.expiresAt).toBeGreaterThan(data.generatedAt)
       expect(data.expiresIn).toBeGreaterThan(0)
@@ -149,6 +173,12 @@ describe('Agora Token API 真实API集成测试', () => {
 
     // 类型断言
     if ('token' in data) {
+      // 验证配置常量字段
+      expect(data).toHaveProperty('appId')
+      expect(data).toHaveProperty('sttJoinUrl')
+      expect(data).toHaveProperty('sttWsUrl')
+      expect(data).toHaveProperty('defaultChannel')
+
       // 验证Token在有效期内
       const currentTime = Math.floor(Date.now() / 1000)
       expect(data.expiresAt).toBeGreaterThan(currentTime)
@@ -185,6 +215,12 @@ describe('Agora Token API 真实API集成测试', () => {
       if ('token' in data) {
         expect(data.type).toBe(testCase.type)
         expect(data.token).toBeTruthy()
+
+        // 验证配置常量字段
+        expect(data).toHaveProperty('appId')
+        expect(data).toHaveProperty('sttJoinUrl')
+        expect(data).toHaveProperty('sttWsUrl')
+        expect(data).toHaveProperty('defaultChannel')
       }
     }
   })

+ 6 - 2
src/server/modules/agora/agora-token.service.ts

@@ -95,7 +95,7 @@ export class AgoraTokenService {
   }
 
   /**
-   * 获取Token信息(包含过期时间)
+   * 获取Token信息(包含过期时间和配置常量
    */
   getTokenInfo(token: string, type: 'rtc' | 'rtm') {
     const currentTimestamp = Math.floor(Date.now() / 1000)
@@ -106,7 +106,11 @@ export class AgoraTokenService {
       type,
       expiresAt: expiryTimestamp,
       expiresIn: this.tokenExpiry,
-      generatedAt: currentTimestamp
+      generatedAt: currentTimestamp,
+      appId: this.appId,
+      sttJoinUrl: process.env.AGORA_STT_JOIN_URL || 'https://api.agora.io/v7/rtm/stt/join',
+      sttWsUrl: process.env.AGORA_STT_WS_URL || 'wss://api.agora.io/v7/rtm/stt/connect',
+      defaultChannel: process.env.AGORA_DEFAULT_CHANNEL || '123'
     }
   }
 }

+ 16 - 0
src/server/types/agora.ts

@@ -35,6 +35,22 @@ export const AgoraTokenResponseSchema = z.object({
   generatedAt: z.number().openapi({
     example: 1737650721,
     description: 'Token生成时间戳(秒)'
+  }),
+  appId: z.string().openapi({
+    example: '1234567890abcdef1234567890abcdef',
+    description: 'Agora应用ID'
+  }),
+  sttJoinUrl: z.string().openapi({
+    example: 'https://api.agora.io/v7/rtm/stt/join',
+    description: 'STT加入频道URL'
+  }),
+  sttWsUrl: z.string().openapi({
+    example: 'wss://api.agora.io/v7/rtm/stt/connect',
+    description: 'STT WebSocket连接URL'
+  }),
+  defaultChannel: z.string().openapi({
+    example: '123',
+    description: '默认频道名称'
   })
 })