mirror of
https://github.com/langgenius/dify.git
synced 2026-07-25 21:48:30 +08:00
fix(web): allow clipboard writes in embedded apps (#39511)
This commit is contained in:
parent
7bb09ce039
commit
a2c67372ed
52
web/__tests__/embed.spec.ts
Normal file
52
web/__tests__/embed.spec.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { Script } from 'node:vm'
|
||||
import { waitFor } from '@testing-library/react'
|
||||
|
||||
const embedArtifacts = [
|
||||
{
|
||||
name: 'embed.js',
|
||||
source: readFileSync(resolve(process.cwd(), 'public/embed.js'), 'utf8'),
|
||||
},
|
||||
{
|
||||
name: 'embed.min.js',
|
||||
source: readFileSync(resolve(process.cwd(), 'public/embed.min.js'), 'utf8'),
|
||||
},
|
||||
]
|
||||
|
||||
function executeEmbedScript(source: string) {
|
||||
Object.defineProperty(window, 'difyChatbotConfig', {
|
||||
configurable: true,
|
||||
value: {
|
||||
token: 'test-token',
|
||||
baseUrl: 'about:blank',
|
||||
dynamicScript: true,
|
||||
},
|
||||
})
|
||||
|
||||
new Script(source).runInThisContext()
|
||||
}
|
||||
|
||||
describe.each(embedArtifacts)('$name', ({ source }) => {
|
||||
beforeEach(() => {
|
||||
document.head.replaceChildren()
|
||||
document.body.replaceChildren()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
Reflect.deleteProperty(window, 'difyChatbotConfig')
|
||||
document.head.replaceChildren()
|
||||
document.body.replaceChildren()
|
||||
})
|
||||
|
||||
it('should delegate clipboard-write permission to the chatbot iframe', async () => {
|
||||
executeEmbedScript(source)
|
||||
|
||||
await waitFor(() => {
|
||||
const iframe = document.querySelector<HTMLIFrameElement>('#dify-chatbot-bubble-window')
|
||||
const permissions = iframe?.allow.split(';').map((permission) => permission.trim())
|
||||
|
||||
expect(permissions).toContain('clipboard-write')
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -296,7 +296,7 @@ describe('app-card-utils', () => {
|
||||
const snippet = getEmbeddedIframeSnippet('https://example.com/chatbot/token-1')
|
||||
expect(snippet).toContain('src="https://example.com/chatbot/token-1"')
|
||||
expect(snippet).toContain('frameborder="0"')
|
||||
expect(snippet).toContain('allow="microphone"')
|
||||
expect(snippet).toContain('allow="microphone;clipboard-write"')
|
||||
})
|
||||
|
||||
it('should generate an embedded script snippet with inputs', () => {
|
||||
|
||||
@ -142,7 +142,7 @@ export const getEmbeddedIframeSnippet = (iframeUrl: string) =>
|
||||
src="${iframeUrl}"
|
||||
style="width: 100%; height: 100%; min-height: 700px"
|
||||
frameborder="0"
|
||||
allow="microphone">
|
||||
allow="microphone;clipboard-write">
|
||||
</iframe>`
|
||||
|
||||
const getScriptInputsContent = (values: Record<string, WorkflowLaunchInputValue>) => {
|
||||
|
||||
@ -155,7 +155,7 @@
|
||||
// Function to create the iframe for the chatbot
|
||||
function createIframe() {
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.allow = 'fullscreen;microphone'
|
||||
iframe.allow = 'fullscreen;microphone;clipboard-write'
|
||||
iframe.title = 'dify chatbot bubble window'
|
||||
iframe.id = iframeId
|
||||
iframe.src = iframeUrl
|
||||
|
||||
2
web/public/embed.min.js
vendored
2
web/public/embed.min.js
vendored
@ -19,7 +19,7 @@
|
||||
transition-property: width, height;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
`;async function e(){let u=!1;if(y&&y.token){var e=new URLSearchParams({...await(async()=>{var e=y?.inputs||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n[e]=await r(t)})),n})(),...await(async()=>{var e=y?.systemVariables||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n["sys."+e]=await r(t)})),n})(),...await(async()=>{var e=y?.userVariables||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n["user."+e]=await r(t)})),n})()}),n=y.baseUrl||`https://${y.isDev?"dev.":""}udify.app`,i=(y.routeSegment||"chatbot").replace(/^\/+|\/+$/g,"")||"chatbot";let o=new URL(n).origin,t=(!1===y.sendOnEnter&&e.set("sendOnEnter","false"),`${n}/${i}/${y.token}?`+e);n=s();async function r(e){e=(new TextEncoder).encode(e),e=new Response(new Blob([e]).stream().pipeThrough(new CompressionStream("gzip"))).arrayBuffer(),e=new Uint8Array(await e);return btoa(String.fromCharCode(...e))}function s(){var e=document.createElement("iframe");return e.allow="fullscreen;microphone",e.title="dify chatbot bubble window",e.id=h,e.src=t,e.style.cssText=c,e}function d(){var e,t,n;window.innerWidth<=640||(e=document.getElementById(h),t=document.getElementById(m),e&&t&&(t=t.getBoundingClientRect(),n=window.innerHeight/2,t.top+t.height/2<n?(e.style.top=`var(--${m}-bottom, 1rem)`,e.style.bottom="unset"):(e.style.bottom=`var(--${m}-bottom, 1rem)`,e.style.top="unset"),t.left+t.width/2<window.innerWidth/2?(e.style.left=`var(--${m}-right, 1rem)`,e.style.right="unset"):(e.style.right=`var(--${m}-right, 1rem)`,e.style.left="unset")))}function a(){let n=document.createElement("div");Object.entries(y.containerProps||{}).forEach(([e,t])=>{"className"===e?n.classList.add(...t.split(" ")):"style"===e?"object"==typeof t?Object.assign(n.style,t):n.style.cssText=t:"function"==typeof t?n.addEventListener(e.replace(/^on/,"").toLowerCase(),t):n[e]=t}),n.id=m;var e=document.createElement("style"),e=(document.head.appendChild(e),e.sheet.insertRule(`
|
||||
`;async function e(){let u=!1;if(y&&y.token){var e=new URLSearchParams({...await(async()=>{var e=y?.inputs||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n[e]=await r(t)})),n})(),...await(async()=>{var e=y?.systemVariables||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n["sys."+e]=await r(t)})),n})(),...await(async()=>{var e=y?.userVariables||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n["user."+e]=await r(t)})),n})()}),n=y.baseUrl||`https://${y.isDev?"dev.":""}udify.app`,i=(y.routeSegment||"chatbot").replace(/^\/+|\/+$/g,"")||"chatbot";let o=new URL(n).origin,t=(!1===y.sendOnEnter&&e.set("sendOnEnter","false"),`${n}/${i}/${y.token}?`+e);n=s();async function r(e){e=(new TextEncoder).encode(e),e=new Response(new Blob([e]).stream().pipeThrough(new CompressionStream("gzip"))).arrayBuffer(),e=new Uint8Array(await e);return btoa(String.fromCharCode(...e))}function s(){var e=document.createElement("iframe");return e.allow="fullscreen;microphone;clipboard-write",e.title="dify chatbot bubble window",e.id=h,e.src=t,e.style.cssText=c,e}function d(){var e,t,n;window.innerWidth<=640||(e=document.getElementById(h),t=document.getElementById(m),e&&t&&(t=t.getBoundingClientRect(),n=window.innerHeight/2,t.top+t.height/2<n?(e.style.top=`var(--${m}-bottom, 1rem)`,e.style.bottom="unset"):(e.style.bottom=`var(--${m}-bottom, 1rem)`,e.style.top="unset"),t.left+t.width/2<window.innerWidth/2?(e.style.left=`var(--${m}-right, 1rem)`,e.style.right="unset"):(e.style.right=`var(--${m}-right, 1rem)`,e.style.left="unset")))}function a(){let n=document.createElement("div");Object.entries(y.containerProps||{}).forEach(([e,t])=>{"className"===e?n.classList.add(...t.split(" ")):"style"===e?"object"==typeof t?Object.assign(n.style,t):n.style.cssText=t:"function"==typeof t?n.addEventListener(e.replace(/^on/,"").toLowerCase(),t):n[e]=t}),n.id=m;var e=document.createElement("style"),e=(document.head.appendChild(e),e.sheet.insertRule(`
|
||||
#${n.id} {
|
||||
position: fixed;
|
||||
bottom: var(--${n.id}-bottom, 1rem);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user