Kaynağa Gözat

✨ feat(aliyun): 重构阿里云IM和RTC Token接口

- 引入@hono/zod-openapi实现OpenAPI规范的API接口
- 定义请求和响应数据Schema,增强类型安全
- 实现IM Token生成接口(/im_token),支持用户角色参数
- 实现RTC Token生成接口(/rtc_token),支持频道ID参数
- 统一错误处理格式,返回包含code和message的错误响应
- 替换Deno.env为process.env,适配Node.js环境
- 重构路由创建方式,使用OpenAPIHono替代基础Hono实例

♻️ refactor(aliyun): 优化环境变量处理和代码结构

- 为环境变量添加默认空字符串,避免undefined问题
- 移除withAuth中间件参数,直接在路由中集成authMiddleware
- 优化变量命名和代码格式,提升可读性
- 统一错误日志输出格式
- 重构路由导出方式,直接导出API实例

✅ test(aliyun): 添加请求参数验证

- 使用zod定义请求参数验证规则
- 为IM Token添加角色(role)参数验证
- 为RTC Token添加频道ID(channelId)参数验证
- 实现用户信息有效性校验逻辑
yourname 5 ay önce
ebeveyn
işleme
cde63543db
1 değiştirilmiş dosya ile 124 ekleme ve 38 silme
  1. 124 38
      src/server/api/aliyun/index.ts

+ 124 - 38
src/server/api/aliyun/index.ts

@@ -1,12 +1,43 @@
-import { Hono } from 'hono'
-import type { Variables , WithAuth } from './middlewares.ts'
+import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+import * as process from 'node:process'
 
 // 配置信息
-const IM_APP_ID = Deno.env.get('IM_APP_ID');
-const IM_APP_KEY = Deno.env.get('IM_APP_KEY');
-const IM_APP_SIGN = Deno.env.get('IM_APP_SIGN');
-const RTC_APP_ID = Deno.env.get('RTC_APP_ID')
-const RTC_APP_KEY = Deno.env.get('RTC_APP_KEY')
+const IM_APP_ID = process.env.IM_APP_ID || '';
+const IM_APP_KEY = process.env.IM_APP_KEY || '';
+const IM_APP_SIGN = process.env.IM_APP_SIGN || '';
+const RTC_APP_ID = process.env.RTC_APP_ID || '';
+const RTC_APP_KEY = process.env.RTC_APP_KEY || '';
+// 请求和响应Schema定义
+const CreateImTokenRequest = z.object({
+  role: z.string().openapi({
+    description: '用户角色',
+    example: 'teacher'
+  })
+});
+
+const ImTokenResponse = z.object({
+  nonce: z.string().openapi({ description: '随机字符串' }),
+  token: z.string().openapi({ description: 'IM认证令牌' }),
+  timestamp: z.number().openapi({ description: '时间戳' }),
+  appId: z.string().openapi({ description: '应用ID' }),
+  appSign: z.string().openapi({ description: '应用签名' })
+});
+
+const CreateRtcTokenRequest = z.object({
+  channelId: z.string().openapi({
+    description: '频道ID',
+    example: 'classroom_123'
+  })
+});
+
+const RtcTokenResponse = z.object({
+  token: z.string().openapi({ description: 'RTC认证令牌' }),
+  timestamp: z.number().openapi({ description: '时间戳' }),
+  appId: z.string().openapi({ description: '应用ID' })
+});
 
 const hex = (buffer: ArrayBuffer): string => {
   const hexCodes = [];
@@ -58,62 +89,117 @@ const generateImToken = async (userId: string, role: string): Promise<{
   }
 };
 
-export function createClassRoomRoutes(withAuth: WithAuth) {
-  const tokenRoutes = new Hono<{ Variables: Variables }>()
+// 创建IM Token路由
+const createImTokenRoute = createRoute({
+  method: 'post',
+  path: '/im_token',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': { schema: CreateImTokenRequest }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '成功生成IM Token',
+      content: { 'application/json': { schema: ImTokenResponse } }
+    },
+    401: {
+      description: '未授权',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
 
-  // 生成IM Token
-  tokenRoutes.post('/im_token', withAuth, async (c) => {
+// 创建RTC Token路由
+const createRtcTokenRoute = createRoute({
+  method: 'post',
+  path: '/rtc_token',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': { schema: CreateRtcTokenRequest }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '成功生成RTC Token',
+      content: { 'application/json': { schema: RtcTokenResponse } }
+    },
+    401: {
+      description: '未授权',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 创建API应用实例
+const app = new OpenAPIHono<AuthContext>()
+  .openapi(createImTokenRoute, async (c) => {
     try {
-      const { role } = await c.req.json()
-      const user = c.get('user')
+      const { role } = c.req.valid('json');
+      const user = c.get('user');
+
       if (!user || typeof user !== 'object' || !('id' in user)) {
-        return c.json({ error: '用户信息无效' }, 401)
+        return c.json({ code: 401, message: '用户信息无效' }, 401);
       }
-      
 
-      // 生成Token
-      const { nonce, token , timestamp } = await generateImToken(user.id.toString(), role);
+      const { nonce, token, timestamp } = await generateImToken(user.id.toString(), role);
 
       return c.json({
-        nonce, 
+        nonce,
         token,
         timestamp,
         appId: IM_APP_ID,
         appSign: IM_APP_SIGN,
-      })
+      }, 200);
     } catch (error) {
-      console.error('生成IM Token失败:', error)
-      return c.json({ error: '生成IM Token失败' }, 500)
+      console.error('生成IM Token失败:', error);
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '生成IM Token失败'
+      }, 500);
     }
   })
-
-  // 生成RTC Token
-  tokenRoutes.post('/rtc_token', withAuth, async (c) => {
+  .openapi(createRtcTokenRoute, async (c) => {
     try {
-      const { channelId } = await c.req.json()
-      const user = c.get('user')
-      
+      const { channelId } = c.req.valid('json');
+      const user = c.get('user');
+
       if (!user || typeof user !== 'object' || !('id' in user)) {
-        return c.json({ error: '用户信息无效' }, 401)
+        return c.json({ code: 401, message: '用户信息无效' }, 401);
       }
-      
+
       if (!RTC_APP_ID || !RTC_APP_KEY) {
-        return c.json({ error: '服务配置不完整' }, 500)
+        return c.json({ code: 500, message: '服务配置不完整' }, 500);
       }
 
-      // 生成Token
-      const { token , timestamp } = await generateRTCToken(channelId, user.id.toString());
+      const { token, timestamp } = await generateRTCToken(channelId, user.id.toString());
 
       return c.json({
         token,
         timestamp,
         appId: RTC_APP_ID,
-      })
+      }, 200);
     } catch (error) {
-      console.error('生成RTC Token失败:', error)
-      return c.json({ error: '生成RTC Token失败' }, 500)
+      console.error('生成RTC Token失败:', error);
+      return c.json({
+        code: 500,
+        message: error instanceof Error ? error.message : '生成RTC Token失败'
+      }, 500);
     }
-  })
+  });
 
-  return tokenRoutes
-}
+export default app;