This commit is contained in:
dataeaseShu
2025-07-29 16:31:16 +08:00
14 changed files with 83 additions and 35 deletions
+1 -1
View File
@@ -253,7 +253,7 @@ def exec_sql(ds: CoreDatasource | AssistantOutDsSchema, sql: str):
with get_session(ds) as session:
with session.execute(text(sql)) as result:
try:
columns = result.keys()._keys
columns = [item.lower() for item in result.keys()._keys]
res = result.fetchall()
result_list = [
{columns[i]: float(value) if isinstance(value, Decimal) else value for i, value in
+5 -2
View File
@@ -3,6 +3,7 @@ from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import OAuth2PasswordRequestForm
from apps.system.schemas.system_schema import BaseUserDTO
from common.core.deps import SessionDep, Trans
from common.utils.crypto import sqlbot_decrypt
from ..crud.user import authenticate
from common.core.security import create_access_token
from datetime import timedelta
@@ -11,12 +12,14 @@ from common.core.schemas import Token
router = APIRouter(tags=["login"], prefix="/login")
@router.post("/access-token")
def local_login(
async def local_login(
session: SessionDep,
trans: Trans,
form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
) -> Token:
user: BaseUserDTO = authenticate(session=session, account=form_data.username, password=form_data.password)
origin_account = await sqlbot_decrypt(form_data.username)
origin_pwd = await sqlbot_decrypt(form_data.password)
user: BaseUserDTO = authenticate(session=session, account=origin_account, password=origin_pwd)
if not user:
raise HTTPException(status_code=400, detail=trans('i18n_login.account_pwd_error'))
if not user.oid or user.oid == 0:
+4
View File
@@ -0,0 +1,4 @@
from sqlbot_xpack.core import sqlbot_decrypt as xpack_sqlbot_decrypt
async def sqlbot_decrypt(text: str) -> str:
return await xpack_sqlbot_decrypt(text)
+1
View File
@@ -29,6 +29,7 @@ async def lifespan(app: FastAPI):
init_sqlbot_cache()
init_dynamic_cors(app)
SQLBotLogUtil.info("✅ SQLBot 初始化完成")
await sqlbot_xpack.core.clean_xpack_cache()
yield
SQLBotLogUtil.info("SQLBot 应用关闭")
+1 -1
View File
@@ -36,7 +36,7 @@ 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.8",
"sqlbot-xpack==0.0.3.9",
"fastapi-cache2>=0.2.2",
"sqlparse>=0.5.3",
"redis>=6.2.0",
+1
View File
@@ -8,4 +8,5 @@ export {}
declare global {
const ElMessage: typeof import('element-plus-secondary/es')['ElMessage']
const ElMessageBox: typeof import('element-plus-secondary/es')['ElMessageBox']
const LicenseGenerator: any
}
+9 -5
View File
@@ -1,15 +1,19 @@
import { request } from '@/utils/request'
export const AuthApi = {
login: (credentials: { username: string; password: string }) =>
request.post<{
login: (credentials: { username: string; password: string }) => {
const entryCredentials = {
username: LicenseGenerator.sqlbotEncrypt(credentials.username),
password: LicenseGenerator.sqlbotEncrypt(credentials.password),
}
return request.post<{
data: any
token: string
}>('/login/access-token', credentials, {
}>('/login/access-token', entryCredentials, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
}),
})
},
logout: () => request.post('/auth/logout'),
info: () => request.get('/user/info'),
}
+2
View File
@@ -36,6 +36,8 @@
"password_reset_successful": "Password reset successful"
},
"dashboard": {
"add_dashboard_name_tips": "Please Input Dashboard Name",
"existing_dashboard": "Existing Dashboard",
"add_success": "Add Success",
"no_data": "No Relevant Content Found",
"new_tab": "New Tab",
+2
View File
@@ -39,6 +39,8 @@
"or": "或者"
},
"dashboard": {
"add_dashboard_name_tips": "请输入仪表板名称",
"existing_dashboard": "存量仪表板",
"add_success": "添加成功",
"no_data": "没找到相关内容",
"new_tab": "新建Tab",
+1 -3
View File
@@ -50,12 +50,10 @@ const loadXpackStatic = () => {
if (document.getElementById('sqlbot_xpack_static')) {
return Promise.resolve()
}
const url = '/xpack_static/license-generator.umd.js'
const url = `/xpack_static/license-generator.umd.js?t=${Date.now()}`
return new Promise((resolve, reject) => {
request
.loadRemoteScript(url, 'sqlbot_xpack_static', () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
LicenseGenerator?.init(import.meta.env.VITE_API_BASE_URL).then(() => {
resolve(true)
})
+1 -4
View File
@@ -99,8 +99,7 @@ class HttpService {
// Skip auth for xpack_static requests
return config
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const request_key = LicenseGenerator.generate()
config.headers['X-SQLBOT-KEY'] = request_key
@@ -266,8 +265,6 @@ class HttpService {
heads['X-SQLBOT-ASSISTANT-ONLINE'] = assistantStore.getOnline
}
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const request_key = LicenseGenerator.generate()
heads['X-SQLBOT-KEY'] = request_key
@@ -192,9 +192,25 @@ function showSql() {
}
function addToDashboard() {
const viewInfo = chartRef.value?.getViewInfo()
const recordeInfo = {
id: '1-1',
data: {
data: data.value,
},
chart: {},
}
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
addViewRef.value?.optInit(viewInfo)
const chartBaseInfo = JSON.parse(props.message?.record?.chart)
recordeInfo['chart'] = {
type: chartBaseInfo.type,
title: chartBaseInfo.title,
columns: chartBaseInfo.columns,
xAxis: chartBaseInfo.axis?.x ? [chartBaseInfo.axis.x] : [],
yAxis: chartBaseInfo.axis?.y ? [chartBaseInfo.axis.y] : [],
series: chartBaseInfo.axis?.series ? [chartBaseInfo.axis.series] : [],
}
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
addViewRef.value?.optInit(recordeInfo)
}
function copy() {
@@ -264,7 +280,7 @@ function copy() {
</el-button>
</el-tooltip>
</div>
<div v-if="false">
<div>
<!-- todo -->
<el-tooltip effect="dark" :content="t('chat.add_to_dashboard')" placement="top">
<el-button class="tool-btn" text @click="addToDashboard">
@@ -68,6 +68,10 @@ const saveResourcePrepare = () => {
if (result) {
const component = cloneDeep(findNewComponentFromList('SQView'))
const newComponentId = guid()
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
state.viewInfo.chart['id'] = newComponentId
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
state.viewInfo['id'] = newComponentId
if (resourceForm.addType === 'history' && component) {
findNextComponentIndex({ id: resourceForm.dashboardId }, (result: any) => {
const {
@@ -84,10 +88,8 @@ const saveResourcePrepare = () => {
name: dashboardInfo.name,
}
component['id'] = newComponentId
component['y'] = bottomPosition + 1
component['y'] = bottomPosition
canvasDataResult.push(component)
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
state.viewInfo.chart['id'] = newComponentId
canvasViewInfoPreview[newComponentId] = state.viewInfo
const commonParams = {
componentData: canvasDataResult,
@@ -97,12 +99,22 @@ const saveResourcePrepare = () => {
saveResource(params, commonParams)
})
} else if (resourceForm.addType === 'new' && component) {
const params = { opt: 'newLeaf', pid: 'root', name: resourceForm.dashboardName }
const params = {
opt: 'newLeaf',
pid: 'root',
name: resourceForm.dashboardName,
level: 1,
node_type: 'leaf',
type: 'dashboard',
}
component['id'] = newComponentId
const canvasViewInfo = {}
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
canvasViewInfo[newComponentId] = state.viewInfo
const commonParams = {
componentData: [component],
canvasStyleData: {},
canvasViewInfo: { newComponentId: state.viewInfo },
canvasViewInfo: canvasViewInfo,
}
saveResource(params, commonParams)
}
@@ -112,7 +124,7 @@ const saveResourcePrepare = () => {
const saveResource = (params: any, commonParams: any) => {
saveDashboardResourceTarget(params, commonParams, function () {
const messageTips = t('common.save_success')
const messageTips = t('dashboard.add_success')
ElMessage({
type: 'success',
message: messageTips,
@@ -142,7 +154,7 @@ defineExpose({
<el-dialog
v-model="resourceDialogShow"
class="create-dialog"
title="添加到仪表板"
:title="t('chat.add_to_dashboard')"
width="420px"
:before-close="resetForm"
append-to-body
@@ -157,33 +169,36 @@ defineExpose({
:rules="resourceForm.addType === 'new' ? resourceFormRulesNew : resourceFormRulesHistory"
@submit.prevent
>
<el-form-item label="仪表板名称" required prop="addType">
<el-form-item :label="t('dashboard.dashboard_name')" required prop="addType">
<el-radio-group v-model="resourceForm.addType">
<el-radio value="history">存量仪表板</el-radio>
<el-radio value="new">新建仪表板</el-radio>
<el-radio value="history">{{ t('dashboard.existing_dashboard') }}</el-radio>
<el-radio value="new">{{ t('dashboard.new_dashboard') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="resourceForm.addType === 'new'"
label="仪表板"
:label="t('dashboard.dashboard')"
required
prop="dashboardName"
>
<el-input
v-model="resourceForm.dashboardName"
clearable
placeholder="请输入仪表板名称"
:placeholder="t('dashboard.add_dashboard_name_tips')"
@keydown.stop
@keyup.stop
/>
</el-form-item>
<el-form-item
v-if="resourceForm.addType === 'history'"
label="仪表板"
:label="t('dashboard.dashboard')"
required
prop="dashboardId"
>
<el-select v-model="resourceForm.dashboardId" placeholder="请选择仪表板">
<el-select
v-model="resourceForm.dashboardId"
:placeholder="t('dashboard.select_dashboard')"
>
<el-option
v-for="item in state.dashboardList"
:key="item.id"
+7 -2
View File
@@ -51,8 +51,13 @@
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column prop="name" :label="$t('user.name')" width="280" />
<el-table-column prop="account" :label="$t('user.account')" width="280" />
<el-table-column prop="name" show-overflow-tooltip :label="$t('user.name')" width="280" />
<el-table-column
prop="account"
show-overflow-tooltip
:label="$t('user.account')"
width="280"
/>
<el-table-column prop="status" :label="$t('user.user_status')" width="180">
<template #default="scope">
<div class="user-status-container" :class="[scope.row.status ? 'active' : 'disabled']">