Skip to content

Commit e29d6df

Browse files
committed
Merge branch 'main' of https://github.com/dataease/SQLBot
2 parents 070a36e + e6f90a6 commit e29d6df

File tree

6 files changed

+128
-70
lines changed

6 files changed

+128
-70
lines changed

backend/apps/chat/task/llm.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,8 @@ def select_datasource(self):
399399
]
400400
""" _ds_list = self.session.exec(select(CoreDatasource).options(
401401
load_only(CoreDatasource.id, CoreDatasource.name, CoreDatasource.description))).all() """
402-
402+
if not _ds_list:
403+
raise SingleMessageError('No available datasource configuration found')
403404
ignore_auto_select = _ds_list and len(_ds_list) == 1
404405
# ignore auto select ds
405406

@@ -1175,7 +1176,7 @@ def validate_history_ds(self):
11751176
match_ds = any(item.get("id") == _ds.id for item in _ds_list)
11761177
if not match_ds:
11771178
type = self.current_assistant.type
1178-
msg = f"ds is invalid [please check ds list and public ds list]" if type == 0 else f"ds is invalid [please check ds api]"
1179+
msg = f"[please check ds list and public ds list]" if type == 0 else f"[please check ds api]"
11791180
raise SingleMessageError(msg)
11801181
except Exception as e:
11811182
raise SingleMessageError(f"ds is invalid [{str(e)}]")

backend/apps/db/db.py

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,62 @@ def get_session(ds: CoreDatasource | AssistantOutDsSchema):
102102
return session
103103

104104

105-
def check_connection(trans: Optional[Trans], ds: CoreDatasource, is_raise: bool = False):
106-
db = DB.get_db(ds.type)
107-
if db.connect_type == ConnectType.sqlalchemy:
108-
conn = get_engine(ds, 10)
105+
def check_connection(trans: Optional[Trans], ds: CoreDatasource | AssistantOutDsSchema, is_raise: bool = False):
106+
if isinstance(ds, CoreDatasource):
107+
db = DB.get_db(ds.type)
108+
if db.connect_type == ConnectType.sqlalchemy:
109+
conn = get_engine(ds, 10)
110+
try:
111+
with conn.connect() as connection:
112+
SQLBotLogUtil.info("success")
113+
return True
114+
except Exception as e:
115+
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
116+
if is_raise:
117+
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
118+
return False
119+
else:
120+
conf = DatasourceConf(**json.loads(aes_decrypt(ds.configuration)))
121+
if ds.type == 'dm':
122+
with dmPython.connect(user=conf.username, password=conf.password, server=conf.host,
123+
port=conf.port) as conn, conn.cursor() as cursor:
124+
try:
125+
cursor.execute('select 1', timeout=10).fetchall()
126+
SQLBotLogUtil.info("success")
127+
return True
128+
except Exception as e:
129+
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
130+
if is_raise:
131+
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
132+
return False
133+
elif ds.type == 'doris':
134+
with pymysql.connect(user=conf.username, passwd=conf.password, host=conf.host,
135+
port=conf.port, db=conf.database, connect_timeout=10,
136+
read_timeout=10) as conn, conn.cursor() as cursor:
137+
try:
138+
cursor.execute('select 1')
139+
SQLBotLogUtil.info("success")
140+
return True
141+
except Exception as e:
142+
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
143+
if is_raise:
144+
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
145+
return False
146+
elif ds.type == 'redshift':
147+
with redshift_connector.connect(host=conf.host, port=conf.port, database=conf.database, user=conf.username,
148+
password=conf.password,
149+
timeout=10) as conn, conn.cursor() as cursor:
150+
try:
151+
cursor.execute('select 1')
152+
SQLBotLogUtil.info("success")
153+
return True
154+
except Exception as e:
155+
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
156+
if is_raise:
157+
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
158+
return False
159+
else:
160+
conn = get_ds_engine(ds)
109161
try:
110162
with conn.connect() as connection:
111163
SQLBotLogUtil.info("success")
@@ -115,46 +167,8 @@ def check_connection(trans: Optional[Trans], ds: CoreDatasource, is_raise: bool
115167
if is_raise:
116168
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
117169
return False
118-
else:
119-
conf = DatasourceConf(**json.loads(aes_decrypt(ds.configuration)))
120-
if ds.type == 'dm':
121-
with dmPython.connect(user=conf.username, password=conf.password, server=conf.host,
122-
port=conf.port) as conn, conn.cursor() as cursor:
123-
try:
124-
cursor.execute('select 1', timeout=10).fetchall()
125-
SQLBotLogUtil.info("success")
126-
return True
127-
except Exception as e:
128-
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
129-
if is_raise:
130-
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
131-
return False
132-
elif ds.type == 'doris':
133-
with pymysql.connect(user=conf.username, passwd=conf.password, host=conf.host,
134-
port=conf.port, db=conf.database, connect_timeout=10,
135-
read_timeout=10) as conn, conn.cursor() as cursor:
136-
try:
137-
cursor.execute('select 1')
138-
SQLBotLogUtil.info("success")
139-
return True
140-
except Exception as e:
141-
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
142-
if is_raise:
143-
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
144-
return False
145-
elif ds.type == 'redshift':
146-
with redshift_connector.connect(host=conf.host, port=conf.port, database=conf.database, user=conf.username,
147-
password=conf.password,
148-
timeout=10) as conn, conn.cursor() as cursor:
149-
try:
150-
cursor.execute('select 1')
151-
SQLBotLogUtil.info("success")
152-
return True
153-
except Exception as e:
154-
SQLBotLogUtil.error(f"Datasource {ds.id} connection failed: {e}")
155-
if is_raise:
156-
raise HTTPException(status_code=500, detail=trans('i18n_ds_invalid') + f': {e.args}')
157-
return False
170+
171+
return False
158172

159173

160174
def get_version(ds: CoreDatasource | AssistantOutDsSchema):

backend/apps/system/crud/assistant.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def get_assistant_ds(session: Session, llm_service) -> list[dict]:
4444
if public_list:
4545
stmt = stmt.where(CoreDatasource.id.in_(public_list))
4646
else:
47-
raise RuntimeError('no public datasource valid')
47+
return []
4848
""" private_list: list[int] = config.get('private_list') or None
4949
if private_list:
5050
stmt = stmt.where(~CoreDatasource.id.in_(private_list)) """

backend/template.yaml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ template:
2828
不要编造<m-schema>内没有提供给你的表结构
2929
</rule>
3030
<rule>
31-
生成的SQL必须符合<db-engine>提供数据库引擎的规范
31+
生成的SQL必须符合<db-engine>内提供数据库引擎的规范
3232
</rule>
3333
<rule>
3434
若用户提问中提供了参考SQL,你需要判断该SQL是否是查询语句
@@ -74,10 +74,14 @@ template:
7474
如数据库引擎是 MySQL、Doris,则在表名、字段名、别名外层加反引号;
7575
如数据库引擎是 Microsoft SQL Server,则在schema、表名、字段名、别名外层加方括号。
7676
<example>
77-
以PostgreSQL为例,查询Schema为TEST表TABLE下所有数据,则生成的SQL为:
78-
SELECT "id" FROM "TEST"."TABLE"
77+
以PostgreSQL为例,查询Schema为TEST表TABLE下前1000条id字段,则生成的SQL为:
78+
SELECT "id" FROM "TEST"."TABLE" LIMIT 1000
7979
- 注意在表名外双引号的位置,千万不要生成为:
80-
SELECT "id" FROM "TEST.TABLE"
80+
SELECT "id" FROM "TEST.TABLE" LIMIT 1000
81+
以Microsoft SQL Server为例,查询Schema为TEST表TABLE下前1000条id字段,则生成的SQL为:
82+
SELECT TOP 1000 [id] FROM [TEST].[TABLE]
83+
- 注意在表名外方括号的位置,千万不要生成为:
84+
SELECT TOP 1000 [id] FROM [TEST.TABLE]
8185
</example>
8286
</rule>
8387
<rule>
@@ -95,9 +99,13 @@ template:
9599
</rule>
96100
<rule>
97101
如果用户没有指定数据条数的限制,输出的查询SQL需要加上1000条的数据条数限制
98-
</rule>
99-
<rule>
100102
如果用户指定的限制大于1000,则按1000处理
103+
<example>
104+
以PostgreSQL为例,查询Schema为TEST表TABLE下id字段,则生成的SQL为:
105+
SELECT "id" FROM "TEST"."TABLE" LIMIT 1000
106+
以Microsoft SQL Server为例,查询Schema为TEST表TABLE下id字段,则生成的SQL为:
107+
SELECT TOP 1000 [id] FROM [TEST].[TABLE]
108+
</example>
101109
</rule>
102110
<rule>
103111
若需关联多表,优先使用<m-schema>中标记为"Primary key"/"ID"/"主键"的字段作为关联条件。

frontend/public/assistant.js

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,55 @@
157157
closeviewport.classList.remove('sqlbot-assistant-viewportnone')
158158
}
159159
}
160-
const drag = (e) => {
160+
if (data.float_icon_drag) {
161+
chat_button.setAttribute('draggable', 'true')
162+
163+
let startX = 0
164+
let startY = 0
165+
const img = new Image()
166+
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='
167+
chat_button.addEventListener('dragstart', (e) => {
168+
startX = e.clientX - chat_button.offsetLeft
169+
startY = e.clientY - chat_button.offsetTop
170+
e.dataTransfer.setDragImage(img, 0, 0)
171+
})
172+
173+
chat_button.addEventListener('drag', (e) => {
174+
if (e.clientX && e.clientY) {
175+
const left = e.clientX - startX
176+
const top = e.clientY - startY
177+
178+
const maxX = window.innerWidth - chat_button.offsetWidth
179+
const maxY = window.innerHeight - chat_button.offsetHeight
180+
181+
chat_button.style.left = Math.min(Math.max(0, left), maxX) + 'px'
182+
chat_button.style.top = Math.min(Math.max(0, top), maxY) + 'px'
183+
}
184+
})
185+
186+
let touchStartX = 0
187+
let touchStartY = 0
188+
189+
chat_button.addEventListener('touchstart', (e) => {
190+
touchStartX = e.touches[0].clientX - chat_button.offsetLeft
191+
touchStartY = e.touches[0].clientY - chat_button.offsetTop
192+
e.preventDefault()
193+
})
194+
195+
chat_button.addEventListener('touchmove', (e) => {
196+
const left = e.touches[0].clientX - touchStartX
197+
const top = e.touches[0].clientY - touchStartY
198+
199+
const maxX = window.innerWidth - chat_button.offsetWidth
200+
const maxY = window.innerHeight - chat_button.offsetHeight
201+
202+
chat_button.style.left = Math.min(Math.max(0, left), maxX) + 'px'
203+
chat_button.style.top = Math.min(Math.max(0, top), maxY) + 'px'
204+
205+
e.preventDefault()
206+
})
207+
}
208+
/* const drag = (e) => {
161209
if (['touchmove', 'touchstart'].includes(e.type)) {
162210
chat_button.style.top = e.touches[0].clientY - chat_button_img.clientHeight / 2 + 'px'
163211
chat_button.style.left = e.touches[0].clientX - chat_button_img.clientHeight / 2 + 'px'
@@ -169,14 +217,15 @@
169217
chat_button.style.height = chat_button_img.clientHeight + 'px'
170218
}
171219
if (data.float_icon_drag) {
220+
chat_button.setAttribute('draggable', 'true')
172221
chat_button.addEventListener('drag', drag)
173222
chat_button.addEventListener('dragover', (e) => {
174223
e.preventDefault()
175224
})
176225
chat_button.addEventListener('dragend', drag)
177226
chat_button.addEventListener('touchstart', drag)
178227
chat_button.addEventListener('touchmove', drag)
179-
}
228+
} */
180229
viewport.onclick = viewport_func
181230
closeviewport.onclick = viewport_func
182231
}

installer/install.conf

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,6 @@ SQLBOT_BASE=/opt
55
SQLBOT_WEB_PORT=8000
66
SQLBOT_MCP_PORT=8001
77

8-
# 数据库配置
9-
## 是否使用外部数据库
10-
SQLBOT_EXTERNAL_DB=false
11-
## 数据库地址
12-
SQLBOT_DB_HOST=localhost
13-
## 数据库端口 (仅使用外部数据库时才生效)
14-
SQLBOT_DB_PORT=5432
15-
## SQLBot 数据库库名
16-
SQLBOT_DB_DB=sqlbot
17-
## 数据库用户名
18-
SQLBOT_DB_USER=root
19-
## 数据库密码,密码如包含特殊字符,请用双引号引起来,例如 SQLBOT_DB_PASSWORD="Test@4&^%*^"
20-
SQLBOT_DB_PASSWORD=Password123@pg
21-
228
# 其他配置
239
## 普通用户默认密码
2410
SQLBOT_DEFAULT_PWD=SQLBot@123456
@@ -31,4 +17,4 @@ SQLBOT_LOG_LEVEL="INFO"
3117
## 缓存类型
3218
SQLBOT_CACHE_TYPE="memory"
3319
## MCP 图片存储路径
34-
SQLBOT_SERVER_IMAGE_HOST=https://YOUR_SERVER_IP:MCP_PORT/images/
20+
SQLBOT_SERVER_IMAGE_HOST=https://YOUR_SERVER_IP:MCP_PORT/images/

0 commit comments

Comments
 (0)