From 4a3f89e1f59af6c3b8290fce55efc9b4970fbb58 Mon Sep 17 00:00:00 2001 From: SwallowGG <1558143046@qq.com> Date: Fri, 20 Oct 2023 16:17:09 +0800 Subject: [PATCH] Support auto update --- .../start/config/config/AutomaticUpgrade.java | 156 ------------------ .../DbhubTomcatConnectorCustomizer.java | 3 +- .../FailedEventApplicationListener.java | 3 +- .../server/start/config/util/SystemUtils.java | 36 ---- .../controller/system/AutomaticUpgrade.java | 21 +++ .../controller/system/SystemController.java | 50 +++++- .../controller/system/util/SystemUtils.java | 91 ++++++++++ .../controller/system/vo/AppVersionVO.java | 68 ++++++++ 8 files changed, 227 insertions(+), 201 deletions(-) delete mode 100644 chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/AutomaticUpgrade.java delete mode 100644 chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/util/SystemUtils.java create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/AutomaticUpgrade.java create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/util/SystemUtils.java create mode 100644 chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/vo/AppVersionVO.java diff --git a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/AutomaticUpgrade.java b/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/AutomaticUpgrade.java deleted file mode 100644 index af9f3c96..00000000 --- a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/config/AutomaticUpgrade.java +++ /dev/null @@ -1,156 +0,0 @@ -package ai.chat2db.server.start.config.config; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Serializable; -import java.time.Duration; -import java.util.List; -import java.util.Map; - -import ai.chat2db.server.tools.common.util.ConfigUtils; -import cn.hutool.core.io.FileUtil; -import com.dtflys.forest.Forest; -import com.dtflys.forest.utils.TypeReference; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.apache.commons.lang3.StringUtils; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -public class AutomaticUpgrade { - - private static final OkHttpClient client = new OkHttpClient(); - - private static final String VERSION_URL = "https://sqlgpt.cn/api/version.json"; - - @Scheduled(fixedRate = 3600000) // 每小时运行一次 - public void checkVersionUpdates() { - String appPath = ConfigUtils.APP_PATH; - - log.info("appPath: {}", appPath); - if (StringUtils.isBlank(appPath) || !appPath.contains("app")) { - return; - } - try { - Upgrade dataResult = getUpgrade(); - - String oldVersion = ConfigUtils.getLocalVersion(); - - if (!needUpdate(dataResult, oldVersion)) { - return; - } - - File versionFile = new File( - appPath + File.separator + "versions" + File.separator + dataResult.getVersion()); - if (!versionFile.exists()) { - versionFile.mkdir(); - } - File oldLib = new File( - appPath + File.separator + "versions" + File.separator + oldVersion + File.separator + "static" - + File.separator + "lib"); - - File newLib = new File( - appPath + File.separator + "versions" + File.separator + dataResult.getVersion() + File.separator - + "static"); - - if (oldLib.exists()) { - FileUtil.copy(oldLib, newLib, true); - } - - for (Map downloadFile : dataResult.getDownloadFiles()) { - downloadUpgrade(downloadFile, versionFile); - } - - ConfigUtils.updateVersion(dataResult.getVersion()); - } catch (Exception e) { - log.error("checkVersionUpdates error", e); - } - - } - - private void downloadUpgrade(Map downloadFile, File versionFile) throws IOException { - String url = downloadFile.get("url"); - String fileName = downloadFile.get("fileName"); - String[] paths = fileName.split("/"); - String filePath = versionFile.getPath(); - for (int i = 0; i < paths.length; i++) { - filePath = filePath + File.separator + paths[i]; - if (i < paths.length - 1) { - File file = new File(filePath); - if (!file.exists()) { - file.mkdir(); - } - } - } - download(url, filePath); - } - - /** - * Find upgrade files - * - * @return - */ - private Upgrade getUpgrade() { - return Forest.get(VERSION_URL) - .connectTimeout(Duration.ofMillis(5000)) - .readTimeout(Duration.ofMillis(10000)) - .execute(new TypeReference<>() {}); - } - - private boolean needUpdate(Upgrade upgrade, String localVersion) { - if (upgrade == null || StringUtils.isBlank(upgrade.getVersion()) || StringUtils.isBlank(localVersion) - || upgrade.getVersion().equals(localVersion)) { - return false; - } - String[] versionArray = upgrade.getVersion().split("\\."); - String[] localVersionArray = localVersion.split("\\."); - for (int i = 0; i < versionArray.length; i++) { - if (Long.parseLong(versionArray[i]) < Long.parseLong(localVersionArray[i])) { - return false; - } - if (Long.parseLong(versionArray[i]) > Long.parseLong(localVersionArray[i])) { - return true; - } - } - return false; - } - - private void download(String url, String outputPath) throws IOException { - File file = new File(outputPath); - if (file.exists()) { - file.delete(); - } - Request request = new Request.Builder().url(url).build(); - try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) { - throw new IOException("Unexpected code " + response); - } - try (InputStream is = response.body().byteStream(); FileOutputStream fos = new FileOutputStream( - outputPath)) { - - byte[] buffer = new byte[2048]; - int length; - while ((length = is.read(buffer)) != -1) { - fos.write(buffer, 0, length); - } - fos.flush(); - } - // System.out.println("File downloaded: " + outputPath); - } - } - - @Data - public static class Upgrade implements Serializable { - - private String version; - - private List> downloadFiles; - } -} diff --git a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/DbhubTomcatConnectorCustomizer.java b/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/DbhubTomcatConnectorCustomizer.java index 54d17494..b9be9aa8 100644 --- a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/DbhubTomcatConnectorCustomizer.java +++ b/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/DbhubTomcatConnectorCustomizer.java @@ -1,7 +1,6 @@ package ai.chat2db.server.start.config.listener; -import ai.chat2db.server.start.config.util.SystemUtils; - +import ai.chat2db.server.web.api.controller.system.util.SystemUtils; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.LifecycleState; import org.apache.catalina.connector.Connector; diff --git a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/FailedEventApplicationListener.java b/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/FailedEventApplicationListener.java index 3ee3b2fc..4185ae7f 100644 --- a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/FailedEventApplicationListener.java +++ b/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/listener/FailedEventApplicationListener.java @@ -1,7 +1,6 @@ package ai.chat2db.server.start.config.listener; -import ai.chat2db.server.start.config.util.SystemUtils; - +import ai.chat2db.server.web.api.controller.system.util.SystemUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.context.ApplicationListener; diff --git a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/util/SystemUtils.java b/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/util/SystemUtils.java deleted file mode 100644 index abb9fdaa..00000000 --- a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/config/util/SystemUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -package ai.chat2db.server.start.config.util; - - -import ai.chat2db.spi.ssh.SSHManager; -import lombok.extern.slf4j.Slf4j; - -/** - * 系统工具包 - * - * @author Jiaju Zhuang - */ -@Slf4j -public class SystemUtils { - - /** - * 停止当前应用 - */ - public static void stop() { - new Thread(() -> { - log.info("1秒以后退出应用"); - // 1秒以后自动退出应用 - try { - Thread.sleep(1000L); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - // 直接系统退出 - log.info("开始退出系统应用"); - SSHManager.close(); - try { - System.exit(0); - } catch (Exception ignore) { - } - }).start(); - } -} diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/AutomaticUpgrade.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/AutomaticUpgrade.java new file mode 100644 index 00000000..40833045 --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/AutomaticUpgrade.java @@ -0,0 +1,21 @@ +package ai.chat2db.server.web.api.controller.system; + +import ai.chat2db.server.tools.common.util.ConfigUtils; +import ai.chat2db.server.web.api.controller.system.util.SystemUtils; +import ai.chat2db.server.web.api.controller.system.vo.AppVersionVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class AutomaticUpgrade { + + @Scheduled(fixedRate = 3600000) // 每小时运行一次 + public void checkVersionUpdates() { + AppVersionVO appVersion = SystemUtils.getLatestVersion(ConfigUtils.getLocalVersion(), "auto", ""); + if (appVersion != null) { + SystemUtils.upgrade(appVersion); + } + } +} diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/SystemController.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/SystemController.java index 58c506e3..d7d6f88f 100644 --- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/SystemController.java +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/SystemController.java @@ -4,20 +4,28 @@ */ package ai.chat2db.server.web.api.controller.system; +import ai.chat2db.server.domain.api.model.Config; +import ai.chat2db.server.domain.api.service.ConfigService; import ai.chat2db.server.domain.core.cache.CacheManage; import ai.chat2db.server.tools.base.wrapper.result.DataResult; import ai.chat2db.server.tools.common.config.Chat2dbProperties; +import ai.chat2db.server.tools.common.enums.ModeEnum; import ai.chat2db.server.tools.common.model.ConfigJson; import ai.chat2db.server.tools.common.util.ConfigUtils; +import ai.chat2db.server.tools.common.util.EasyEnumUtils; +import ai.chat2db.server.web.api.controller.ai.chat2db.client.Chat2dbAIClient; +import ai.chat2db.server.web.api.controller.system.util.SystemUtils; +import ai.chat2db.server.web.api.controller.system.vo.AppVersionVO; import ai.chat2db.server.web.api.controller.system.vo.SystemVO; +import ai.chat2db.server.web.api.util.ApplicationContextUtil; import ai.chat2db.spi.ssh.SSHManager; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.context.ApplicationContext; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + /** * @author jipengfei @@ -43,10 +51,42 @@ public class SystemController { public DataResult get() { ConfigJson configJson = ConfigUtils.getConfig(); return DataResult.of(SystemVO.builder() - .systemUuid(configJson.getSystemUuid()) - .build()); + .systemUuid(configJson.getSystemUuid()) + .build()); } + + @GetMapping("/get_latest_version") + public DataResult getLatestVersion(String currentVersion) { + ConfigService configService = ApplicationContextUtil.getBean(ConfigService.class); + Config keyConfig = configService.find(Chat2dbAIClient.CHAT2DB_OPENAI_KEY).getData(); + AppVersionVO appVersionVO = SystemUtils.getLatestVersion(currentVersion, "manual", keyConfig.getContent()); + ModeEnum mode = EasyEnumUtils.getEnum(ModeEnum.class, System.getProperty("chat2db.mode")); + if (mode == ModeEnum.DESKTOP) { + // In this mode, no user login is required, so only local access is available + appVersionVO.setDesktop(true); + } + return DataResult.of(appVersionVO); + } + + @PostMapping("/update_desktop_version") + public DataResult updateDesktopVersion(@RequestBody AppVersionVO version) { + new Thread(() -> { + SystemUtils.upgrade(version); + }).start(); + return DataResult.of(version.getVersion()); + } + + @GetMapping("/is_update_success") + public DataResult isUpdateSuccess(String version) { + String localVersion = ConfigUtils.getLocalVersion(); + if (StringUtils.isEmpty(localVersion)) { + return DataResult.of(false); + } + return DataResult.of(localVersion.equals(version)); + } + + /** * 获取当前版本号 * diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/util/SystemUtils.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/util/SystemUtils.java new file mode 100644 index 00000000..65f1870a --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/util/SystemUtils.java @@ -0,0 +1,91 @@ +package ai.chat2db.server.web.api.controller.system.util; + + +import ai.chat2db.server.tools.base.wrapper.result.DataResult; +import ai.chat2db.server.tools.common.util.ConfigUtils; +import ai.chat2db.server.web.api.controller.system.vo.AppVersionVO; +import ai.chat2db.spi.ssh.SSHManager; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ZipUtil; +import cn.hutool.http.HttpUtil; +import com.dtflys.forest.Forest; +import com.dtflys.forest.utils.TypeReference; +import lombok.extern.slf4j.Slf4j; +import okhttp3.OkHttpClient; +import org.apache.commons.lang3.StringUtils; + +import java.io.File; +import java.time.Duration; + +/** + * 系统工具包 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class SystemUtils { + + /** + * 停止当前应用 + */ + public static void stop() { + new Thread(() -> { + log.info("1秒以后退出应用"); + // 1秒以后自动退出应用 + try { + Thread.sleep(1000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + // 直接系统退出 + log.info("开始退出系统应用"); + SSHManager.close(); + try { + System.exit(0); + } catch (Exception ignore) { + } + }).start(); + } + + private static final OkHttpClient client = new OkHttpClient(); + + private static final String VERSION_URL = "https://sqlgpt.cn/api/version.json"; + + private static final String ZIP_FILE_PATH = ConfigUtils.APP_PATH + File.separator + "versions" + File.separator; + + public static void upgrade(AppVersionVO appVersion) { + + String appPath = ConfigUtils.APP_PATH; + + log.info("appPath: {}", appPath); + if (StringUtils.isBlank(appPath) || !appPath.contains("app")) { + return; + } + try { + String zipPath = ZIP_FILE_PATH + appVersion.getVersion() + ".zip"; + + HttpUtil.downloadFile(appVersion.getHotUpgradeUrl(), ZIP_FILE_PATH + appVersion.getVersion() + ".zip"); + + ZipUtil.unzip(zipPath); + + FileUtil.del(zipPath); + + ConfigUtils.updateVersion(appVersion.getVersion()); + } catch (Exception e) { + log.error("checkVersionUpdates error", e); + } + } + + private static final String LATEST_VERSION_URL = "https://test.sqlgpt.cn/api/client/version/check/v3?version=%s&type=wechat&userId=%s"; + + public static AppVersionVO getLatestVersion(String version, String type, String userId) { + String url = String.format(LATEST_VERSION_URL, version, type, userId); + DataResult result = Forest.get(url) + .connectTimeout(Duration.ofMillis(5000)) + .readTimeout(Duration.ofMillis(10000)) + .execute(new TypeReference<>() { + }); + return result.getData(); + } + +} diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/vo/AppVersionVO.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/vo/AppVersionVO.java new file mode 100644 index 00000000..789754e8 --- /dev/null +++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/system/vo/AppVersionVO.java @@ -0,0 +1,68 @@ +package ai.chat2db.server.web.api.controller.system.vo; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class AppVersionVO { + /** + * 主键 + */ + private Long id; + + /** + * 创建时间 + */ + private LocalDateTime gmtCreate; + + /** + * 修改时间 + */ + private LocalDateTime gmtModified; + + /** + * 新版本 + */ + private String version; + + /** + * 哪些版本可以升级到该版本 + */ + private String versionUse; + + /** + * 状态 + */ + private String status; + + /** + * 下载地址 + */ + private String downloadLink; + + /** + * 手工更新,自动强制更新 + */ + private String type; + + /** + * 白名单,用于测试 + */ + private String whiteList; + + /** + * 热更新包地址 + */ + private String hotUpgradeUrl; + + /** + * 更新日志 + */ + private String updateLog; + + /** + * 桌面 + */ + private boolean desktop; +}