lsc 2 months ago
parent
commit
287d29d974
4 changed files with 162 additions and 4 deletions
  1. 4 1
      src/services/config.ts
  2. 1 0
      src/services/course.ts
  3. 152 3
      src/views/Student/index.vue
  4. 5 0
      vite.config.ts

+ 4 - 1
src/services/config.ts

@@ -3,7 +3,7 @@ import message from '@/utils/message'
 import qs from 'qs'
 
 const instance = axios.create({ timeout: 1000 * 300 })
-
+axios.defaults.withCredentials = true
 // POST传参序列化(添加请求拦截器)
 
 instance.interceptors.request.use(
@@ -13,6 +13,9 @@ instance.interceptors.request.use(
     // 修复 config.data 可能为 undefined 的问题
     const data = config.data ?? {}
 
+    // 确保每个请求都带上cookie
+    config.withCredentials = true
+
     // 需要 form-urlencoded 且 data 为数组的情况
     if (
       config.method === 'post' &&

+ 1 - 0
src/services/course.ts

@@ -2,6 +2,7 @@ import axios from './config'
 
 export const API_URL = 'https://pbl.cocorobo.cn/api/pbl/'
 export const yweb_socket = 'wss://yjs.cocorobo.cn'
+// export const yweb_socket = 'wss://yrs.cocorobo.cn'
 
 /**
  * 获取课程详情

+ 152 - 3
src/views/Student/index.vue

@@ -589,6 +589,9 @@ const reconnectInterval = ref(5000) // 重连间隔(毫秒)
 const reconnectTimer = ref<NodeJS.Timeout | null>(null)
 const isConnecting = ref(false)
 const connectionStatus = ref<'disconnected' | 'connecting' | 'connected'>('disconnected')
+// 认证 token 相关变量
+const authToken = ref<string | null>(null)
+const authTokenUpdateTimer = ref<NodeJS.Timeout | null>(null)
 
 // 同步数据最大保留时间(40分钟)
 const SYNC_DATA_MAX_AGE = 40 * 60 * 1000 // 40分钟 = 40 * 60 * 1000毫秒
@@ -1354,7 +1357,48 @@ const processIframeLinks = async () => {
                     console.error(`处理幻灯片 ${slideIndex + 1} 的iframe链接时出错:`, error)
                     return element
                   }
-                } 
+                }
+                else if (iframeSrc.includes('aichat.cocorobo') || iframeSrc.includes('knowledge.cocorobo')) {
+                  hasIframe = true
+                  try {
+                    // 解析URL,处理hash部分
+                    let baseUrl = iframeSrc
+                    let hashPart = ''
+
+                    // 分离base URL和hash部分
+                    if (iframeSrc.includes('#')) {
+                      const parts = iframeSrc.split('#')
+                      baseUrl = parts[0]
+                      hashPart = parts[1]
+                    }
+
+                    // 构建新的hash部分,添加参数
+                    // 使用当前幻灯片索引作为task参数
+                    let newHash = hashPart
+                    if (newHash.includes('?')) {
+                      // 如果hash中已经有查询参数,添加&
+                      newHash += `&courseid=${props.courseid || ''}`
+                    } 
+                    else {
+                      // 如果hash中没有查询参数,添加?
+                      newHash += `?courseid=${props.courseid || ''}`
+                    }
+
+                    // 构建新的URL
+                    const newUrl = `${baseUrl}#${newHash}`
+
+                    console.log(`幻灯片 ${slideIndex + 1} 的iframe链接已更新:`, newUrl)
+                    // 返回更新后的元素
+                    return {
+                      ...element,
+                      url: newUrl
+                    }
+                  }
+                  catch (error) {
+                    console.error(`处理幻灯片 ${slideIndex + 1} 的iframe链接时出错:`, error)
+                    return element
+                  }
+                }
                 else if (toolType == 73) {
                   hasIframe = true
                   
@@ -3262,6 +3306,12 @@ onUnmounted(() => {
     reconnectTimer.value = null
   }
 
+  // 清理认证 token 更新定时器
+  if (authTokenUpdateTimer.value) {
+    clearTimeout(authTokenUpdateTimer.value)
+    authTokenUpdateTimer.value = null
+  }
+
   if (providerSocket.value) {
     providerSocket.value.destroy()
     providerSocket.value = null
@@ -3295,6 +3345,82 @@ onUnmounted(() => {
   handlePageUnload()
 })
 
+// 获取认证 token
+const getAuthToken = async (): Promise<string> => {
+  try {
+    // 使用代理路径避免跨域问题
+    // 开发环境:通过 vite 代理 /yjs-auth/auth/token -> https://yjsredis.cocorobo.cn/auth/token
+    // 生产环境:需要配置服务器代理或使用后端 API
+
+    // 兼容性修复:不直接使用 import.meta.env.DEV
+    let isDev = false
+    // 判断如果有 window 对象且以 localhost/127.0.0.1 开头,则认为是开发环境
+    if (typeof window !== 'undefined') {
+      const hostname = window.location.hostname
+      if (
+        hostname === 'localhost' ||
+        hostname === '127.0.0.1' ||
+        hostname === '::1' ||
+        /^192\.168\.\d+\.\d+$/.test(hostname)
+      ) {
+        isDev = true
+      }
+    }
+
+    let authUrl = ''
+    if (isDev) {
+      // 开发环境使用 vite 代理
+      authUrl = '/yjs-auth/auth/token'
+    }
+    else {
+      // 生产环境:如果服务器有代理则使用代理,否则直接访问(需要服务器配置 CORS)
+      // 或者通过后端 API 获取 token
+      let baseUrl = 'https://yjsredis.cocorobo.cn/'
+      baseUrl = baseUrl.replace(/\/+$/, '')
+      authUrl = `${baseUrl}/auth/token`
+    }
+
+    console.log('🔐 获取认证 token,URL:', authUrl)
+    const response = await axios.get(authUrl)
+    console.log('🔐 获取认证 token 成功', response)
+    
+    return response
+  }
+  catch (error) {
+    console.error('🔐 获取认证 token 失败:', error)
+    throw error
+  }
+}
+
+// 定期更新认证 token
+const updateAuthToken = async () => {
+  try {
+    if (!providerSocket.value) return
+    
+    const newToken = await getAuthToken()
+    authToken.value = newToken
+    // 更新 provider 的 auth 参数
+    if (providerSocket.value.params) {
+      providerSocket.value.params.yauth = newToken
+    }
+    console.log('🔐 认证 token 已更新')
+    
+    // 30分钟后再次更新
+    if (authTokenUpdateTimer.value) {
+      clearTimeout(authTokenUpdateTimer.value)
+    }
+    authTokenUpdateTimer.value = setTimeout(updateAuthToken, 30 * 60 * 1000) as unknown as NodeJS.Timeout
+  }
+  catch (error) {
+    console.error('🔐 更新认证 token 失败:', error)
+    // 失败后1秒重试
+    if (authTokenUpdateTimer.value) {
+      clearTimeout(authTokenUpdateTimer.value)
+    }
+    authTokenUpdateTimer.value = setTimeout(updateAuthToken, 1000) as unknown as NodeJS.Timeout
+  }
+}
+
 // 手动重连
 const manualReconnect = () => {
   if (isConnecting.value) return
@@ -3304,7 +3430,7 @@ const manualReconnect = () => {
 }
 
 // 创建WebSocket连接
-const createWebSocketConnection = () => {
+const createWebSocketConnection = async () => {
   if (!api.yweb_socket || isConnecting.value) return
   
   isConnecting.value = true
@@ -3317,13 +3443,36 @@ const createWebSocketConnection = () => {
       providerSocket.value = null
     }
     
+    // 清理之前的 token 更新定时器
+    if (authTokenUpdateTimer.value) {
+      clearTimeout(authTokenUpdateTimer.value)
+      authTokenUpdateTimer.value = null
+    }
+    
+    // 获取认证 token
+    // try {
+    //   authToken.value = await getAuthToken()
+    //   console.log('🔐 认证 token 获取成功,准备连接 WebSocket')
+    // }
+    // catch (error) {
+    //   console.error('🔐 获取认证 token 失败,连接可能失败:', error)
+    //   connectionStatus.value = 'disconnected'
+    //   isConnecting.value = false
+    //   handleDisconnection()
+    //   return
+    // }
+    
     docSocket.value = new Y.Doc()
     docSocket.value.gc = true
     providerSocket.value = new WebsocketProvider(
       api.yweb_socket,
       'PPT' + props.courseid,
-      docSocket.value
+      docSocket.value,
+      // { params: { yauth: authToken.value } }
     )
+    
+    // 启动定期更新 token
+    // updateAuthToken()
 
     providerSocket.value.on('status', (event: any) => {
       console.log('👉 WebSocket状态:', event.status)

+ 5 - 0
vite.config.ts

@@ -17,6 +17,11 @@ export default defineConfig({
         target: 'https://server.pptist.cn',
         changeOrigin: true,
         rewrite: (path) => path.replace(/^\/api/, ''),
+      },
+      '/yjs-auth': {
+        target: 'https://yjsredis.cocorobo.cn',
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/yjs-auth/, ''),
       }
     }
   },