Merge remote-tracking branch 'origin/developing' into developing

This commit is contained in:
JiaJu Zhuang
2023-07-09 19:18:29 +08:00
25 changed files with 272 additions and 191 deletions

View File

@ -24,8 +24,8 @@ enum DownloadStatus {
export default memo<IProps>(function Driver(props) {
const { className, backfillData, onChange } = props;
const [downloadStatus, setDownloadStatus] = useState<DownloadStatus>();
const [driveForm] = Form.useForm();
const [downloadStatus, setDownloadStatus] = useState<DownloadStatus>(DownloadStatus.Default);
const [driverForm] = Form.useForm();
const [driverObj, setDriverObj] = useState<IDriverResponse>();
const [uploadDriverModal, setUploadDriverModal] = useState(false);
const [driverSaved, setDriverSaved] = useState<any>({});
@ -38,7 +38,7 @@ export default memo<IProps>(function Driver(props) {
useEffect(() => {
if (backfillData) {
driveForm.setFieldsValue({
driverForm.setFieldsValue({
jdbcDriverClass: backfillData?.driverConfig?.jdbcDriverClass,
jdbcDriver: backfillData?.driverConfig?.jdbcDriver
})
@ -47,7 +47,16 @@ export default memo<IProps>(function Driver(props) {
function getDriverList() {
connectionService.getDriverList({ dbType: backfillData.type }).then(res => {
setDriverObj(res)
setDriverObj({
...res,
driverConfigList: res.driverConfigList || []
});
if (res.driverConfigList?.length && !backfillData?.driverConfig?.jdbcDriver) {
driverForm.setFieldsValue({
jdbcDriverClass: res.driverConfigList[0].jdbcDriverClass,
jdbcDriver: res.driverConfigList[0].jdbcDriver
})
}
})
}
@ -65,7 +74,7 @@ export default memo<IProps>(function Driver(props) {
function downloadDrive() {
setDownloadStatus(DownloadStatus.Loading)
connectionService.downloadDriver({ dbType: backfillData.type }).then(res => {
// setDownloadStatus(DownloadStatus.Success)
setDownloadStatus(DownloadStatus.Success);
getDriverList();
}).catch(() => {
setDownloadStatus(DownloadStatus.Error)
@ -74,7 +83,7 @@ export default memo<IProps>(function Driver(props) {
function onValuesChange(data: any) {
const selected = driverObj?.driverConfigList.find(t => t.jdbcDriver === data.jdbcDriver);
driveForm.setFieldsValue({
driverForm.setFieldsValue({
jdbcDriverClass: selected?.jdbcDriverClass
});
onChange({
@ -85,7 +94,7 @@ export default memo<IProps>(function Driver(props) {
return <div className={classnames(styles.box, className)}>
<Form
form={driveForm}
form={driverForm}
onValuesChange={onValuesChange}
colon={false}
>
@ -104,7 +113,7 @@ export default memo<IProps>(function Driver(props) {
</Form>
<div className={styles.downloadDriveFooter}>
{
(!driverObj?.driverConfigList?.length || downloadStatus === DownloadStatus.Success) ? <div onClick={downloadDrive} className={styles.downloadDrive}>
((driverObj?.driverConfigList && !driverObj?.driverConfigList?.length) || downloadStatus === DownloadStatus.Success) ? <div onClick={downloadDrive} className={styles.downloadDrive}>
{
(downloadStatus === DownloadStatus.Default) && <div className={classnames(styles.downloadText, styles.downloadTextDownload)}>{i18n('connection.text.downloadDriver')}</div>
}
@ -124,7 +133,7 @@ export default memo<IProps>(function Driver(props) {
(downloadStatus === DownloadStatus.Success) && <div className={classnames(styles.downloadText, styles.downloadTextSuccess)}>{i18n('connection.text.downloadSuccess')}</div>
}
</div> : <div></div>
</div> : <div />
}
<div

View File

@ -5,7 +5,7 @@ import { InputType, AuthenticationType, SSHAuthenticationType, OperationColumn }
export const sshConfig: IConnectionConfig['ssh'] = {
items: [
{
defaultValue: 'false',
defaultValue: false,
inputType: InputType.SELECT,
labelNameCN: '使用SSH',
labelNameEN: 'USE SSH',
@ -13,10 +13,12 @@ export const sshConfig: IConnectionConfig['ssh'] = {
required: false,
selects: [
{
value: 'false',
label: 'false',
value: false,
},
{
value: 'true',
label: 'true',
value: true,
},
],
@ -33,7 +35,7 @@ export const sshConfig: IConnectionConfig['ssh'] = {
}
},
{
defaultValue: '',
defaultValue: '22',
inputType: InputType.INPUT,
labelNameCN: 'SSH 端口',
labelNameEN: 'Port',
@ -55,7 +57,6 @@ export const sshConfig: IConnectionConfig['ssh'] = {
required: false,
styles: {
width: '70%',
}
},
{
@ -64,6 +65,8 @@ export const sshConfig: IConnectionConfig['ssh'] = {
labelNameCN: '本地端口',
labelNameEN: 'LocalPort',
name: 'localPort',
placeholder: '不必填',
placeholderEN: 'Need not fill in',
required: false,
styles: {
width: '30%',
@ -71,14 +74,57 @@ export const sshConfig: IConnectionConfig['ssh'] = {
}
},
{
defaultValue: '',
inputType: InputType.PASSWORD,
labelNameCN: '密码',
labelNameEN: 'Password',
name: 'password',
defaultValue: 'password',
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authenticationType',
required: true,
selects: [
{
items: [
{
defaultValue: '',
inputType: InputType.PASSWORD,
labelNameCN: '密码',
labelNameEN: 'Password',
name: 'password',
required: true,
},
],
label: 'password',
value: 'password',
},
{
items: [
{
defaultValue: '',
inputType: InputType.INPUT,
labelNameCN: '密钥文件',
labelNameEN: 'Private key file',
name: 'keyFile',
required: true,
placeholder: '/user/userName/.ssh/xxxx',
placeholderEN: '/user/userName/.ssh/xxxx',
},
{
defaultValue: '',
inputType: InputType.INPUT,
labelNameCN: '密码短语',
labelNameEN: 'Passphrase',
name: 'passphrase',
required: true,
},
],
label: 'Private key',
value: 'keyFile',
},
],
styles: {
width: '50%',
}
},
]
}
@ -126,7 +172,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -154,6 +200,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -235,7 +283,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -266,6 +314,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -295,85 +345,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
pattern: /jdbc:postgresql:\/\/(.*):(\d+)(\/(\w+))?/,
template: 'jdbc:postgresql://{host}:{port}/{database}',
},
ssh: {
items: [
{
defaultValue: 'false',
inputType: InputType.SELECT,
labelNameCN: '使用SSH',
labelNameEN: 'USE SSH',
name: 'use',
required: false,
selects: [
{
value: 'false',
},
{
value: 'true',
},
],
},
{
defaultValue: '',
inputType: InputType.INPUT,
labelNameCN: 'SSH 主机',
labelNameEN: 'SSH Hostname',
name: 'hostName',
required: false,
styles: {
width: '70%',
}
},
{
defaultValue: '',
inputType: InputType.INPUT,
labelNameCN: 'SSH 端口',
labelNameEN: 'Port',
name: 'port',
required: false,
styles: {
width: '30%',
labelWidthEN: '40px',
labelWidthCN: '40px',
labelAlign: 'right'
}
},
{
defaultValue: '',
inputType: InputType.INPUT,
labelNameCN: '用户名',
labelNameEN: 'SSH UserName',
name: 'userName',
required: false,
styles: {
width: '70%',
}
},
{
defaultValue: '',
inputType: InputType.INPUT,
labelNameCN: '本地端口',
labelNameEN: 'LocalPort',
name: 'localPort',
required: false,
styles: {
width: '30%',
labelAlign: 'right'
}
},
{
defaultValue: '',
inputType: InputType.PASSWORD,
labelNameCN: '密码',
labelNameEN: 'Password',
name: 'password',
required: true,
},
]
},
ssh: sshConfig,
},
// ORACLE
{
@ -459,7 +431,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -490,6 +462,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -558,7 +532,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -590,6 +564,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -694,7 +670,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -726,6 +702,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -841,7 +819,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -873,6 +851,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -950,7 +930,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -981,7 +961,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1059,7 +1040,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -1080,7 +1061,6 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
labelNameEN: 'Password',
name: 'password',
required: true,
},
],
label: 'User&Password',
@ -1089,6 +1069,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1173,7 +1154,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -1207,6 +1188,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1292,7 +1275,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -1326,6 +1309,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1411,7 +1396,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -1445,6 +1430,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1530,7 +1517,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -1564,6 +1551,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1649,7 +1638,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -1683,6 +1672,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1768,7 +1759,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -1802,6 +1793,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1887,7 +1880,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
inputType: InputType.SELECT,
labelNameCN: '身份验证',
labelNameEN: 'Authentication',
name: 'authentication',
name: 'authenticationType',
required: true,
selects: [
{
@ -1921,6 +1914,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {

View File

@ -5,8 +5,8 @@ export enum InputType {
}
export enum AuthenticationType {
USERANDPASSWORD = 1,
NONE = 2,
USERANDPASSWORD = '1',
NONE = '2',
}
export enum SSHAuthenticationType {

View File

@ -2,7 +2,7 @@ import { InputType, AuthenticationType, SSHAuthenticationType } from './enum';
import { DatabaseTypeCode, OperationColumn } from '@/constants';
export type ISelect = {
value?: AuthenticationType | SSHAuthenticationType | string;
value?: AuthenticationType | SSHAuthenticationType | string | boolean;
label?: string;
rest?: {
[key in string]: any
@ -20,6 +20,8 @@ export interface IFormItem {
selected?: any;
selects?: ISelect[];
labelTextAlign?: 'right';
placeholder?: string;
placeholderEN?: string;
styles?: {
width?: string; // 表单占用的长度 推荐百分比 默认值为 100%
labelWidthEN?: string; // 英文环境下表单label的长度 推荐px 默认值为 70px

View File

@ -103,6 +103,7 @@
color: var(--success-color);
}
.testSSHConnectText {
margin-left: 4px;
font-size: 12px;
color: var(--color-primary);
cursor: pointer;

View File

@ -3,17 +3,19 @@ import { i18n, isEn } from '@/i18n';
import styles from './index.less';
import classnames from 'classnames';
import connectionService, { IDriverResponse } from '@/service/connection';
import connectionService from '@/service/connection';
import { DatabaseTypeCode, ConnectionEnvType, databaseMap } from '@/constants';
import { dataSourceFormConfigs } from './config/dataSource';
import { IConnectionConfig, IFormItem, ISelect } from './config/types';
import { AuthenticationType } from './config/enum';
import { IConnectionDetails } from '@/typings';
import { InputType } from './config/enum';
import { deepClone } from '@/utils';
import { Select, Form, Input, message, Table, Button, Collapse, Modal } from 'antd';
import Iconfont from '@/components/Iconfont';
import LoadingContent from '@/components/Loading/LoadingContent';
import LoadingGracile from '@/components/Loading/LoadingGracile';
import Driver from './components/Driver';
const { Option } = Select;
@ -33,13 +35,6 @@ interface IProps {
submitCallback?: Function;
}
enum DownloadStatus {
Default,
Loading,
Error,
Success
}
export default function CreateConnection(props: IProps) {
const { className, closeCreateConnection, submitCallback, connectionData } = props;
const [baseInfoForm] = Form.useForm();
@ -49,7 +44,8 @@ export default function CreateConnection(props: IProps) {
const [loadings, setLoading] = useState({
confirmButton: false,
testButton: false,
backfillDataLoading: false
backfillDataLoading: false,
sshTestLoading: false
});
const dataSourceFormConfigPropsMemo = useMemo<IConnectionConfig>(() => {
const deepCloneDataSourceFormConfigs = deepClone(dataSourceFormConfigs)
@ -80,11 +76,6 @@ export default function CreateConnection(props: IProps) {
if (!res) {
return;
}
if (res.user) {
res.authentication = 1;
} else {
res.authentication = 2;
}
setBackfillData(res);
}).finally(() => {
setTimeout(() => {
@ -102,12 +93,14 @@ export default function CreateConnection(props: IProps) {
const getItems = () => [
{
forceRender: true,
key: 'driver',
label: i18n('connection.title.driver'),
children: <Driver backfillData={backfillData} onChange={driverFormChange}></Driver>,
},
{
key: 'ssh',
forceRender: true,
label: i18n('connection.label.sshConfiguration'),
children: (
<div className={styles.sshBox}>
@ -118,6 +111,7 @@ export default function CreateConnection(props: IProps) {
tab="ssh"
/>
<div className={styles.testSSHConnect}>
{loadings.sshTestLoading && <LoadingGracile></LoadingGracile>}
<div onClick={testSSH} className={styles.testSSHConnectText}>
{i18n('connection.message.testSshConnection')}
</div>
@ -126,6 +120,7 @@ export default function CreateConnection(props: IProps) {
),
},
{
forceRender: true,
key: 'extendInfo',
label: i18n('connection.label.advancedConfiguration'),
children: (
@ -209,9 +204,19 @@ export default function CreateConnection(props: IProps) {
}
function testSSH() {
let p = sshForm.getFieldsValue();
setLoading({
...loadings,
sshTestLoading: true
})
connectionService.testSSH(p).then((res) => {
message.success(i18n('connection.message.testConnectResult', i18n('common.text.successful')));
}).finally(() => {
setLoading({
...loadings,
sshTestLoading: false
})
});
}
@ -269,7 +274,8 @@ interface IRenderFormProps {
function RenderForm(props: IRenderFormProps) {
const { tab, form, backfillData, dataSourceFormConfigProps } = props;
useEffect(() => {
form.resetFields()
form.resetFields();
changeDataSourceFormConfig(backfillData);
}, [backfillData.id, backfillData.type])
let aliasChanged = false;
@ -291,8 +297,6 @@ function RenderForm(props: IRenderFormProps) {
return;
}
if (tab === 'baseInfo') {
// TODO:
// selectChange({ name: 'authentication', value: backfillData.user ? 1 : 2 });
regEXFormatting({ url: backfillData.url }, backfillData);
}
@ -304,6 +308,21 @@ function RenderForm(props: IRenderFormProps) {
}
}, [backfillData]);
function changeDataSourceFormConfig(backfillData: any) {
// TODO: select 联动下级只处理了ssh和baseInfo 这种方法也待改造
dataSourceFormConfig.ssh.items.forEach((t: IFormItem) => {
if (t.selects) {
t.defaultValue = backfillData?.ssh?.[t.name] || 'password'
}
});
dataSourceFormConfig.baseInfo.items.forEach((t: IFormItem) => {
if (t.selects) {
t.defaultValue = backfillData[t.name] || AuthenticationType.USERANDPASSWORD
}
});
setDataSourceFormConfig({ ...dataSourceFormConfig })
}
function initialFormData(dataSourceFormConfig: IFormItem[] | undefined) {
let initValue: any = {};
dataSourceFormConfig?.map((t) => {
@ -407,6 +426,7 @@ function RenderForm(props: IRenderFormProps) {
const name = t.name;
const width = t?.styles?.width || '100%';
const labelWidth = isEn ? t?.styles?.labelWidthEN || '100px' : t?.styles?.labelWidthCN || '70px';
const placeholder = isEn ? t.placeholderEN : t.placeholder;
const labelAlign = t?.styles?.labelAlign || 'left';
const FormItemTypes: { [key in InputType]: () => React.ReactNode } = {
@ -417,7 +437,7 @@ function RenderForm(props: IRenderFormProps) {
style={{ '--form-label-width': labelWidth } as any}
labelAlign={labelAlign}
>
<Input />
<Input placeholder={placeholder} />
</Form.Item>
),
@ -429,13 +449,14 @@ function RenderForm(props: IRenderFormProps) {
labelAlign={labelAlign}
>
<Select
placeholder={placeholder}
value={t.defaultValue}
onChange={(e) => {
selectChange({ name: name, value: e });
}}
>
{t.selects?.map((t: ISelect) => (
<Option key={t.value} value={t.value}>
<Option key={t.value?.toString()} value={t.value}>
{t.label}
</Option>
))}
@ -465,6 +486,7 @@ function RenderForm(props: IRenderFormProps) {
{FormItemTypes[t.inputType]()}
</div>
{t.selects?.map((item) => {
console.log(t.defaultValue,)
if (t.defaultValue === item.value) {
return item.items?.map((t) => renderFormItem(t));
}

View File

@ -50,7 +50,7 @@ export default memo<IProps>(function UploadDriver(props) {
return <div className={classnames(styles.box, className)}>
<div>
<Form
labelCol={{ span: 5 }}
labelCol={{ span: 3 }}
wrapperCol={{ span: 16 }}
>
<Form.Item label="Class">

View File

@ -17,7 +17,7 @@ export default {
'connection.message.testSshConnection': 'Test the ssh connection',
'connection.tableHeader.name': 'Name',
'connection.tableHeader.value': 'Value',
'connection.title.uploadDriver': 'Upload Driver',
'connection.title.uploadDriver': 'Upload',
'connection.tips.customUpload': "Upload driver",
'connection.title.driver': 'Driver',
'connection.button.clickUpload': 'Click to Upload',

View File

@ -17,7 +17,7 @@ export default {
'connection.message.testSshConnection': '测试ssh连接',
'connection.tableHeader.name': '名称',
'connection.tableHeader.value': '值',
'connection.title.uploadDriver': '上传驱动',
'connection.title.uploadDriver': '上传',
'connection.tips.customUpload': '上传驱动',
'connection.title.driver': '驱动',
'connection.button.clickUpload': '点击上传',

View File

@ -24,7 +24,7 @@ import registerMessage from './init/registerMessage';
import registerNotification from './init/registerNotification';
import { NotificationInstance } from 'antd/es/notification/interface';
import { ModalStaticFunctions } from 'antd/es/modal/confirm';
import Sub from './sub'
import Sub from './sub';
declare global {
interface Window {
_Lang: string;
@ -81,7 +81,6 @@ const restartCount = 200;
let staticNotification: NotificationInstance;
function AppContainer() {
const { token } = useToken();
const [initEnd, setInitEnd] = useState(false);
@ -92,7 +91,6 @@ function AppContainer() {
// staticNotification = staticFunction.notification
useEffect(() => {
let date = new Date('2030-12-30 12:30:00').toUTCString();
document.cookie = `CHAT2DB.LOCALE=${getLang()};Expires=${date}`;
@ -196,12 +194,12 @@ function AppContainer() {
</div>
)}
<div className={styles.hint}>
<Setting text={i18n('common.text.setting')} />
<Setting />
</div>
{serviceFail && (
<>
<div className={styles.github}>
{i18n('common.text.contactUs')}-github
{i18n('common.text.contactUs')}
<a target="_blank" href="https://github.com/chat2db/Chat2DB">
github
</a>
@ -220,4 +218,4 @@ function AppContainer() {
)}
</div>
);
}
}

View File

@ -0,0 +1,11 @@
:root{
:global{
.ant-modal-header{
border-bottom: 0px;
}
.ant-modal-footer{
border-top: 0px;
}
}
}

View File

@ -1,5 +1,6 @@
@import '../theme/custom/dark.less';
@import '../theme/custom/light.less';
@import './antd.less';
html,
body {

View File

@ -10,7 +10,7 @@ export function setLang(lang: LangType) {
}
export function getTheme(): ThemeType {
return (localStorage.getItem('theme') as ThemeType) || ThemeType.Dark;
return (localStorage.getItem('theme') as ThemeType) || ThemeType.Light;
}
export function setTheme(theme: ThemeType) {

View File

@ -6,4 +6,7 @@ common.paramError=The parameter is incorrect
common.paramDetailError=The parameter: {0} is incorrect
common.paramCheckError=The following parameters are not valid:
common.maxUploadSize=The file exceeds the maximum limit
dataSource.sqlAnalysisError=Invalid statements
dataSource.sqlAnalysisError=Invalid statements
connection.error=Connection failed, please check the connection information
connection.ssh.error=SSH connection failed, please check the connection information
connection.driver.load.error=Failed to load driver class, please check the driver jar package

View File

@ -6,4 +6,7 @@ common.paramError=The parameter is incorrect
common.paramDetailError=The parameter: {0} is incorrect
common.paramCheckError=The following parameters are not valid
common.maxUploadSize=The file exceeds the maximum limit
dataSource.sqlAnalysisError=Invalid statements
dataSource.sqlAnalysisError=Invalid statements
connection.error=Connection failed, please check the connection information
connection.ssh.error=SSH connection failed, please check the connection information
connection.driver.load.error=Failed to load driver class, please check the driver jar package

View File

@ -8,4 +8,7 @@ common.paramCheckError=请检查以下参数:
common.maxUploadSize=您输入的文件超过最大限制
dataSource.sqlAnalysisError=不合法的执行语句
dataSource.sqlAnalysisError=不合法的执行语句
connection.error=数据库链接异常,请检查数据库配置
connection.ssh.error=SSH 链接异常请检查SSH配置
connection.driver.load.error=数据库驱动加载异常,请检查驱动配置

View File

@ -0,0 +1,25 @@
package ai.chat2db.server.tools.common.exception;
import ai.chat2db.server.tools.base.excption.BusinessException;
import lombok.Getter;
@Getter
public class ConnectionException extends BusinessException {
public ConnectionException() {
this("connection.error");
}
public ConnectionException(String code) {
this(code, null);
}
public ConnectionException(String code, Object[] args) {
super(code,args);
}
public ConnectionException(String code, Object[] args, Throwable throwable) {
super(code,args, throwable);
}
}

View File

@ -1,5 +1,7 @@
package ai.chat2db.server.tools.common.util;
import java.util.Locale;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
@ -9,8 +11,6 @@ import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import java.util.Locale;
/**
* i18n utility
*
@ -19,7 +19,7 @@ import java.util.Locale;
@Slf4j
@Component
public class I18nUtils implements InitializingBean {
public static final String DEFAULT_message_Code="";
public static final String DEFAULT_message_Code="common.systemError";
@Resource
private MessageSource messageSource;
private static MessageSource messageSourceStatic;
@ -34,7 +34,7 @@ public class I18nUtils implements InitializingBean {
} catch (NoSuchMessageException e) {
log.error("no message.", e);
}
return messageSourceStatic.getMessage(messageCode, args, LocaleContextHolder.getLocale());
return messageSourceStatic.getMessage(DEFAULT_message_Code, args, LocaleContextHolder.getLocale());
}
/**

View File

@ -38,6 +38,7 @@ import com.jcraft.jsch.Session;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@ -179,6 +180,11 @@ public class DataSourceController {
public DataResult<DataSourceVO> queryById(@PathVariable("id") Long id) {
DataResult<DataSource> dataResult = dataSourceService.queryById(id);
DataSourceVO dataSourceVO = dataSourceWebConverter.dto2vo(dataResult.getData());
if(StringUtils.isNotBlank(dataSourceVO.getUser())){
dataSourceVO.setAuthenticationType("1");
}else {
dataSourceVO.setAuthenticationType("2");
}
return DataResult.of(dataSourceVO);
}

View File

@ -42,9 +42,13 @@ public class DataSourceCreateRequest {
@NotNull
private String password;
/**
* 认证类型
*/
private String authenticationType;
/**
* 连接类型
* @see DbTypeEnum
*/
@NotNull
private String type;
@ -100,4 +104,7 @@ public class DataSourceCreateRequest {
* 驱动配置
*/
private DriverConfig driverConfig;
}

View File

@ -47,7 +47,10 @@ public class DataSourceTestRequest {
@NotNull
private String type;
/**
* 认证类型
*/
private String authenticationType;
/**
* host

View File

@ -40,6 +40,10 @@ public class DataSourceVO {
*/
private String password;
/**
* 认证类型
*/
private String authenticationType;
/**
* 连接类型
*/

View File

@ -8,6 +8,8 @@ import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
import ai.chat2db.server.tools.base.excption.BusinessException;
import ai.chat2db.server.tools.common.exception.ConnectionException;
import ai.chat2db.spi.DBManage;
import ai.chat2db.spi.MetaData;
import ai.chat2db.spi.Plugin;
@ -101,6 +103,10 @@ public class Chat2DBContext {
if (session != null) {
url = url.replace(host, "127.0.0.1").replace(port, ssh.getLocalPort());
}
}catch (Exception e){
throw new ConnectionException("connection.ssh.error",null,e);
}
try {
DriverConfig config = connectInfo.getDriverConfig();
if (config == null) {
config = getDefaultDriverConfig(connectInfo.getDbType());
@ -111,27 +117,23 @@ public class Chat2DBContext {
connectInfo.getDriverConfig(), connectInfo.getExtendMap());
} catch (Exception e1) {
log.error("GetConnect error", e1);
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
log.error("session close error", e);
}
}
if (session != null) {
try {
session.delPortForwardingL(Integer.parseInt(ssh.getLocalPort()));
} catch (JSchException e) {
log.error("session delPortForwardingL error", e);
}
try {
session.disconnect();
} catch (Exception e) {
log.error("session disconnect error", e);
}
}
throw new RuntimeException("GetConnect error", e1);
throw new BusinessException("connection.error",null,e1);
}
connectInfo.setSession(session);
connectInfo.setConnection(connection);

View File

@ -1,12 +1,12 @@
package ai.chat2db.spi.sql;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.fastjson2.JSON;
import ai.chat2db.server.tools.common.exception.ConnectionException;
import ai.chat2db.spi.config.DriverConfig;
import ai.chat2db.spi.model.DriverEntry;
import ai.chat2db.spi.util.JdbcJarUtils;
@ -62,7 +63,13 @@ public class IDriverManager {
if (StringUtils.isNotEmpty(password)) {
info.put("password", password);
}
info.putAll(properties);
if (properties != null && !properties.isEmpty()) {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
if (entry.getKey() != null && entry.getValue() != null) {
info.put(entry.getKey(), entry.getValue());
}
}
}
return getConnection(url, info, driver);
}
@ -91,10 +98,8 @@ public class IDriverManager {
}
if (reason != null) {
DriverManager.println("getConnection failed: " + reason);
throw reason;
} else {
DriverManager.println("getConnection: no suitable driver found for " + url);
throw new SQLException("No suitable driver found for " + url, "08001");
}
}
@ -123,8 +128,7 @@ public class IDriverManager {
DRIVER_ENTRY_MAP.put(driver.getJdbcDriver(), driverEntry);
return driverEntry;
} catch (Exception e) {
log.error("getJDBCDriver error", e);
throw new SQLException("getJDBCDriver error", "08001");
throw new ConnectionException("connection.driver.load.error", null, e);
}
}
@ -169,26 +173,4 @@ public class IDriverManager {
}
}
//private static List<Class> loadClass(String jarPath, ClassLoader classLoader) throws IOException {
// Long s1 = System.currentTimeMillis();
// JarFile jarFile = new JarFile(getFullPath(jarPath));
// Enumeration<JarEntry> entries = jarFile.entries();
// List<Class> classes = new ArrayList();
// while (entries.hasMoreElements()) {
// JarEntry jarEntry = entries.nextElement();
// if (jarEntry.getName().endsWith(".class") && !jarEntry.getName().contains("$")) {
// String className = jarEntry.getName().substring(0, jarEntry.getName().length() - 6).replaceAll("/",
// ".");
// try {
// classes.add(classLoader.loadClass(className));
// // log.info("loadClass:{}", className);
// } catch (Throwable var7) {
// //log.error("getClasses error "+className, var7);
// }
// }
// }
// log.info("loadClass cost:{}", System.currentTimeMillis() - s1);
// return classes;
//}
}

View File

@ -11,6 +11,7 @@ import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import ai.chat2db.server.tools.base.constant.EasyToolsConstant;
import ai.chat2db.spi.model.*;
import cn.hutool.core.date.TimeInterval;
@ -94,6 +95,7 @@ public class SQLExecutor {
ExecuteResult executeResult = ExecuteResult.builder().sql(sql).success(Boolean.TRUE).build();
try (Statement stmt = connection.createStatement()) {
stmt.setFetchSize(EasyToolsConstant.MAX_PAGE_SIZE);
TimeInterval timeInterval = new TimeInterval();
boolean query = stmt.execute(sql.replaceFirst(";", ""));
executeResult.setDescription("执行成功");
@ -121,7 +123,9 @@ public class SQLExecutor {
List<List<String>> dataList = Lists.newArrayList();
executeResult.setDataList(dataList);
while (rs.next()) {
int n = 0;
while (rs.next() && n < EasyToolsConstant.MAX_PAGE_SIZE) {
n++;
List<String> row = Lists.newArrayListWithExpectedSize(col);
dataList.add(row);
for (int i = 1; i <= col; i++) {