Просмотр исходного кода

✨ feat(agora): 添加Agora实时语音转录Token管理API

- 创建Agora相关路由文件和目录结构
- 实现Agora Token生成接口,支持RTC和RTM两种Token类型
- 定义Agora Token请求和响应数据模型
- 添加OpenAPI文档配置,提供API说明
- 集成认证中间件确保接口安全访问
- 实现Token参数验证和错误处理逻辑
yourname 4 месяцев назад
Родитель
Сommit
e57e67f396
4 измененных файлов с 156 добавлено и 0 удалено
  1. 3 0
      src/server/api.ts
  2. 21 0
      src/server/api/agora/index.ts
  3. 90 0
      src/server/api/agora/token/get.ts
  4. 42 0
      src/server/types/agora.ts

+ 3 - 0
src/server/api.ts

@@ -5,6 +5,7 @@ import usersRouter from './api/users/index'
 import authRoute from './api/auth/index'
 import rolesRoute from './api/roles/index'
 import fileRoutes from './api/files/index'
+import agoraRoutes from './api/agora/index'
 import { AuthContext } from './types/context'
 import { AppDataSource } from './data-source'
 import { Hono } from 'hono'
@@ -110,11 +111,13 @@ export const userRoutes = api.route('/api/v1/users', usersRouter)
 export const authRoutes = api.route('/api/v1/auth', authRoute)
 export const roleRoutes = api.route('/api/v1/roles', rolesRoute)
 export const fileApiRoutes = api.route('/api/v1/files', fileRoutes)
+export const agoraApiRoutes = api.route('/api/v1/agora', agoraRoutes)
 
 export type AuthRoutes = typeof authRoutes
 export type UserRoutes = typeof userRoutes
 export type RoleRoutes = typeof roleRoutes
 export type FileRoutes = typeof fileApiRoutes
+export type AgoraRoutes = typeof agoraApiRoutes
 
 app.route('/', api)
 export default app

+ 21 - 0
src/server/api/agora/index.ts

@@ -0,0 +1,21 @@
+import { OpenAPIHono } from '@hono/zod-openapi'
+import tokenRoutes from './token/get'
+
+const app = new OpenAPIHono()
+
+// 注册Agora路由
+app.route('/token', tokenRoutes)
+
+// OpenAPI文档配置
+app.doc('/doc', {
+  openapi: '3.0.0',
+  info: {
+    title: 'Agora API',
+    version: '1.0.0',
+    description: 'Agora实时语音转录Token管理API'
+  }
+})
+
+export default app
+
+export type AgoraRoutes = typeof app

+ 90 - 0
src/server/api/agora/token/get.ts

@@ -0,0 +1,90 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi'
+import { ErrorSchema } from '@/server/utils/errorHandler'
+import { authMiddleware } from '@/server/middleware/auth.middleware'
+import { AuthContext } from '@/server/types/context'
+import { AgoraTokenService } from '@/server/modules/agora/agora-token.service'
+import { AgoraTokenRequestSchema, AgoraTokenResponseSchema } from '@/server/types/agora'
+
+const agoraTokenService = new AgoraTokenService()
+
+const routeDef = createRoute({
+  method: 'get',
+  path: '/token',
+  middleware: authMiddleware,
+  request: {
+    query: AgoraTokenRequestSchema
+  },
+  responses: {
+    200: {
+      description: 'Token生成成功',
+      content: {
+        'application/json': {
+          schema: AgoraTokenResponseSchema
+        }
+      }
+    },
+    400: {
+      description: '请求参数错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    401: {
+      description: '未授权',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    500: {
+      description: '服务器内部错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+})
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { type, channel, userId } = c.req.valid('query')
+
+    // 验证参数
+    agoraTokenService.validateTokenParams(type, channel, userId)
+
+    let token: string
+
+    if (type === 'rtc') {
+      // 生成RTC Token
+      token = agoraTokenService.generateRtcToken(channel!, userId || '0')
+    } else {
+      // 生成RTM Token
+      token = agoraTokenService.generateRtmToken(userId!)
+    }
+
+    const tokenInfo = agoraTokenService.getTokenInfo(token, type)
+
+    return c.json(tokenInfo, 200)
+
+  } catch (error) {
+    if (error instanceof Error && error.message.includes('参数')) {
+      return c.json(
+        {
+          code: 400,
+          message: error.message
+        },
+        400
+      )
+    }
+
+    // 重新抛出其他错误,由错误处理中间件处理
+    throw error
+  }
+})
+
+export default app

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

@@ -0,0 +1,42 @@
+import { z } from '@hono/zod-openapi'
+
+export const AgoraTokenRequestSchema = z.object({
+  type: z.enum(['rtc', 'rtm']).openapi({
+    example: 'rtc',
+    description: 'Token类型:rtc用于语音转文字,rtm用于实时消息'
+  }),
+  channel: z.string().optional().openapi({
+    example: 'test-channel',
+    description: '频道名称(RTC Token必需)'
+  }),
+  userId: z.string().optional().openapi({
+    example: 'user123',
+    description: '用户ID(RTM Token必需)'
+  })
+})
+
+export const AgoraTokenResponseSchema = z.object({
+  token: z.string().openapi({
+    example: '006AEA6D93A24E4A9D3B4A29A8C2D6B2IAA...',
+    description: 'Agora Token'
+  }),
+  type: z.enum(['rtc', 'rtm']).openapi({
+    example: 'rtc',
+    description: 'Token类型'
+  }),
+  expiresAt: z.number().openapi({
+    example: 1737654321,
+    description: 'Token过期时间戳(秒)'
+  }),
+  expiresIn: z.number().openapi({
+    example: 3600,
+    description: 'Token有效期(秒)'
+  }),
+  generatedAt: z.number().openapi({
+    example: 1737650721,
+    description: 'Token生成时间戳(秒)'
+  })
+})
+
+export type AgoraTokenRequest = z.infer<typeof AgoraTokenRequestSchema>
+export type AgoraTokenResponse = z.infer<typeof AgoraTokenResponseSchema>