fix:ssh loaing

This commit is contained in:
shanhexi
2023-07-09 17:37:41 +08:00
18 changed files with 148 additions and 69 deletions

View File

@ -24,7 +24,7 @@ enum DownloadStatus {
export default memo<IProps>(function Driver(props) {
const { className, backfillData, onChange } = props;
const [downloadStatus, setDownloadStatus] = useState<DownloadStatus>();
const [downloadStatus, setDownloadStatus] = useState<DownloadStatus>(DownloadStatus.Default);
const [driverForm] = Form.useForm();
const [driverObj, setDriverObj] = useState<IDriverResponse>();
const [uploadDriverModal, setUploadDriverModal] = useState(false);
@ -47,7 +47,10 @@ 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,
@ -110,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>
}

View File

@ -35,14 +35,12 @@ export const sshConfig: IConnectionConfig['ssh'] = {
}
},
{
defaultValue: '',
defaultValue: '22',
inputType: InputType.INPUT,
labelNameCN: 'SSH 端口',
labelNameEN: 'Port',
name: 'port',
required: false,
placeholder: '不必填',
placeholderEN: 'Need not fill in',
styles: {
width: '30%',
labelWidthEN: '40px',
@ -67,6 +65,8 @@ export const sshConfig: IConnectionConfig['ssh'] = {
labelNameCN: '本地端口',
labelNameEN: 'LocalPort',
name: 'localPort',
placeholder: '不必填',
placeholderEN: 'Need not fill in',
required: false,
styles: {
width: '30%',
@ -200,6 +200,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -312,6 +314,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -458,6 +462,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -558,6 +564,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -694,6 +702,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -841,6 +851,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -949,7 +961,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1048,7 +1061,6 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
labelNameEN: 'Password',
name: 'password',
required: true,
},
],
label: 'User&Password',
@ -1057,6 +1069,7 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1175,6 +1188,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1294,6 +1309,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1413,6 +1430,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1532,6 +1551,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1651,6 +1672,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1770,6 +1793,8 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
{
label: 'NONE',
value: AuthenticationType.NONE,
items: [],
},
],
styles: {
@ -1889,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

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

View File

@ -15,6 +15,7 @@ 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;
@ -34,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();
@ -50,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)
@ -116,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>
@ -208,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
})
});
}
@ -275,7 +281,7 @@ function RenderForm(props: IRenderFormProps) {
let aliasChanged = false;
const [dataSourceFormConfig, setDataSourceFormConfig] = useState<IConnectionConfig>(dataSourceFormConfigProps);
console.log(dataSourceFormConfig.ssh.items[5].defaultValue)
useEffect(() => {
setDataSourceFormConfig(dataSourceFormConfigProps)
}, [dataSourceFormConfigProps])
@ -303,6 +309,7 @@ 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'
@ -449,7 +456,7 @@ function RenderForm(props: IRenderFormProps) {
}}
>
{t.selects?.map((t: ISelect) => (
<Option key={t.value} value={t.value}>
<Option key={t.value?.toString()} value={t.value}>
{t.label}
</Option>
))}
@ -479,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

@ -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

@ -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++) {