diff --git a/backend/apps/system/middleware/auth.py b/backend/apps/system/middleware/auth.py index 32fa4c71..1f3b3a27 100644 --- a/backend/apps/system/middleware/auth.py +++ b/backend/apps/system/middleware/auth.py @@ -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 \ No newline at end of file diff --git a/backend/common/utils/whitelist.py b/backend/common/utils/whitelist.py index e8654738..ce978899 100644 --- a/backend/common/utils/whitelist.py +++ b/backend/common/utils/whitelist.py @@ -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" ] diff --git a/backend/pyproject.toml b/backend/pyproject.toml index f925c6ea..e07b601e 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -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 = [ diff --git a/frontend/src/stores/appearance.ts b/frontend/src/stores/appearance.ts index 3abb26e7..1930f378 100644 --- a/frontend/src/stores/appearance.ts +++ b/frontend/src/stores/appearance.ts @@ -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 diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index 5c0ad969..6635f421 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -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 } } diff --git a/frontend/src/views/embedded/page.vue b/frontend/src/views/embedded/page.vue index 4aead20e..62418e76 100644 --- a/frontend/src/views/embedded/page.vue +++ b/frontend/src/views/embedded/page.vue @@ -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(() => { diff --git a/frontend/src/views/system/appearance/index.vue b/frontend/src/views/system/appearance/index.vue index 0ff0c5d3..0bed44ec 100644 --- a/frontend/src/views/system/appearance/index.vue +++ b/frontend/src/views/system/appearance/index.vue @@ -402,7 +402,7 @@ const buildParam = () => { return formData } const init = () => { - const url = '/system/appearance' + const url = '/system/appearance/ui' changedItemArray.value = [] fileList.value = [] request