routes_classroom.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import { Hono } from 'hono'
  2. import type { Variables , WithAuth } from './middlewares.ts'
  3. // 配置信息
  4. const IM_APP_ID = Deno.env.get('IM_APP_ID');
  5. const IM_APP_KEY = Deno.env.get('IM_APP_KEY');
  6. const IM_APP_SIGN = Deno.env.get('IM_APP_SIGN');
  7. const RTC_APP_ID = Deno.env.get('RTC_APP_ID')
  8. const RTC_APP_KEY = Deno.env.get('RTC_APP_KEY')
  9. const hex = (buffer: ArrayBuffer): string => {
  10. const hexCodes = [];
  11. const view = new DataView(buffer);
  12. for (let i = 0; i < view.byteLength; i += 4) {
  13. const value = view.getUint32(i);
  14. const stringValue = value.toString(16);
  15. const padding = '00000000';
  16. const paddedValue = (padding + stringValue).slice(-padding.length);
  17. hexCodes.push(paddedValue);
  18. }
  19. return hexCodes.join('');
  20. };
  21. const generateRTCToken = async (
  22. channelId: string,
  23. userId: string
  24. ): Promise<{
  25. token: string;
  26. timestamp: number;
  27. }> => {
  28. const timestamp = Math.floor(Date.now() / 1000) + 3600 * 3;
  29. const encoder = new TextEncoder();
  30. const data = encoder.encode(`${RTC_APP_ID}${RTC_APP_KEY}${channelId}${userId}${timestamp}`);
  31. const hash = await crypto.subtle.digest('SHA-256', data);
  32. const token = hex(hash);
  33. return {
  34. token,
  35. timestamp
  36. }
  37. };
  38. const generateImToken = async (userId: string, role: string): Promise<{
  39. nonce: string;
  40. token: string;
  41. timestamp: number;
  42. }> => {
  43. const nonce = 'AK_4';
  44. const timestamp = Math.floor(Date.now() / 1000) + 3600 * 3;
  45. const pendingShaStr = `${IM_APP_ID}${IM_APP_KEY}${userId}${nonce}${timestamp}${role}`;
  46. const encoder = new TextEncoder();
  47. const data = encoder.encode(pendingShaStr);
  48. const hash = await crypto.subtle.digest('SHA-256', data);
  49. const token = hex(hash);
  50. return {
  51. nonce,
  52. token,
  53. timestamp
  54. }
  55. };
  56. export function createClassRoomRoutes(withAuth: WithAuth) {
  57. const tokenRoutes = new Hono<{ Variables: Variables }>()
  58. // 生成IM Token
  59. tokenRoutes.post('/im_token', withAuth, async (c) => {
  60. try {
  61. const { role } = await c.req.json()
  62. const user = c.get('user')
  63. if (!user || typeof user !== 'object' || !('id' in user)) {
  64. return c.json({ error: '用户信息无效' }, 401)
  65. }
  66. // 生成Token
  67. const { nonce, token , timestamp } = await generateImToken(user.id.toString(), role);
  68. return c.json({
  69. nonce,
  70. token,
  71. timestamp,
  72. appId: IM_APP_ID,
  73. appSign: IM_APP_SIGN,
  74. })
  75. } catch (error) {
  76. console.error('生成IM Token失败:', error)
  77. return c.json({ error: '生成IM Token失败' }, 500)
  78. }
  79. })
  80. // 生成RTC Token
  81. tokenRoutes.post('/rtc_token', withAuth, async (c) => {
  82. try {
  83. const { channelId } = await c.req.json()
  84. const user = c.get('user')
  85. if (!user || typeof user !== 'object' || !('id' in user)) {
  86. return c.json({ error: '用户信息无效' }, 401)
  87. }
  88. if (!RTC_APP_ID || !RTC_APP_KEY) {
  89. return c.json({ error: '服务配置不完整' }, 500)
  90. }
  91. // 生成Token
  92. const { token , timestamp } = await generateRTCToken(channelId, user.id.toString());
  93. return c.json({
  94. token,
  95. timestamp,
  96. appId: RTC_APP_ID,
  97. })
  98. } catch (error) {
  99. console.error('生成RTC Token失败:', error)
  100. return c.json({ error: '生成RTC Token失败' }, 500)
  101. }
  102. })
  103. return tokenRoutes
  104. }