mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
"""030_permission_oid
|
||||
|
||||
Revision ID: c1d7ac00b3a8
|
||||
Revises: 77d4c39ec22f
|
||||
Create Date: 2025-07-21 11:49:43.115524
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel.sql.sqltypes
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c1d7ac00b3a8'
|
||||
down_revision = '77d4c39ec22f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('ds_rules', sa.Column('oid', sa.BigInteger(), nullable=True))
|
||||
op.alter_column('ds_permission', 'id',
|
||||
existing_type=sa.INTEGER(),
|
||||
type_=sa.BigInteger(),
|
||||
existing_nullable=False,
|
||||
autoincrement=True,
|
||||
existing_server_default=sa.Identity(always=True, start=1, increment=1, minvalue=1,
|
||||
maxvalue=2147483647, cycle=False, cache=1))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('ds_permission', 'id',
|
||||
existing_type=sa.BigInteger(),
|
||||
type_=sa.INTEGER(),
|
||||
existing_nullable=False,
|
||||
autoincrement=True,
|
||||
existing_server_default=sa.Identity(always=True, start=1, increment=1, minvalue=1,
|
||||
maxvalue=2147483647, cycle=False, cache=1))
|
||||
op.drop_column('ds_rules', 'oid')
|
||||
# ### end Alembic commands ###
|
||||
@@ -11,7 +11,7 @@ import requests
|
||||
from langchain.chat_models.base import BaseChatModel
|
||||
from langchain_community.utilities import SQLDatabase
|
||||
from langchain_core.messages import BaseMessage, SystemMessage, HumanMessage, AIMessage, BaseMessageChunk
|
||||
from sqlalchemy import and_, cast
|
||||
from sqlalchemy import and_, cast, or_
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import load_only
|
||||
@@ -489,7 +489,8 @@ class LLMService:
|
||||
# check permission and user in same rules
|
||||
obj = self.session.query(DsRules).filter(
|
||||
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
|
||||
DsRules.user_list.op('@>')(cast([self.current_user.id], JSONB)))
|
||||
or_(DsRules.user_list.op('@>')(cast([f'{self.current_user.id}'], JSONB)),
|
||||
DsRules.user_list.op('@>')(cast([self.current_user.id], JSONB))))
|
||||
).first()
|
||||
if obj is not None:
|
||||
res.append(transRecord2DTO(self.session, permission))
|
||||
|
||||
@@ -3,7 +3,7 @@ import json
|
||||
from typing import List
|
||||
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy import and_, text, cast
|
||||
from sqlalchemy import and_, text, cast, or_
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlbot_xpack.permissions.models.ds_permission import DsPermission
|
||||
from sqlbot_xpack.permissions.models.ds_rules import DsRules
|
||||
@@ -295,7 +295,8 @@ def get_table_obj_by_ds(session: SessionDep, current_user: CurrentUser, ds: Core
|
||||
# check permission and user in same rules
|
||||
obj = session.query(DsRules).filter(
|
||||
and_(DsRules.permission_list.op('@>')(cast([permission.id], JSONB)),
|
||||
DsRules.user_list.op('@>')(cast([current_user.id], JSONB)))
|
||||
or_(DsRules.user_list.op('@>')(cast([f'{current_user.id}'], JSONB)),
|
||||
DsRules.user_list.op('@>')(cast([current_user.id], JSONB))))
|
||||
).first()
|
||||
if obj is not None:
|
||||
permission_list = json.loads(permission.permissions)
|
||||
|
||||
@@ -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.5",
|
||||
"sqlbot-xpack==0.0.3.6",
|
||||
"fastapi-cache2>=0.2.2",
|
||||
"sqlparse>=0.5.3",
|
||||
]
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
"error": "An error occurred, please try again later",
|
||||
"question_placeholder": "Press Enter to submit, or use Ctrl + Enter for a new line",
|
||||
"greeting": "Hello, I'm SQLBot. How can I assist you today?",
|
||||
"hint_description": "I can query data, generate charts, detect anomalies, predict trends, and more. Please select a data source to begin!",
|
||||
"hint_description": "You can query data, generate charts, analyze data, predict trends, and more. Please select a data source to start your smart data exploration~",
|
||||
"select_datasource": "Select Data Source",
|
||||
"view_more": "View More",
|
||||
"selected_datasource": "Selected Data Source",
|
||||
@@ -96,7 +96,11 @@
|
||||
"chat_search": "Search",
|
||||
"thinking": "Thinking",
|
||||
"thinking_step": "Thought Process",
|
||||
"ask_again": "Regenerate"
|
||||
"ask_again": "Regenerate",
|
||||
"today": "Today",
|
||||
"week": "This Week",
|
||||
"earlier": "Earlier",
|
||||
"no_time": "No Time"
|
||||
},
|
||||
"ds": {
|
||||
"title": "Data Source",
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
"error": "发生错误,请稍后再试",
|
||||
"question_placeholder": "按下 Enter 提交问题, 或使用 Ctrl + Enter 换行",
|
||||
"greeting": "你好,我是SQLBot,很高兴为你服务",
|
||||
"hint_description": "我可以查询数据、生成图表、检测数据异常、预测数据等,请选择一个数据源,开启智能问数吧~",
|
||||
"hint_description": "我可以查询数据、生成图表、分析数据、预测数据等,请选择一个数据源,开启智能问数吧~",
|
||||
"select_datasource": "选择数据源",
|
||||
"view_more": "查看更多",
|
||||
"selected_datasource": "已选择数据源",
|
||||
@@ -103,7 +103,11 @@
|
||||
"chat_search": "搜索",
|
||||
"thinking": "思考中",
|
||||
"thinking_step": "思考过程",
|
||||
"ask_again": "重新生成"
|
||||
"ask_again": "重新生成",
|
||||
"today": "今天",
|
||||
"week": "7天内",
|
||||
"earlier": "更早以前",
|
||||
"no_time": "没有时间"
|
||||
},
|
||||
"ds": {
|
||||
"title": "数据源",
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
import { Delete, EditPen, MoreFilled } from '@element-plus/icons-vue'
|
||||
import { type Chat, chatApi } from '@/api/chat.ts'
|
||||
import { computed } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { getDate } from '@/utils/utils.ts'
|
||||
import { groupBy } from 'lodash-es'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -16,6 +20,64 @@ const props = withDefaults(
|
||||
}
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
function groupByDate(chat: Chat) {
|
||||
const todayStart = dayjs(dayjs().format('YYYY-MM-DD') + ' 00:00:00').toDate()
|
||||
const todayEnd = dayjs(dayjs().format('YYYY-MM-DD') + ' 23:59:59').toDate()
|
||||
const weekStart = dayjs(dayjs().subtract(7, 'day').format('YYYY-MM-DD') + ' 00:00:00').toDate()
|
||||
|
||||
const time = getDate(chat.create_time)
|
||||
|
||||
if (time) {
|
||||
if (time >= todayStart && time <= todayEnd) {
|
||||
return t('qa.today')
|
||||
}
|
||||
if (time < todayStart && time >= weekStart) {
|
||||
return t('qa.week')
|
||||
}
|
||||
if (time < weekStart) {
|
||||
return t('qa.earlier')
|
||||
}
|
||||
}
|
||||
|
||||
return t('qa.no_time')
|
||||
}
|
||||
|
||||
const computedChatGroup = computed(() => {
|
||||
return groupBy(props.chatList, groupByDate)
|
||||
})
|
||||
|
||||
const computedChatList = computed(() => {
|
||||
const _list = []
|
||||
if (computedChatGroup.value[t('qa.today')]) {
|
||||
_list.push({
|
||||
key: t('qa.today'),
|
||||
list: computedChatGroup.value[t('qa.today')],
|
||||
})
|
||||
}
|
||||
if (computedChatGroup.value[t('qa.week')]) {
|
||||
_list.push({
|
||||
key: t('qa.week'),
|
||||
list: computedChatGroup.value[t('qa.week')],
|
||||
})
|
||||
}
|
||||
if (computedChatGroup.value[t('qa.earlier')]) {
|
||||
_list.push({
|
||||
key: t('qa.earlier'),
|
||||
list: computedChatGroup.value[t('qa.earlier')],
|
||||
})
|
||||
}
|
||||
if (computedChatGroup.value[t('qa.no_time')]) {
|
||||
_list.push({
|
||||
key: t('qa.no_time'),
|
||||
list: computedChatGroup.value[t('qa.no_time')],
|
||||
})
|
||||
}
|
||||
|
||||
return _list
|
||||
})
|
||||
|
||||
const emits = defineEmits(['chatSelected', 'chatRenamed', 'chatDeleted', 'update:loading'])
|
||||
|
||||
const _loading = computed({
|
||||
@@ -106,28 +168,31 @@ function handleCommand(command: string | number | object, chat: Chat) {
|
||||
<template>
|
||||
<el-scrollbar ref="chatListRef">
|
||||
<div class="chat-list-inner">
|
||||
<template v-for="chat in chatList" :key="chat.id">
|
||||
<div
|
||||
class="chat-list-item"
|
||||
:class="{ active: currentChatId === chat.id }"
|
||||
@click="onClickHistory(chat)"
|
||||
>
|
||||
<span class="title">{{ chat.brief ?? 'Untitled' }}</span>
|
||||
<el-button class="icon-more" link type="primary" @click.stop>
|
||||
<el-dropdown trigger="click" @command="(cmd: any) => handleCommand(cmd, chat)">
|
||||
<el-icon>
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :icon="EditPen" command="rename">Rename</el-dropdown-item>
|
||||
<el-dropdown-item :icon="Delete" command="delete">Delete</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<div v-for="group in computedChatList" :key="group.key" class="group">
|
||||
<div class="group-title">{{ group.key }}</div>
|
||||
<template v-for="chat in group.list" :key="chat.id">
|
||||
<div
|
||||
class="chat-list-item"
|
||||
:class="{ active: currentChatId === chat.id }"
|
||||
@click="onClickHistory(chat)"
|
||||
>
|
||||
<span class="title">{{ chat.brief ?? 'Untitled' }}</span>
|
||||
<el-button class="icon-more" link type="primary" @click.stop>
|
||||
<el-dropdown trigger="click" @command="(cmd: any) => handleCommand(cmd, chat)">
|
||||
<el-icon>
|
||||
<MoreFilled />
|
||||
</el-icon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :icon="EditPen" command="rename">Rename</el-dropdown-item>
|
||||
<el-dropdown-item :icon="Delete" command="delete">Delete</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
@@ -144,15 +209,31 @@ function handleCommand(command: string | number | object, chat: Chat) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: 8px;
|
||||
gap: 16px;
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.group-title {
|
||||
padding: 0 8px;
|
||||
color: rgba(100, 106, 115, 1);
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-list-item {
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
border-radius: 6px;
|
||||
line-height: 1em;
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -194,6 +194,7 @@ function onChatRenamed(chat: Chat) {
|
||||
name="quick-search"
|
||||
autocomplete="off"
|
||||
:placeholder="t('qa.chat_search')"
|
||||
clearable
|
||||
/>
|
||||
</el-header>
|
||||
<el-main class="chat-list">
|
||||
|
||||
Reference in New Issue
Block a user