perf: Optimize page embedding mechanism

This commit is contained in:
fit2cloud-chenyw
2025-08-21 10:00:50 +08:00
parent b8e9da12d2
commit 5323993214
7 changed files with 104 additions and 36 deletions
+50 -6
View File
@@ -8,7 +8,7 @@ from starlette.middleware.base import BaseHTTPMiddleware
from apps.system.models.system_model import AssistantModel
from common.core.db import engine
from apps.system.crud.assistant import get_assistant_info, get_assistant_user
from apps.system.crud.user import get_user_info
from apps.system.crud.user import get_user_by_account, get_user_info
from apps.system.schemas.system_schema import AssistantHeader, UserInfoDTO
from common.core import security
from common.core.config import settings
@@ -34,7 +34,7 @@ class TokenMiddleware(BaseHTTPMiddleware):
trans = await get_i18n(request)
#if assistantToken and assistantToken.lower().startswith("assistant "):
if assistantToken:
validator: tuple[any] = await self.validateAssistant(assistantToken)
validator: tuple[any] = await self.validateAssistant(assistantToken, trans)
if validator[0]:
request.state.current_user = validator[1]
request.state.assistant = validator[2]
@@ -87,14 +87,17 @@ class TokenMiddleware(BaseHTTPMiddleware):
return False, e
async def validateAssistant(self, assistantToken: Optional[str]) -> tuple[any]:
async def validateAssistant(self, assistantToken: Optional[str], trans: I18n) -> tuple[any]:
if not assistantToken:
return False, f"Miss Token[{settings.TOKEN_KEY}]!"
schema, param = get_authorization_scheme_param(assistantToken)
if schema.lower() != "assistant":
return False, f"Token schema error!"
try:
try:
if schema.lower() == 'embedded':
return await self.validateEmbedded(param, trans)
if schema.lower() != "assistant":
return False, f"Token schema error!"
payload = jwt.decode(
param, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
)
@@ -112,4 +115,45 @@ class TokenMiddleware(BaseHTTPMiddleware):
except Exception as e:
SQLBotLogUtil.exception(f"Assistant validation error: {str(e)}")
# Return False and the exception message
return False, e
async def validateEmbedded(self, param: str, trans: I18n) -> tuple[any]:
try:
""" payload = jwt.decode(
param, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
) """
payload: dict = jwt.decode(
param,
options={"verify_signature": False, "verify_exp": False},
algorithms=[security.ALGORITHM]
)
if not payload['embeddedId']:
return False, f"Miss embeddedId payload error!"
if not payload['account']:
return False, f"Miss account payload error!"
embeddedId = payload['embeddedId']
account = payload['account']
with Session(engine) as session:
""" session_user = await get_user_info(session = session, user_id = token_data.id)
session_user = UserInfoDTO.model_validate(session_user) """
session_user = get_user_by_account(session = session, account=account)
if not session_user:
message = trans('i18n_not_exist', msg = trans('i18n_user.account'))
raise Exception(message)
session_user = await get_user_info(session = session, user_id = session_user.id)
session_user = UserInfoDTO.model_validate(session_user)
if session_user.status != 1:
message = trans('i18n_login.user_disable', msg = trans('i18n_concat_admin'))
raise Exception(message)
if not session_user.oid or session_user.oid == 0:
message = trans('i18n_login.no_associated_ws', msg = trans('i18n_concat_admin'))
raise Exception(message)
assistant_info = await get_assistant_info(session=session, assistant_id=embeddedId)
assistant_info = AssistantModel.model_validate(assistant_info)
assistant_info = AssistantHeader.model_validate(assistant_info.model_dump(exclude_unset=True))
return True, session_user, assistant_info
except Exception as e:
SQLBotLogUtil.exception(f"Embedded validation error: {str(e)}")
# Return False and the exception message
return False, e
+2 -2
View File
@@ -27,11 +27,11 @@ wlist = [
"/system/config/key",
"/images/*",
"/sse",
"/system/appearance*",
"/system/appearance/ui",
"/system/appearance/picture/*",
"/system/assistant/validator*",
"/system/assistant/info/*",
"/system/assistant/picture/*",
"/system/embedded*",
"/datasource/uploadExcel"
]
+2 -9
View File
@@ -36,28 +36,21 @@ dependencies = [
"pyyaml (>=6.0.2,<7.0.0)",
"fastapi-mcp (>=0.3.4,<0.4.0)",
"tabulate>=0.9.0",
"sqlbot-xpack>=0.0.3.19,<1.0.0",
"fastapi-cache2>=0.2.2",
"sqlparse>=0.5.3",
"redis>=6.2.0",
"xlsxwriter>=3.2.5",
"python-calamine>=0.4.0",
"xlrd>=2.0.2",
"clickhouse-sqlalchemy>=0.3.2",
"dmpython>=2.5.22",
"sqlbot-xpack",
]
[[tool.uv.index]]
name = "default"
url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
default = true
[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple"
explicit = true
[tool.uv.sources]
sqlbot-xpack = { index = "testpypi" }
sqlbot-xpack = { path = "../../sqlbot-xpack/dist/sqlbot_xpack-0.0.3.19-cp311-cp311-macosx_11_0_arm64.whl" }
[tool.uv]
dev-dependencies = [
+1 -1
View File
@@ -252,7 +252,7 @@ export const useAppearanceStore = defineStore('appearanceStore', {
setLinkIcon()
return
}
const resData = await request.get('/system/appearance')
const resData = await request.get('/system/appearance/ui')
this.loaded = true
if (!resData?.length) {
return
+16 -6
View File
@@ -80,14 +80,19 @@ class HttpService {
config.headers['X-SQLBOT-TOKEN'] = `Bearer ${token}`
}
if (assistantStore.getToken) {
config.headers['X-SQLBOT-ASSISTANT-TOKEN'] = `Assistant ${assistantStore.getToken}`
const prefix = assistantStore.getType === 4 ? 'Embedded ' : 'Assistant '
config.headers['X-SQLBOT-ASSISTANT-TOKEN'] = `${prefix}${assistantStore.getToken}`
if (config.headers['X-SQLBOT-TOKEN']) config.headers.delete('X-SQLBOT-TOKEN')
if (assistantStore.getType && assistantStore.getCertificate) {
if (
assistantStore.getType &&
!!(assistantStore.getType % 2) &&
assistantStore.getCertificate
) {
config.headers['X-SQLBOT-ASSISTANT-CERTIFICATE'] = btoa(
encodeURIComponent(assistantStore.getCertificate)
)
}
if (!assistantStore.getType) {
if (!assistantStore.getType || assistantStore.getType === 2) {
config.headers['X-SQLBOT-ASSISTANT-ONLINE'] = assistantStore.getOnline
}
}
@@ -270,15 +275,20 @@ class HttpService {
heads['X-SQLBOT-TOKEN'] = `Bearer ${token}`
}
if (assistantStore.getToken) {
heads['X-SQLBOT-ASSISTANT-TOKEN'] = `Assistant ${assistantStore.getToken}`
const prefix = assistantStore.getType === 4 ? 'Embedded ' : 'Assistant '
heads['X-SQLBOT-ASSISTANT-TOKEN'] = `${prefix}${assistantStore.getToken}`
if (heads['X-SQLBOT-TOKEN']) delete heads['X-SQLBOT-TOKEN']
if (assistantStore.getType && assistantStore.getCertificate) {
if (
assistantStore.getType &&
!!(assistantStore.getType % 2) &&
assistantStore.getCertificate
) {
await assistantStore.refreshCertificate()
heads['X-SQLBOT-ASSISTANT-CERTIFICATE'] = btoa(
encodeURIComponent(assistantStore.getCertificate)
)
}
if (!assistantStore.getType) {
if (!assistantStore.getType || assistantStore.getType === 2) {
heads['X-SQLBOT-ASSISTANT-ONLINE'] = assistantStore.getOnline
}
}
+32 -11
View File
@@ -22,15 +22,22 @@ const validator = ref({
token: '',
})
const loading = ref(true)
const eventName = 'sqlbot_assistant_event'
const eventName = 'sqlbot_embedded_event'
const communicationCb = async (event: any) => {
if (event.data?.eventName === eventName) {
if (event.data?.messageId !== route.query.id) {
return
}
if (event.data?.busi == 'certificate') {
const type = parseInt(event.data['type'])
const certificate = event.data['certificate']
assistantStore.setType(1)
assistantStore.setType(type || 3)
if (type === 4) {
assistantStore.setToken(certificate)
assistantStore.setAssistant(true)
loading.value = false
return
}
assistantStore.setCertificate(certificate)
assistantStore.resolveCertificate(certificate)
}
@@ -55,13 +62,29 @@ const setFormatOnline = (text?: any) => {
assistantStore.setOnline(false)
}
const registerReady = (assistantId: any) => {
window.addEventListener('message', communicationCb)
const readyData = {
eventName: 'sqlbot_embedded_event',
busi: 'ready',
ready: true,
messageId: assistantId,
}
window.parent.postMessage(readyData, '*')
}
onBeforeMount(async () => {
debugger
const assistantId = route.query.id
if (!assistantId) {
ElMessage.error('Miss embedded id, please check embedded url')
return
}
const typeParam = route.query.type
let assistantType = 2
if (typeParam) {
assistantType = parseInt(typeParam.toString())
assistantStore.setType(assistantType)
}
const online = route.query.online
setFormatOnline(online)
@@ -72,6 +95,11 @@ onBeforeMount(async () => {
const now = Date.now()
assistantStore.setFlag(now)
assistantStore.setId(assistantId?.toString() || '')
if (assistantType === 4) {
assistantStore.setAssistant(true)
registerReady(assistantId)
return
}
const param = {
id: assistantId,
virtual: assistantStore.getFlag,
@@ -82,14 +110,7 @@ onBeforeMount(async () => {
assistantStore.setAssistant(true)
loading.value = false
window.addEventListener('message', communicationCb)
const readyData = {
eventName: 'sqlbot_embedded_event',
busi: 'ready',
ready: true,
messageId: assistantId,
}
window.parent.postMessage(readyData, '*')
registerReady(assistantId)
})
onBeforeUnmount(() => {
@@ -402,7 +402,7 @@ const buildParam = () => {
return formData
}
const init = () => {
const url = '/system/appearance'
const url = '/system/appearance/ui'
changedItemArray.value = []
fileList.value = []
request