mirror of
http://112.124.100.131/huang.ze/ebiz-dify-ai.git
synced 2025-12-22 17:26:54 +08:00
feat(web): 添加 iframe 通信支持
为应用添加 iframe 嵌入功能的通信支持
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import { activeMessageChannel } from '@/utils/message-channel'
|
||||||
|
|
||||||
class StorageMock {
|
class StorageMock {
|
||||||
data: Record<string, string>
|
data: Record<string, string>
|
||||||
|
|
||||||
@@ -30,7 +32,7 @@ try {
|
|||||||
localStorage = globalThis.localStorage
|
localStorage = globalThis.localStorage
|
||||||
sessionStorage = globalThis.sessionStorage
|
sessionStorage = globalThis.sessionStorage
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (_) {
|
||||||
localStorage = new StorageMock()
|
localStorage = new StorageMock()
|
||||||
sessionStorage = new StorageMock()
|
sessionStorage = new StorageMock()
|
||||||
}
|
}
|
||||||
@@ -43,6 +45,9 @@ Object.defineProperty(globalThis, 'sessionStorage', {
|
|||||||
value: sessionStorage,
|
value: sessionStorage,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 激活消息通道
|
||||||
|
activeMessageChannel()
|
||||||
|
|
||||||
const BrowserInitor = ({
|
const BrowserInitor = ({
|
||||||
children,
|
children,
|
||||||
}: { children: React.ReactNode }) => {
|
}: { children: React.ReactNode }) => {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { removeAccessToken } from '@/app/components/share/utils'
|
|||||||
import type { FetchOptionType, ResponseError } from './fetch'
|
import type { FetchOptionType, ResponseError } from './fetch'
|
||||||
import { ContentType, base, baseOptions, getAccessToken } from './fetch'
|
import { ContentType, base, baseOptions, getAccessToken } from './fetch'
|
||||||
import { asyncRunSafe } from '@/utils'
|
import { asyncRunSafe } from '@/utils'
|
||||||
|
|
||||||
const TIME_OUT = 100000
|
const TIME_OUT = 100000
|
||||||
|
|
||||||
export type IOnDataMoreInfo = {
|
export type IOnDataMoreInfo = {
|
||||||
@@ -154,6 +155,7 @@ const handleStream = (
|
|||||||
let buffer = ''
|
let buffer = ''
|
||||||
let bufferObj: Record<string, any>
|
let bufferObj: Record<string, any>
|
||||||
let isFirstMessage = true
|
let isFirstMessage = true
|
||||||
|
|
||||||
function read() {
|
function read() {
|
||||||
let hasError = false
|
let hasError = false
|
||||||
reader?.read().then((result: any) => {
|
reader?.read().then((result: any) => {
|
||||||
@@ -281,6 +283,7 @@ const handleStream = (
|
|||||||
read()
|
read()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
read()
|
read()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +388,7 @@ export const ssePost = (
|
|||||||
options.body = JSON.stringify(body)
|
options.body = JSON.stringify(body)
|
||||||
|
|
||||||
const accessToken = getAccessToken(isPublicAPI)
|
const accessToken = getAccessToken(isPublicAPI)
|
||||||
;(options.headers as Headers).set('Authorization', `Bearer ${accessToken}`)
|
;(options.headers as Headers).set('Authorization', `Bearer ${accessToken}`)
|
||||||
|
|
||||||
globalThis.fetch(urlWithPrefix, options as RequestInit)
|
globalThis.fetch(urlWithPrefix, options as RequestInit)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
@@ -457,17 +460,22 @@ export const ssePost = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// base request
|
// base request
|
||||||
export const request = async<T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
|
export const request = async <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
|
||||||
|
const baseURL = process.env.NEXT_PUBLIC_BASE_URL
|
||||||
try {
|
try {
|
||||||
const otherOptionsForBaseFetch = otherOptions || {}
|
const otherOptionsForBaseFetch = otherOptions || {}
|
||||||
const [err, resp] = await asyncRunSafe<T>(baseFetch(url, options, otherOptionsForBaseFetch))
|
const [err, resp] = await asyncRunSafe<T>(baseFetch(url, options, otherOptionsForBaseFetch))
|
||||||
if (err === null)
|
if (err === null)
|
||||||
return resp
|
return resp
|
||||||
const errResp: Response = err as any
|
const errResp: Response = err as any
|
||||||
|
// 状态未认证,401, 重定向到 signin 界面
|
||||||
if (errResp.status === 401) {
|
if (errResp.status === 401) {
|
||||||
const [parseErr, errRespData] = await asyncRunSafe<ResponseError>(errResp.json())
|
const [parseErr, errRespData] = await asyncRunSafe<ResponseError>(errResp.json())
|
||||||
const loginUrl = `${globalThis.location.origin}/signin`
|
const loginUrl = `${baseURL}/signin`
|
||||||
if (parseErr) {
|
if (parseErr) {
|
||||||
|
// 如果是 iframe 状态,则不进行跳转,向上返回错误,等待上层处理
|
||||||
|
const isIframe = typeof window !== 'undefined' && window.self !== window.top
|
||||||
|
// 跳转到登录页面
|
||||||
globalThis.location.href = loginUrl
|
globalThis.location.href = loginUrl
|
||||||
return Promise.reject(err)
|
return Promise.reject(err)
|
||||||
}
|
}
|
||||||
@@ -498,11 +506,11 @@ export const request = async<T>(url: string, options = {}, otherOptions?: IOther
|
|||||||
return Promise.reject(err)
|
return Promise.reject(err)
|
||||||
}
|
}
|
||||||
if (code === 'not_init_validated' && IS_CE_EDITION) {
|
if (code === 'not_init_validated' && IS_CE_EDITION) {
|
||||||
globalThis.location.href = `${globalThis.location.origin}/init`
|
globalThis.location.href = `${baseURL}/init`
|
||||||
return Promise.reject(err)
|
return Promise.reject(err)
|
||||||
}
|
}
|
||||||
if (code === 'not_setup' && IS_CE_EDITION) {
|
if (code === 'not_setup' && IS_CE_EDITION) {
|
||||||
globalThis.location.href = `${globalThis.location.origin}/install`
|
globalThis.location.href = `${baseURL}/install`
|
||||||
return Promise.reject(err)
|
return Promise.reject(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +518,7 @@ export const request = async<T>(url: string, options = {}, otherOptions?: IOther
|
|||||||
const [refreshErr] = await asyncRunSafe(refreshAccessTokenOrRelogin(TIME_OUT))
|
const [refreshErr] = await asyncRunSafe(refreshAccessTokenOrRelogin(TIME_OUT))
|
||||||
if (refreshErr === null)
|
if (refreshErr === null)
|
||||||
return baseFetch<T>(url, options, otherOptionsForBaseFetch)
|
return baseFetch<T>(url, options, otherOptionsForBaseFetch)
|
||||||
if (location.pathname !== '/signin' || !IS_CE_EDITION) {
|
if (location.pathname !== `${baseURL}/signin` || !IS_CE_EDITION) {
|
||||||
globalThis.location.href = loginUrl
|
globalThis.location.href = loginUrl
|
||||||
return Promise.reject(err)
|
return Promise.reject(err)
|
||||||
}
|
}
|
||||||
|
|||||||
26
web/utils/message-channel.ts
Normal file
26
web/utils/message-channel.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// 定义一个全局的 MessageChannel 用于 iframe 通信
|
||||||
|
let port
|
||||||
|
|
||||||
|
export function activeMessageChannel() {
|
||||||
|
// 只监听一次初始化消息,接收到后自动移除监听器
|
||||||
|
window.addEventListener('message', function initHandler(event) {
|
||||||
|
console.log('尝试接收初始化消息:', event.data)
|
||||||
|
|
||||||
|
console.log('接收到初始化消息,设置端口')
|
||||||
|
port = event.ports[0]
|
||||||
|
|
||||||
|
// 设置端口消息处理函数
|
||||||
|
port.onmessage = (msgEvent) => {
|
||||||
|
console.log('接收到worker消息:', msgEvent.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(globalThis, 'port', {
|
||||||
|
value: port,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始化完成后移除这个事件监听器
|
||||||
|
window.removeEventListener('message', initHandler)
|
||||||
|
}, /* {
|
||||||
|
once: true,
|
||||||
|
} */)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user