Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
chat-query-app
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
jaden
chat-query-app
Commits
67b9ea7e
Commit
67b9ea7e
authored
Mar 17, 2025
by
王昆
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
import from dbs in ziwei
parent
ebe1acd9
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
744 additions
and
627 deletions
+744
-627
chat-query/components/decrypt.js
chat-query/components/decrypt.js
+57
-0
chat-query/components/import_modal.js
chat-query/components/import_modal.js
+109
-6
chat-query/components/list_nav.js
chat-query/components/list_nav.js
+5
-5
chat-query/next-i18next.config.js
chat-query/next-i18next.config.js
+1
-0
chat-query/next.config.js
chat-query/next.config.js
+4
-0
chat-query/package.json
chat-query/package.json
+2
-1
chat-query/pnpm-lock.yaml
chat-query/pnpm-lock.yaml
+566
-615
No files found.
chat-query/components/decrypt.js
0 → 100644
View file @
67b9ea7e
async
function
decryptDataWithPrivateKey
(
privateKey
,
encryptedBase64
)
{
// 将 Base64 编码的加密数据解码为 ArrayBuffer
const
encryptedData
=
base64ToArrayBuffer
(
encryptedBase64
);
// 使用 RSA-OAEP 解密数据
const
decryptedBuffer
=
await
crypto
.
subtle
.
decrypt
(
{
name
:
"
RSA-OAEP
"
},
privateKey
,
// 使用私钥解密
encryptedData
);
// 将解密后的数据转换为字符串
return
new
TextDecoder
().
decode
(
decryptedBuffer
);
}
// 将 Base64 转换为 ArrayBuffer
function
base64ToArrayBuffer
(
base64
)
{
const
binaryString
=
atob
(
base64
);
const
length
=
binaryString
.
length
;
const
arrayBuffer
=
new
ArrayBuffer
(
length
);
const
uint8Array
=
new
Uint8Array
(
arrayBuffer
);
for
(
let
i
=
0
;
i
<
length
;
i
++
)
{
uint8Array
[
i
]
=
binaryString
.
charCodeAt
(
i
);
}
return
arrayBuffer
;
}
async
function
importPrivateKey
(
privateKeyBase64
)
{
// 1. Base64 解码(转换为 ArrayBuffer)
const
binaryDer
=
atob
(
privateKeyBase64
);
const
keyBuffer
=
new
Uint8Array
(
binaryDer
.
length
).
map
((
_
,
i
)
=>
binaryDer
.
charCodeAt
(
i
));
// 2. 使用 Web Crypto API 解析 PKCS#8 私钥
return
await
crypto
.
subtle
.
importKey
(
"
pkcs8
"
,
keyBuffer
.
buffer
,
{
name
:
"
RSA-OAEP
"
,
hash
:
"
SHA-256
"
},
// 确保 Java 端使用 "RSA-OAEP" 加密
true
,
[
"
decrypt
"
]
);
}
async
function
decryptMessage
(
privateKey
,
encryptedAesKey
)
{
// 导入私钥
const
pk
=
await
importPrivateKey
(
privateKey
);
// console.log("私钥解析成功: ", pk);
// 用私钥解密数据
const
decryptedData
=
await
decryptDataWithPrivateKey
(
pk
,
encryptedAesKey
);
// console.log("解密后的数据:", decryptedData);
return
decryptedData
;
}
export
{
decryptMessage
}
\ No newline at end of file
chat-query/components/import_modal.js
View file @
67b9ea7e
import
{
Modal
,
Notification
,
Select
,
Tabs
}
from
'
@arco-design/web-react
'
;
import
{
Modal
,
Notification
,
Select
,
Tabs
,
Spin
}
from
'
@arco-design/web-react
'
;
import
{
Parser
}
from
'
@dbml/core
'
;
import
{
Parser
}
from
'
@dbml/core
'
;
import
{
useRef
,
useState
}
from
'
react
'
;
import
{
useRef
,
useState
}
from
'
react
'
;
import
{
nanoid
}
from
'
nanoid
'
;
import
{
nanoid
}
from
'
nanoid
'
;
...
@@ -12,6 +12,8 @@ import { useEffect } from 'react';
...
@@ -12,6 +12,8 @@ import { useEffect } from 'react';
import
{
useMemo
}
from
'
react
'
;
import
{
useMemo
}
from
'
react
'
;
import
ConnectDb
from
'
@/client/api/connectDb
'
;
import
ConnectDb
from
'
@/client/api/connectDb
'
;
import
useSWRMutation
from
'
swr/mutation
'
;
import
useSWRMutation
from
'
swr/mutation
'
;
import
{
useSearchParams
}
from
'
next/navigation
'
;
import
{
decryptMessage
}
from
'
./decrypt
'
;
const
TabPane
=
Tabs
.
TabPane
;
const
TabPane
=
Tabs
.
TabPane
;
...
@@ -62,8 +64,8 @@ export function DBForm({
...
@@ -62,8 +64,8 @@ export function DBForm({
style
=
{{
width
:
'
100%
'
}}
style
=
{{
width
:
'
100%
'
}}
initialValues
=
{
initialValues
}
initialValues
=
{
initialValues
}
autoComplete
=
"
off
"
autoComplete
=
"
off
"
onValuesChange
=
{(
v
,
vs
)
=>
{}}
onValuesChange
=
{(
v
,
vs
)
=>
{
}}
onSubmit
=
{
v
=>
{}}
onSubmit
=
{
v
=>
{
}}
className
=
"
p-[20px]
"
className
=
"
p-[20px]
"
>
>
<
FormItem
label
=
{
t
(
'
Connection Information
'
)}
rules
=
{[{
required
:
true
}]}
>
<
FormItem
label
=
{
t
(
'
Connection Information
'
)}
rules
=
{[{
required
:
true
}]}
>
...
@@ -92,6 +94,10 @@ export function DBForm({
...
@@ -92,6 +94,10 @@ export function DBForm({
label
:
'
mysql
'
,
label
:
'
mysql
'
,
value
:
'
mysql2
'
,
value
:
'
mysql2
'
,
},
},
{
label
:
'
oracle
'
,
value
:
'
oracle
'
,
},
]}
]}
><
/Select
>
><
/Select
>
<
/Form.Item
>
<
/Form.Item
>
...
@@ -168,7 +174,7 @@ function bfs(nodes) {
...
@@ -168,7 +174,7 @@ function bfs(nodes) {
* It's a modal that allows you to import a graph from a string
* It's a modal that allows you to import a graph from a string
* @returns Modal component
* @returns Modal component
*/
*/
export
default
function
ImportModal
({
showModal
,
onCloseModal
,
cb
=
_p
=>
{},
type
,
importDBML
})
{
export
default
function
ImportModal
({
showModal
,
onCloseModal
,
cb
=
_p
=>
{
},
type
,
importDBML
})
{
const
{
t
}
=
useTranslation
(
'
modal
'
);
const
{
t
}
=
useTranslation
(
'
modal
'
);
const
{
theme
,
setTableDict
,
setLinkDict
,
tableList
}
=
graphState
.
useContainer
();
const
{
theme
,
setTableDict
,
setLinkDict
,
tableList
}
=
graphState
.
useContainer
();
const
{
calcXY
}
=
tableModel
();
const
{
calcXY
}
=
tableModel
();
...
@@ -205,6 +211,8 @@ export default function ImportModal({ showModal, onCloseModal, cb = _p => {}, ty
...
@@ -205,6 +211,8 @@ export default function ImportModal({ showModal, onCloseModal, cb = _p => {}, ty
}
else
if
(
type
===
4
)
{
}
else
if
(
type
===
4
)
{
console
.
log
(
'
excel
'
);
console
.
log
(
'
excel
'
);
setImportType
(
'
excel
'
);
setImportType
(
'
excel
'
);
}
else
if
(
type
===
5
)
{
setImportType
(
'
ziwei
'
);
}
}
},
[
type
]);
},
[
type
]);
...
@@ -222,7 +230,12 @@ export default function ImportModal({ showModal, onCloseModal, cb = _p => {}, ty
...
@@ -222,7 +230,12 @@ export default function ImportModal({ showModal, onCloseModal, cb = _p => {}, ty
console
.
log
(
lists
,
'
lists
'
);
console
.
log
(
lists
,
'
lists
'
);
if
(
!
value
&&
!
(
lists
.
length
&&
type
===
4
))
{
if
(
!
value
&&
!
(
lists
.
length
&&
type
===
4
))
{
onCloseModal
();
if
(
type
===
5
)
{
const
formValues
=
form
.
getFields
();
trigger
(
formValues
);
}
else
{
onCloseModal
();
}
return
;
return
;
}
}
try
{
try
{
...
@@ -417,6 +430,62 @@ export default function ImportModal({ showModal, onCloseModal, cb = _p => {}, ty
...
@@ -417,6 +430,62 @@ export default function ImportModal({ showModal, onCloseModal, cb = _p => {}, ty
);
);
const
[
lists
,
setList
]
=
useState
([]);
const
[
lists
,
setList
]
=
useState
([]);
const
[
selectedDbIndex
,
setSelectedDbIndex
]
=
useState
(
0
);
const
[
dbList
,
setDbList
]
=
useState
([]);
const
searchParams
=
useSearchParams
();
const
applicationId
=
searchParams
.
get
(
'
applicationId
'
);
useEffect
(()
=>
{
if
(
type
===
5
&&
applicationId
)
{
setLoading
(
true
);
// 开始加载时显示loading
fetch
(
`/ziwei/api/v1/datasources?applicationId=
${
applicationId
}
`
,
{
method
:
"
GET
"
,
credentials
:
"
include
"
,
}).
finally
(()
=>
{
setLoading
(
false
);
// 请求完成后隐藏loading
})
.
then
(
res
=>
res
.
json
())
.
then
(
async
data
=>
{
if
(
data
?.
data
&&
Array
.
isArray
(
data
?.
data
))
{
const
formattedData
=
await
Promise
.
all
(
data
.
data
.
map
(
async
item
=>
{
// 从 datasourceConfiguration 中获取连接信息
const
endpoint
=
item
.
datasourceConfiguration
?.
endpoints
?.[
0
]
||
{};
// 获取认证信息,包含加密的密钥
const
auth
=
item
.
datasourceConfiguration
?.
authentication
||
{};
// 如果存在加密密钥,需要进行解密
if
(
auth
.
privateKey
&&
auth
.
encryptedAesKey
)
{
try
{
// 解密密码
auth
.
password
=
await
decryptMessage
(
auth
.
privateKey
,
auth
.
encryptedAesKey
)
}
catch
(
error
)
{
console
.
error
(
'
Failed to decrypt keys:
'
,
error
);
Message
.
error
(
t
(
'
Failed to decrypt database credentials
'
));
}
}
return
{
host
:
endpoint
.
host
||
'
127.0.0.1
'
,
port
:
endpoint
.
port
||
3306
,
user
:
auth
.
username
||
'
root
'
,
password
:
auth
.
password
||
''
,
database
:
auth
.
databaseName
||
''
,
name
:
item
.
name
||
''
,
client
:
item
.
dbType
===
1
?
'
oracle
'
:
'
mysql2
'
// 根据 dbType 判断数据库类型
};
}));
setDbList
(
formattedData
);
if
(
formattedData
.
length
>
0
)
{
form
.
setFieldsValue
(
formattedData
[
0
]);
}
}
})
.
catch
(
error
=>
{
console
.
error
(
'
Error fetching datasources:
'
,
error
);
Message
.
error
(
t
(
'
Failed to fetch datasources
'
));
});
}
},
[
type
,
applicationId
]);
const
tabs
=
useMemo
(()
=>
{
const
tabs
=
useMemo
(()
=>
{
const
result
=
[];
const
result
=
[];
if
(
type
===
1
||
!
type
)
{
if
(
type
===
1
||
!
type
)
{
...
@@ -449,9 +518,43 @@ export default function ImportModal({ showModal, onCloseModal, cb = _p => {}, ty
...
@@ -449,9 +518,43 @@ export default function ImportModal({ showModal, onCloseModal, cb = _p => {}, ty
<
ImportExcel
setList
=
{
setList
}
/
>
<
ImportExcel
setList
=
{
setList
}
/
>
<
/TabPane
>
<
/TabPane
>
);
);
}
else
if
(
type
===
5
)
{
result
.
push
(
<
TabPane
key
=
"
ziwei
"
title
=
"
紫微数据库
"
>
<
div
className
=
"
space-y-4
"
>
{
loading
?
(
<
div
className
=
"
flex items-center justify-center h-[360px]
"
>
<
Spin
/>
<
/div
>
)
:
(
<>
<
Select
placeholder
=
"
请选择数据库
"
value
=
{
selectedDbIndex
}
onChange
=
{
value
=>
{
setSelectedDbIndex
(
value
);
form
.
setFieldsValue
(
dbList
[
value
]);
}}
style
=
{{
width
:
'
100%
'
,
marginBottom
:
'
16px
'
}}
>
{
dbList
.
map
((
db
,
index
)
=>
(
<
Select
.
Option
key
=
{
index
}
value
=
{
index
}
>
{
db
.
name
}
({
db
.
host
}:{
db
.
port
})
<
/Select.Option
>
))}
<
/Select
>
<
DBForm
form
=
{
form
}
initialValues
=
{
dbList
[
selectedDbIndex
]}
/
>
<
/
>
)}
<
/div
>
<
/TabPane
>
);
}
}
return
result
;
return
result
;
},
[
type
]);
},
[
type
,
selectedDbIndex
,
dbList
,
loading
]);
return
(
return
(
<
Modal
<
Modal
title
=
{
null
}
title
=
{
null
}
...
...
chat-query/components/list_nav.js
View file @
67b9ea7e
...
@@ -38,7 +38,10 @@ export default function ListNav({
...
@@ -38,7 +38,10 @@ export default function ListNav({
key
=
{
0
}
key
=
{
0
}
trigger
=
{
'
hover
'
}
trigger
=
{
'
hover
'
}
droplist
=
{
droplist
=
{
<
Menu
>
<
Menu
style
=
{{
maxHeight
:
'
none
'
,
overflow
:
'
visible
'
}}
>
<
Menu
.
Item
key
=
"
ziwei
"
onClick
=
{()
=>
importGraph
(
5
)}
>
{
t
(
'
Import from Ziwei
'
)}
<
/Menu.Item
>
<
Menu
.
Item
key
=
"
3
"
onClick
=
{()
=>
importGraph
(
3
)}
>
<
Menu
.
Item
key
=
"
3
"
onClick
=
{()
=>
importGraph
(
3
)}
>
{
t
(
'
Import Database
'
)}
{
t
(
'
Import Database
'
)}
<
/Menu.Item
>
<
/Menu.Item
>
...
@@ -49,12 +52,9 @@ export default function ListNav({
...
@@ -49,12 +52,9 @@ export default function ListNav({
{
t
(
'
Import DDL
'
)}
{
t
(
'
Import DDL
'
)}
<
/Menu.Item
>
<
/Menu.Item
>
<
Menu
.
Item
key
=
"
1
"
onClick
=
{()
=>
setSimpleModeVisible
(
true
)}
>
<
Menu
.
Item
key
=
"
1
"
onClick
=
{()
=>
setSimpleModeVisible
(
true
)}
>
{
/* <span className=" text-lime-600 mr-[10px]">
<IconRobot />
</span> */
}
{
t
(
'
Created by AI
'
)}
{
t
(
'
Created by AI
'
)}
<
/Menu.Item
>
<
/Menu.Item
>
<
Menu
.
Item
key
=
"
1
"
onClick
=
{()
=>
addGraph
()}
>
<
Menu
.
Item
key
=
"
5
"
onClick
=
{()
=>
addGraph
()}
>
{
t
(
'
Create from Blank
'
)}
{
t
(
'
Create from Blank
'
)}
<
/Menu.Item
>
<
/Menu.Item
>
<
/Menu
>
<
/Menu
>
...
...
chat-query/next-i18next.config.js
View file @
67b9ea7e
...
@@ -30,6 +30,7 @@ i18n.use(LanguageDetector)
...
@@ -30,6 +30,7 @@ i18n.use(LanguageDetector)
'
Import DBML
'
:
'
导入DBML
'
,
'
Import DBML
'
:
'
导入DBML
'
,
'
Import DDL
'
:
'
导入DDL
'
,
'
Import DDL
'
:
'
导入DDL
'
,
'
Import excel
'
:
'
导入excel
'
,
'
Import excel
'
:
'
导入excel
'
,
'
Import from Ziwei
'
:
'
从紫微导入
'
,
'
Create from Blank
'
:
'
从空白新建
'
,
'
Create from Blank
'
:
'
从空白新建
'
,
'
New Model
'
:
'
新建模型
'
,
'
New Model
'
:
'
新建模型
'
,
'
Import Example
'
:
'
导入示例
'
,
'
Import Example
'
:
'
导入示例
'
,
...
...
chat-query/next.config.js
View file @
67b9ea7e
...
@@ -21,6 +21,10 @@ const rewrites = async () => {
...
@@ -21,6 +21,10 @@ const rewrites = async () => {
destination
:
`
${
process
.
env
.
NEXT_PUBLIC_BACKEND_URL
}
/:path*`
,
destination
:
`
${
process
.
env
.
NEXT_PUBLIC_BACKEND_URL
}
/:path*`
,
// destination: 'http://139.198.179.193:30981/:path*',
// destination: 'http://139.198.179.193:30981/:path*',
},
},
{
source
:
'
/ziwei/:path*
'
,
destination
:
`https://staging.fusiontech.cn/:path*`
,
},
];
];
return
{
return
{
...
...
chat-query/package.json
View file @
67b9ea7e
...
@@ -27,7 +27,7 @@
...
@@ -27,7 +27,7 @@
"@antv/x6-vue-shape"
:
"^2.0.0"
,
"@antv/x6-vue-shape"
:
"^2.0.0"
,
"@arco-design/web-react"
:
"^2.45.0"
,
"@arco-design/web-react"
:
"^2.45.0"
,
"@dbml/core"
:
"^2.5.1"
,
"@dbml/core"
:
"^2.5.1"
,
"@digitalocean/do-markdownit"
:
"
^1.9
.0"
,
"@digitalocean/do-markdownit"
:
"
1.16
.0"
,
"@formily/core"
:
"^2.2.29"
,
"@formily/core"
:
"^2.2.29"
,
"@formily/react"
:
"^2.2.29"
,
"@formily/react"
:
"^2.2.29"
,
"@koale/useworker"
:
"^4.0.2"
,
"@koale/useworker"
:
"^4.0.2"
,
...
@@ -63,6 +63,7 @@
...
@@ -63,6 +63,7 @@
"pinyin"
:
"3.0.0-alpha.5"
,
"pinyin"
:
"3.0.0-alpha.5"
,
"postcss-flexbugs-fixes"
:
"^5.0.2"
,
"postcss-flexbugs-fixes"
:
"^5.0.2"
,
"postcss-preset-env"
:
"^8.0.1"
,
"postcss-preset-env"
:
"^8.0.1"
,
"prismjs"
:
"^1.30.0"
,
"react"
:
"18.2.0"
,
"react"
:
"18.2.0"
,
"react-dnd"
:
"^16.0.1"
,
"react-dnd"
:
"^16.0.1"
,
"react-dnd-html5-backend"
:
"^16.0.1"
,
"react-dnd-html5-backend"
:
"^16.0.1"
,
...
...
chat-query/pnpm-lock.yaml
View file @
67b9ea7e
This source diff could not be displayed because it is too large. You can
view the blob
instead.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment