diff --git a/api/controllers/web/passport.py b/api/controllers/web/passport.py index 4625c1f43d..e30998c803 100644 --- a/api/controllers/web/passport.py +++ b/api/controllers/web/passport.py @@ -19,6 +19,8 @@ class PassportResource(Resource): def get(self): system_features = FeatureService.get_system_features() app_code = request.headers.get("X-App-Code") + user_id = request.args.get("user_id") + if app_code is None: raise Unauthorized("X-App-Code header is missing.") @@ -36,16 +38,33 @@ class PassportResource(Resource): if not app_model or app_model.status != "normal" or not app_model.enable_site: raise NotFound() - end_user = EndUser( - tenant_id=app_model.tenant_id, - app_id=app_model.id, - type="browser", - is_anonymous=True, - session_id=generate_session_id(), - ) + if user_id: + end_user = ( + db.session.query(EndUser).filter(EndUser.app_id == app_model.id, EndUser.session_id == user_id).first() + ) - db.session.add(end_user) - db.session.commit() + if end_user: + pass + else: + end_user = EndUser( + tenant_id=app_model.tenant_id, + app_id=app_model.id, + type="browser", + is_anonymous=True, + session_id=user_id, + ) + db.session.add(end_user) + db.session.commit() + else: + end_user = EndUser( + tenant_id=app_model.tenant_id, + app_id=app_model.id, + type="browser", + is_anonymous=True, + session_id=generate_session_id(), + ) + db.session.add(end_user) + db.session.commit() payload = { "iss": site.app_id, diff --git a/web/app/components/base/chat/utils.ts b/web/app/components/base/chat/utils.ts index 729a50d996..0e4e54e14d 100644 --- a/web/app/components/base/chat/utils.ts +++ b/web/app/components/base/chat/utils.ts @@ -15,12 +15,25 @@ async function getProcessedInputsFromUrlParams(): Promise> { const inputs: Record = {} await Promise.all( urlParams.entries().map(async ([key, value]) => { - inputs[key] = await decodeBase64AndDecompress(decodeURIComponent(value)) + if (!key.startsWith('sys.')) + inputs[key] = await decodeBase64AndDecompress(decodeURIComponent(value)) }), ) return inputs } +async function getProcessedSystemVariablesFromUrlParams(): Promise> { + const urlParams = new URLSearchParams(window.location.search) + const systemVariables: Record = {} + await Promise.all( + urlParams.entries().map(async ([key, value]) => { + if (key.startsWith('sys.')) + systemVariables[key.slice(4)] = await decodeBase64AndDecompress(decodeURIComponent(value)) + }), + ) + return systemVariables +} + function isValidGeneratedAnswer(item?: ChatItem | ChatItemInTree): boolean { return !!item && item.isAnswer && !item.id.startsWith('answer-placeholder-') && !item.isOpeningStatement } @@ -165,6 +178,7 @@ function getThreadMessages(tree: ChatItemInTree[], targetMessageId?: string): Ch export { getProcessedInputsFromUrlParams, + getProcessedSystemVariablesFromUrlParams, isValidGeneratedAnswer, getLastAnswer, buildChatItemTree, diff --git a/web/app/components/share/utils.ts b/web/app/components/share/utils.ts index 5a41523404..9c243b3b56 100644 --- a/web/app/components/share/utils.ts +++ b/web/app/components/share/utils.ts @@ -1,5 +1,6 @@ import { CONVERSATION_ID_INFO } from '../base/chat/constants' import { fetchAccessToken } from '@/service/share' +import { getProcessedSystemVariablesFromUrlParams } from '../base/chat/utils' export const checkOrSetAccessToken = async () => { const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0] @@ -12,7 +13,8 @@ export const checkOrSetAccessToken = async () => { } if (!accessTokenJson[sharedToken]) { - const res = await fetchAccessToken(sharedToken) + const sysUserId = (await getProcessedSystemVariablesFromUrlParams()).user_id + const res = await fetchAccessToken(sharedToken, sysUserId) accessTokenJson[sharedToken] = res.access_token localStorage.setItem('token', JSON.stringify(accessTokenJson)) } diff --git a/web/public/embed.js b/web/public/embed.js index 304409ded6..b3d3454ba7 100644 --- a/web/public/embed.js +++ b/web/public/embed.js @@ -50,7 +50,21 @@ return compressedInputs; } - const params = new URLSearchParams(await getCompressedInputsFromConfig()); + async function getCompressedSystemVariablesFromConfig() { + const systemVariables = config?.systemVariables || {}; + const compressedSystemVariables = {}; + await Promise.all( + Object.entries(systemVariables).map(async ([key, value]) => { + compressedSystemVariables[`sys.${key}`] = await compressAndEncodeBase64(value); + }) + ); + return compressedSystemVariables; + } + + const params = new URLSearchParams({ + ...await getCompressedInputsFromConfig(), + ...await getCompressedSystemVariablesFromConfig() + }); const baseUrl = config.baseUrl || `https://${config.isDev ? "dev." : ""}udify.app`; diff --git a/web/public/embed.min.js b/web/public/embed.min.js index f1d86a310b..f9e1c30af4 100644 --- a/web/public/embed.min.js +++ b/web/public/embed.min.js @@ -2,7 +2,7 @@ `,close:` - `};async function e(){if(p&&p.token){var e=new URLSearchParams(await(async()=>{var e=p?.inputs||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n[e]=(e=t,e=(new TextEncoder).encode(e),e=new Response(new Blob([e]).stream().pipeThrough(new CompressionStream("gzip"))).arrayBuffer(),e=new Uint8Array(await e),await btoa(String.fromCharCode(...e)))})),n})());let o=`${p.baseUrl||`https://${p.isDev?"dev.":""}udify.app`}/chatbot/${p.token}?`+e;function i(){var e,t;window.innerWidth<=640||(e=document.getElementById(c),t=document.getElementById(a),e&&t&&((t=t.getBoundingClientRect()).top-5>e.clientHeight?(e.style.bottom="0px",e.style.top="unset"):(e.style.bottom="unset",e.style.top="0px"),t.right>e.clientWidth?(e.style.right="0",e.style.left="unset"):(e.style.right="unset",e.style.left=0)))}function t(){let n=document.createElement("div");Object.entries(p.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=a;var e=document.createElement("style");document.head.appendChild(e),e.sheet.insertRule(` + `};async function e(){if(p&&p.token){var e=new URLSearchParams({...await(async()=>{var e=p?.inputs||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n[e]=await i(t)})),n})(),...await(async()=>{var e=p?.systemVariables||{};let n={};return await Promise.all(Object.entries(e).map(async([e,t])=>{n["sys."+e]=await i(t)})),n})()});let o=`${p.baseUrl||`https://${p.isDev?"dev.":""}udify.app`}/chatbot/${p.token}?`+e;async function i(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 r(){var e,t;window.innerWidth<=640||(e=document.getElementById(c),t=document.getElementById(a),e&&t&&((t=t.getBoundingClientRect()).top-5>e.clientHeight?(e.style.bottom="0px",e.style.top="unset"):(e.style.bottom="unset",e.style.top="0px"),t.right>e.clientWidth?(e.style.right="0",e.style.left="unset"):(e.style.right="unset",e.style.left=0)))}function t(){let n=document.createElement("div");Object.entries(p.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=a;var e=document.createElement("style");document.head.appendChild(e),e.sheet.insertRule(` #${n.id} { position: fixed; bottom: var(--${n.id}-bottom, 1rem); @@ -17,7 +17,7 @@ cursor: pointer; z-index: 2147483647; } - `);let t=document.createElement("div");if(t.style.cssText="position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;",t.innerHTML=h.open,n.appendChild(t),document.body.appendChild(n),n.addEventListener("click",function(){var e=document.getElementById(c);e?(e.style.display="none"===e.style.display?"block":"none",t.innerHTML="none"===e.style.display?h.open:h.close,"none"===e.style.display?document.removeEventListener("keydown",d):document.addEventListener("keydown",d),i()):(n.prepend(((e=document.createElement("iframe")).allow="fullscreen;microphone",e.title="dify chatbot bubble window",e.id=c,e.src=o,e.style.cssText=` + `);let t=document.createElement("div");if(t.style.cssText="position: relative; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; z-index: 2147483647;",t.innerHTML=h.open,n.appendChild(t),document.body.appendChild(n),n.addEventListener("click",function(){var e=document.getElementById(c);e?(e.style.display="none"===e.style.display?"block":"none",t.innerHTML="none"===e.style.display?h.open:h.close,"none"===e.style.display?document.removeEventListener("keydown",s):document.addEventListener("keydown",s),r()):(n.prepend(((e=document.createElement("iframe")).allow="fullscreen;microphone",e.title="dify chatbot bubble window",e.id=c,e.src=o,e.style.cssText=` position: absolute; display: flex; flex-direction: column; @@ -33,4 +33,4 @@ z-index: 2147483640; overflow: hidden; user-select: none; - `,e)),i(),this.title="Exit (ESC)",t.innerHTML=h.close,document.addEventListener("keydown",d))}),p.draggable){var s=n;var l=p.dragAxis||"both";let i=!1,d,r;s.addEventListener("mousedown",function(e){i=!0,d=e.clientX-s.offsetLeft,r=e.clientY-s.offsetTop}),document.addEventListener("mousemove",function(e){var t,n,o;i&&(s.style.transition="none",s.style.cursor="grabbing",(t=document.getElementById(c))&&(t.style.display="none",s.querySelector("div").innerHTML=h.open),t=e.clientX-d,e=window.innerHeight-e.clientY-r,o=s.getBoundingClientRect(),n=window.innerWidth-o.width,o=window.innerHeight-o.height,"x"!==l&&"both"!==l||s.style.setProperty(`--${a}-left`,Math.max(0,Math.min(t,n))+"px"),"y"!==l&&"both"!==l||s.style.setProperty(`--${a}-bottom`,Math.max(0,Math.min(e,o))+"px"))}),document.addEventListener("mouseup",function(){i=!1,s.style.transition="",s.style.cursor="pointer"})}}2048 { +export const fetchAccessToken = async (appCode: string, userId?: string) => { const headers = new Headers() headers.append('X-App-Code', appCode) - return get('/passport', { headers }) as Promise<{ access_token: string }> + const url = userId ? `/passport?user_id=${encodeURIComponent(userId)}` : '/passport' + return get(url, { headers }) as Promise<{ access_token: string }> }