mirror of
https://github.com/CodePhiliaX/Chat2DB.git
synced 2025-09-21 03:53:24 +08:00
dbeaver导入实现
This commit is contained in:
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ai.chat2db.server.web.api.controller.ncx.dbeaver;
|
||||
|
||||
/**
|
||||
* Value encryptor
|
||||
*/
|
||||
public interface DBSValueEncryptor {
|
||||
|
||||
byte[] encryptValue(byte[] value);
|
||||
|
||||
byte[] decryptValue(byte[] value);
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2023 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ai.chat2db.server.web.api.controller.ncx.dbeaver;
|
||||
|
||||
import org.apache.poi.util.IOUtils;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Default value encryptor.
|
||||
*
|
||||
* Uses Eclipse secure preferences to read/write secrets.
|
||||
*/
|
||||
public class DefaultValueEncryptor implements DBSValueEncryptor {
|
||||
|
||||
public static final String CIPHER_NAME = "AES/CBC/PKCS5Padding";
|
||||
public static final String KEY_ALGORITHM = "AES";
|
||||
|
||||
private static final byte[] LOCAL_KEY_CACHE = new byte[] { -70, -69, 74, -97, 119, 74, -72, 83, -55, 108, 45, 101, 61, -2, 84, 74 };
|
||||
|
||||
private final SecretKey secretKey;
|
||||
private final Cipher cipher;
|
||||
|
||||
public DefaultValueEncryptor(SecretKey secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
try {
|
||||
this.cipher = Cipher.getInstance(CIPHER_NAME);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Internal error during encrypted init", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 DBeaver 源码查看到默认的 SecretKey
|
||||
**/
|
||||
public static SecretKey getLocalSecretKey() {
|
||||
return new SecretKeySpec(LOCAL_KEY_CACHE, DefaultValueEncryptor.KEY_ALGORITHM);
|
||||
}
|
||||
|
||||
public static SecretKey makeSecretKeyFromPassword(String password) {
|
||||
byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] passBytes = Arrays.copyOf(bytes, 16);
|
||||
return new SecretKeySpec(passBytes, KEY_ALGORITHM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encryptValue(byte[] value) {
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
byte[] iv = cipher.getIV();
|
||||
|
||||
ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
|
||||
try (CipherOutputStream cipherOut = new CipherOutputStream(resultBuffer, cipher)) {
|
||||
resultBuffer.write(iv);
|
||||
cipherOut.write(value);
|
||||
}
|
||||
return resultBuffer.toByteArray();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error encrypting value", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decryptValue(byte[] value) {
|
||||
try (InputStream byteStream = new ByteArrayInputStream(value)) {
|
||||
byte[] fileIv = new byte[16];
|
||||
byteStream.read(fileIv);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(fileIv));
|
||||
|
||||
try (CipherInputStream cipherIn = new CipherInputStream(byteStream, cipher)) {
|
||||
ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
|
||||
IOUtils.copy(cipherIn, resultBuffer);
|
||||
return resultBuffer.toByteArray();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error decrypting value", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -37,31 +37,31 @@ public enum DataBaseType {
|
||||
/**
|
||||
* Mariadb
|
||||
**/
|
||||
Mariadb("jdbc:mariadb://%s:%s"),
|
||||
MARIADB("jdbc:mariadb://%s:%s"),
|
||||
/**
|
||||
* DM
|
||||
**/
|
||||
DM("jdbc:dm://%s:%s"),
|
||||
/**
|
||||
* KINGBASE8
|
||||
* KINGBASE
|
||||
**/
|
||||
KINGBASE8("jdbc:kingbase8://%s:%s"),
|
||||
KINGBASE("jdbc:kingbase8://%s:%s"),
|
||||
/**
|
||||
* Presto
|
||||
**/
|
||||
Presto("jdbc:presto://%s:%s"),
|
||||
PRESTO("jdbc:presto://%s:%s"),
|
||||
/**
|
||||
* OceanBase
|
||||
**/
|
||||
OceanBase("jdbc:oceanbase://%s:%s"),
|
||||
OCEANBASE("jdbc:oceanbase://%s:%s"),
|
||||
/**
|
||||
* Hive
|
||||
**/
|
||||
Hive("jdbc:hive2://%s:%s"),
|
||||
HIVE("jdbc:hive2://%s:%s"),
|
||||
/**
|
||||
* ClickHouse
|
||||
**/
|
||||
ClickHouse("jdbc:clickhouse://%s:%s");
|
||||
CLICKHOUSE("jdbc:clickhouse://%s:%s");
|
||||
|
||||
private String urlString;
|
||||
|
||||
@ -76,7 +76,8 @@ public enum DataBaseType {
|
||||
public static DataBaseType matchType(String value) {
|
||||
if (StringUtils.isNotEmpty(value)) {
|
||||
for (DataBaseType dataBase : DataBaseType.values()) {
|
||||
if (dataBase.name().equals(value.toUpperCase())) {
|
||||
//kingbase -> kingbase8
|
||||
if (value.toUpperCase().contains(dataBase.name())) {
|
||||
return dataBase;
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,6 @@
|
||||
|
||||
package ai.chat2db.server.web.api.controller.ncx.enums;
|
||||
|
||||
import ai.chat2db.server.tools.common.util.ConfigUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Import/Export constants
|
||||
*/
|
||||
@ -29,8 +25,12 @@ public class ExportConstants {
|
||||
public static final String ARCHIVE_FILE_EXT = ".dbp"; //NON-NLS-1
|
||||
public static final String CONFIG_FILE = ".dbeaver"; //NON-NLS-1
|
||||
public static final String CONFIG_DATASOURCE_FILE = "data-sources.json"; //NON-NLS-1
|
||||
public static final String CONFIG_CREDENTIALS_FILE = "credentials-config.json"; //NON-NLS-1
|
||||
public static final String DIR_PROJECTS = "projects"; //NON-NLS-1
|
||||
public static final String DIR_DRIVERS = "drivers"; //NON-NLS-1
|
||||
public static final String DIR_CONNECTIONS = "connections"; //NON-NLS-1
|
||||
public static final String DIR_CONFIGURATION = "configuration"; //NON-NLS-1
|
||||
public static final String GENERIC = "generic"; //NON-NLS-1
|
||||
|
||||
public static final String META_FILENAME = "meta.xml"; //NON-NLS-1
|
||||
|
||||
|
@ -5,6 +5,7 @@ import ai.chat2db.server.domain.repository.entity.DataSourceDO;
|
||||
import ai.chat2db.server.domain.repository.mapper.DataSourceMapper;
|
||||
import ai.chat2db.server.tools.common.util.ConfigUtils;
|
||||
import ai.chat2db.server.web.api.controller.ncx.cipher.CommonCipher;
|
||||
import ai.chat2db.server.web.api.controller.ncx.dbeaver.DefaultValueEncryptor;
|
||||
import ai.chat2db.server.web.api.controller.ncx.enums.DataBaseType;
|
||||
import ai.chat2db.server.web.api.controller.ncx.enums.ExportConstants;
|
||||
import ai.chat2db.server.web.api.controller.ncx.enums.VersionEnum;
|
||||
@ -16,14 +17,16 @@ import ai.chat2db.spi.model.SSHInfo;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import com.alibaba.excel.util.FileUtils;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.google.common.io.Files;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.w3c.dom.*;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
@ -31,15 +34,15 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -68,6 +71,10 @@ public class ConverterServiceImpl implements ConverterService {
|
||||
* xml连接信息开始标志位
|
||||
**/
|
||||
private static final String BEGIN = "#BEGIN#";
|
||||
/**
|
||||
* 密码json的key
|
||||
**/
|
||||
private static final String connection = "#connection";
|
||||
|
||||
@Autowired
|
||||
private DataSourceMapper dataSourceMapper;
|
||||
@ -82,7 +89,6 @@ public class ConverterServiceImpl implements ConverterService {
|
||||
|
||||
@Override
|
||||
public UploadVO uploadFile(File file) {
|
||||
|
||||
UploadVO vo = new UploadVO();
|
||||
try {
|
||||
// List<Map <连接名,Map<属性名,值>>> 要导入的连接
|
||||
@ -169,30 +175,64 @@ public class ConverterServiceImpl implements ConverterService {
|
||||
//配置的json文件
|
||||
File json = new File(config + File.separator + ExportConstants.CONFIG_DATASOURCE_FILE);
|
||||
JSONObject jsonObject = JSON.parseObject(new FileInputStream(json));
|
||||
JSONObject connections = jsonObject.getJSONObject("connections");
|
||||
JSONObject connections = jsonObject.getJSONObject(ExportConstants.DIR_CONNECTIONS);
|
||||
Set<String> keys = connections.keySet();
|
||||
for (String key : keys) {
|
||||
JSONObject configurations = connections.getJSONObject(key);
|
||||
JSONObject configuration = configurations.getJSONObject("configuration");
|
||||
DataSourceDO dataSourceDO = new DataSourceDO();
|
||||
LocalDateTime dateTime = LocalDateTime.now();
|
||||
dataSourceDO.setGmtCreate(dateTime);
|
||||
dataSourceDO.setGmtModified(dateTime);
|
||||
dataSourceDO.setAlias(configurations.getString("name"));
|
||||
dataSourceDO.setHost(configuration.getString("host"));
|
||||
dataSourceDO.setPort(configuration.getString("port"));
|
||||
dataSourceDO.setUrl(configuration.getString("url"));
|
||||
//dataSourceDO.setUserName(configuration.getString("host"));
|
||||
//dataSourceDO.setDriver(configuration.getString("host"));
|
||||
dataSourceDO.setType(configurations.getString("provider").toUpperCase());
|
||||
dataSourceMapper.insert(dataSourceDO);
|
||||
JSONObject configuration = configurations.getJSONObject(ExportConstants.DIR_CONFIGURATION);
|
||||
//匹配数据库类型
|
||||
String provider = configurations.getString("provider");
|
||||
if (provider.equals(ExportConstants.GENERIC)) {
|
||||
//自定义驱动
|
||||
JSONObject drivers = jsonObject.getJSONObject(ExportConstants.DIR_DRIVERS);
|
||||
//获得驱动id
|
||||
String driverId = configurations.getString("driver");
|
||||
//获得所有generic
|
||||
JSONObject generics = drivers.getJSONObject(provider);
|
||||
//获得自己的驱动
|
||||
JSONObject generic = generics.getJSONObject(driverId);
|
||||
//如果不存在,则不导入
|
||||
if (null == generic) {
|
||||
continue;
|
||||
}
|
||||
//赋值驱动名称,用来确定数据库的类型
|
||||
provider = generic.getString("name");
|
||||
}
|
||||
DataBaseType dataBaseType = DataBaseType.matchType(provider.toUpperCase());
|
||||
DataSourceDO dataSourceDO;
|
||||
//未匹配到数据库类型,如:dbeaver支持自定义驱动等,但chat2DB暂不支持
|
||||
if (null != dataBaseType) {
|
||||
//密码信息
|
||||
File credentials = new File(config + File.separator + ExportConstants.CONFIG_CREDENTIALS_FILE);
|
||||
DefaultValueEncryptor defaultValueEncryptor = new DefaultValueEncryptor(DefaultValueEncryptor.getLocalSecretKey());
|
||||
JSONObject credentialsJson = JSON.parseObject(defaultValueEncryptor.decryptValue(Files.readAllBytes(credentials.toPath())));
|
||||
dataSourceDO = new DataSourceDO();
|
||||
LocalDateTime dateTime = LocalDateTime.now();
|
||||
dataSourceDO.setGmtCreate(dateTime);
|
||||
dataSourceDO.setGmtModified(dateTime);
|
||||
dataSourceDO.setAlias(configurations.getString("name"));
|
||||
dataSourceDO.setHost(configuration.getString("host"));
|
||||
dataSourceDO.setPort(configuration.getString("port"));
|
||||
dataSourceDO.setUrl(configuration.getString("url"));
|
||||
if (null != credentialsJson) {
|
||||
JSONObject userInfo = credentialsJson.getJSONObject(key);
|
||||
JSONObject userPassword = userInfo.getJSONObject(connection);
|
||||
dataSourceDO.setUserName(userPassword.getString("user"));
|
||||
DesUtil desUtil = new DesUtil(DesUtil.DES_KEY);
|
||||
String password = userPassword.getString("password");
|
||||
String encryptStr = desUtil.encrypt(Optional.ofNullable(password).orElse(""), "CBC");
|
||||
dataSourceDO.setPassword(encryptStr);
|
||||
}
|
||||
dataSourceDO.setType(dataBaseType.name());
|
||||
dataSourceMapper.insert(dataSourceDO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//删除临时文件
|
||||
FileUtils.delete(file);
|
||||
//删除dbeaver存留的配置临时文件
|
||||
//删除导入dbeaver时,dbp产生的临时配置文件
|
||||
//projects.forEach(v -> FileUtils.delete(new File(ConfigUtils.CONFIG_BASE_PATH + File.separator + v)));
|
||||
return vo;
|
||||
}
|
||||
@ -212,13 +252,10 @@ public class ConverterServiceImpl implements ConverterService {
|
||||
if (!folder.exists()) {
|
||||
FileUtil.mkdir(folder);
|
||||
}
|
||||
resource = folder;
|
||||
importDbeaverConfig(folder, childElement, entryPath + "/", zipFile);
|
||||
} else {
|
||||
File file = new File(resource.getPath() + File.separator + childName);
|
||||
if (!file.exists()) {
|
||||
FileUtil.writeFromStream(zipFile.getInputStream(resourceEntry), file);
|
||||
}
|
||||
FileUtil.writeFromStream(zipFile.getInputStream(resourceEntry), file, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user