Merge branch 'dev'

This commit is contained in:
bwcx_jzy 2022-06-12 09:32:13 +08:00
commit 63f9dc7528
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
37 changed files with 726 additions and 593 deletions

2
.env
View File

@ -1,3 +1,3 @@
JPOM_VERSION=2.8.25
JPOM_VERSION=2.9.0
# Server Token 生产部署请更换
SERVER_TOKEN=7094f673-2c53-4fc1-82e7-86e528449d97

View File

@ -29,14 +29,14 @@ stages:
artifacts:
- name: all_zip
path:
- modules/server/target/server-2.8.25-release.zip
- modules/agent/target/agent-2.8.25-release.zip
- modules/server/target/server-2.9.0-release.zip
- modules/agent/target/agent-2.9.0-release.zip
- name: server_zip
path:
- modules/server/target/server-2.8.25-release.zip
- modules/server/target/server-2.9.0-release.zip
- name: agent_zip
path:
- modules/agent/target/agent-2.8.25-release.zip
- modules/agent/target/agent-2.9.0-release.zip
settings: []
strategy:
retry: '0'
@ -50,7 +50,7 @@ stages:
name: publish_general_artifacts
displayName: 合并打包
dependArtifact: all_zip
artifactName: jpom-2.8.25
artifactName: jpom-2.9.0
strategy:
retry: '0'
strategy:

View File

@ -1,5 +1,29 @@
# 🚀 版本日志
# 2.9.0
### 🐣 新增功能
### 🐞 解决BUG、优化功能
1. 【server】升级 h2 版本,低版本存在漏洞(CVE-2021-23463)
2. 升级 SpringBoot、Hutool 版本
### ⚠️ 注意
> 此版本为不兼容升级,需要手动升级操作数据相关迁移,操作流程如下:
1. 导出低版本数据
1. 启动程序参数里面添加 --backup-h2
2. linux 环境举例:`sh /xxxx/Server.sh restart --backup-h2`
2. 将导出的低版本数据( sql 文件) 导入到新版本中
1. 启动程序参数里面添加 `--replace-import-h2-sql=/xxxx.sql --transform-sql` (路径需要替换为第一步控制台输出的 sql 文件保存路径)
2. linux 环境举例:`sh /xxxx/Server.sh restart --replace-import-h2-sql=/xxxx.sql --transform-sql`
✈️ [更详细的升级说明文档](https://jpom.io/pages/upgrade/2.8.x-to-2.9.x)
------
# 2.8.25 (2022-06-10)
### 🐣 新增功能
@ -19,14 +43,14 @@
### 🐞 解决BUG、优化功能
1. 【server】修复切换分页限制数再排序数据查询条数不正确问题 (感谢[@Eibons](https://gitee.com/eibons) [Gitee issues I5B47O](https://gitee.com/dromara/Jpom/issues/I5B47O)
1. 【server】修复切换分页限制数再排序数据查询条数不正确问题
(感谢[@Eibons](https://gitee.com/eibons) [Gitee issues I5B47O](https://gitee.com/dromara/Jpom/issues/I5B47O)
2. 【server】优化编辑构建时发布选择 docker 镜像服务名输入改为下拉框(感谢@W
3. 【agent】重启项目操作返回停止项目相关信息
4. 【server】缓存用户选择的每页条数
------
# 2.8.23 (2022-06-01)
### 🐣 新增功能
@ -53,11 +77,14 @@
### 🐣 新增功能
1. 新增日志查询功能 (感谢 @ 、 [@漫步青春的日子](https://gitee.com/imoom) [Gitee issues I54GDY](https://gitee.com/dromara/Jpom/issues/I54GDY)
1. 新增日志查询功能 (感谢 @
、 [@漫步青春的日子](https://gitee.com/imoom) [Gitee issues I54GDY](https://gitee.com/dromara/Jpom/issues/I54GDY)
### 🐞 解决BUG、优化功能
1. 【server】修复构建读取 .env 文件空时候无法正常使用(感谢[@wangfeng2228952430](https://gitee.com/wangfeng2228952430) [Gitee issues I57DC1](https://gitee.com/dromara/Jpom/issues/I57DC1)
1. 【server】修复构建读取 .env
文件空时候无法正常使用(感谢[@wangfeng2228952430](https://gitee.com/wangfeng2228952430) [Gitee issues I57DC1](https://gitee.com/dromara/Jpom/issues/I57DC1)
2. 【server】优化构建拉取仓库`锁对象`,避免出现死锁问题(感谢@信徒
3. 【server】修复节点分发配置多个节点无法正常保存问题感谢@李道甫
4. 【agent】上传文件大小限制配置属性默认值恢复为 1GB,避免用户升级后需要逐一配置 (感谢@李道甫
@ -82,16 +109,27 @@
1. 【server】节点新增代理配置实现使用代理访问插件端感谢@背着砍刀的诗人)
2. 【server】构建新增差异构建配置选择如果仓库代码未变动则不执行构建
3. 项目管理文件新增备份,自动备份变动的文件(感谢[@少爷123](https://gitee.com/58753101) [Gitee issues I54ZFM](https://gitee.com/dromara/Jpom/issues/I54ZFM)
4. 【server】SH配置和节点配置新增跨工作空间同步功能方便快速同步信息感谢[@陈旭](https://gitee.com/chenxu8989) [Gitee issues I56YTU](https://gitee.com/dromara/Jpom/issues/I56YTU)
3.
项目管理文件新增备份,自动备份变动的文件(感谢[@少爷123](https://gitee.com/58753101) [Gitee issues I54ZFM](https://gitee.com/dromara/Jpom/issues/I54ZFM)
4. 【server】SH配置和节点配置新增跨工作空间同步功能方便快速同步信息感谢[@陈旭](https://gitee.com/chenxu8989)
[Gitee issues I56YTU](https://gitee.com/dromara/Jpom/issues/I56YTU)
### 🐞 解决BUG、优化功能
1. 修复上传文件限制大小异常拦截不生效的问题(感谢@小工匠
2. 【server】新增配置前端消息弹窗位置属性 `jpom.notificationPlacement` (感谢[@Eibons](https://gitee.com/eibons) [Gitee issues I53V8B](https://gitee.com/dromara/Jpom/issues/I53V8B)
2. 【server】新增配置前端消息弹窗位置属性 `jpom.notificationPlacement`
(感谢[@Eibons](https://gitee.com/eibons) [Gitee issues I53V8B](https://gitee.com/dromara/Jpom/issues/I53V8B)
3. 【server】构建历史新增批量删除
4. 【server】修复关联分发项目无法选择不同节点下相同的项目的问题感谢[@宋建平](https://gitee.com/sjping) [Gitee issues I5680N](https://gitee.com/dromara/Jpom/issues/I5680N)
5. 【server】调整 docker-compose 使用卷方式存储数据,避免在部分环境中出现无法正常使用情况 (感谢 [@💎ℳ๓₯㎕斌💎💘](https://gitee.com/weihongbin) 贡献解决方案)(感谢[@笨笨巫师](https://gitee.com/zhangxin_gitosc) [Gitee issues I52OAV](https://gitee.com/dromara/Jpom/issues/I52OAV)
4.
【server】修复关联分发项目无法选择不同节点下相同的项目的问题感谢[@宋建平](https://gitee.com/sjping) [Gitee issues I5680N](https://gitee.com/dromara/Jpom/issues/I5680N)
5. 【server】调整 docker-compose 使用卷方式存储数据,避免在部分环境中出现无法正常使用情况 (感谢 [@💎ℳ๓₯㎕斌💎💘](https://gitee.com/weihongbin) 贡献解决方案)
(感谢[@笨笨巫师](https://gitee.com/zhangxin_gitosc) [Gitee issues I52OAV](https://gitee.com/dromara/Jpom/issues/I52OAV)
6. 【server】调整节点里面在部分情况下会出现空白行 (感谢[@💎ℳ๓₯㎕斌💎💘](https://gitee.com/weihongbin)
7. 【server】前端部分输入框添加`maxLength` 限制避免出现数据库字段长度不足问题(感谢@ccx2480
8. 【agent】修复项目下载远程文件解压方法错误感谢@背着砍刀的诗人
@ -99,7 +137,7 @@
10. 【server】优化报错构建时未判断构建产物越级问题感谢@奇奇
> 使用项目文件备份说明:
>
>
> 1. 默认未开启文件备份功能
> 2. 可以配置全局开启,插件端配置( `extConfig.yml` )文件中配置`project.fileBackupCount`属性
> 3. DSL 项目可以在配置内容新增 `file.backupCount` 来开启DSL 配置优先级最高)
@ -118,6 +156,7 @@
出现如上提示信息需要对插件端或者服务端进行配置合理对上传文件大小限制。
配置方式:在对应端的 `extConfig.yml` 配置文件中配置如下代码
```yaml
spring:
servlet:
@ -133,23 +172,28 @@ spring:
### 🐣 新增功能
1. 【server】SSH文件管理器中加入创建目录和文件的功能 (感谢 [@wxyShine](https://gitee.com/wxyShine) [Gitee PR 161](https://gitee.com/dromara/Jpom/pulls/161)
2. 【server】新增禁用登录图形验证码配置属性 `jpom.disabledCaptcha` (感谢 [@放学后的茶会](https://gitee.com/tt2yui) [Gitee issues I4GD0U](https://gitee.com/dromara/Jpom/issues/I4GD0U)
3. 【agent】节点项目文件管理新增创建文件夹/文件功能 (感谢 [@Eibons](https://gitee.com/eibons) [Gitee issues I4ZFFH](https://gitee.com/dromara/Jpom/issues/I4ZFFH)
1. 【server】SSH文件管理器中加入创建目录和文件的功能
(感谢 [@wxyShine](https://gitee.com/wxyShine) [Gitee PR 161](https://gitee.com/dromara/Jpom/pulls/161)
2. 【server】新增禁用登录图形验证码配置属性 `jpom.disabledCaptcha`
(感谢 [@放学后的茶会](https://gitee.com/tt2yui) [Gitee issues I4GD0U](https://gitee.com/dromara/Jpom/issues/I4GD0U)
3. 【agent】节点项目文件管理新增创建文件夹/文件功能
(感谢 [@Eibons](https://gitee.com/eibons) [Gitee issues I4ZFFH](https://gitee.com/dromara/Jpom/issues/I4ZFFH)
### 🐞 解决BUG、优化功能
1. 【server】本地构建命令、本地命令发布、ssh 发布支持加载仓库目录 `.env` 文件为环境变量 (感谢@z~
2. 【server】容器相关引用 maven 版本升级为 3.8.5
3. 【server】容器构建 DSL 示例添加镜像地址说明 (感谢 [@wxyShine](https://gitee.com/wxyShine) [Gitee PR 160](https://gitee.com/dromara/Jpom/pulls/160)
3. 【server】容器构建 DSL 示例添加镜像地址说明
(感谢 [@wxyShine](https://gitee.com/wxyShine) [Gitee PR 160](https://gitee.com/dromara/Jpom/pulls/160)
4. 【server】本地构建命令添加本次构建相关的默认变量感谢@杨杰)
5. 【server】优化 SHH 文件管理中文件上传,压缩包上传操作(感谢 [@wxyShine](https://gitee.com/wxyShine) [Gitee PR 161](https://gitee.com/dromara/Jpom/pulls/161)
6. 【agent】批量获取项目状态新增缓存避免部分环境获取项目状态超时感谢@奇奇
5. 【server】优化 SHH
文件管理中文件上传,压缩包上传操作(感谢 [@wxyShine](https://gitee.com/wxyShine) [Gitee PR 161](https://gitee.com/dromara/Jpom/pulls/161)
6. 【agent】批量获取项目状态新增缓存避免部分环境获取项目状态超时感谢@奇奇
7. 远程升级检查地址支持自定义配置,解决没有外网或者网络不同情况下自定义配置升级服务器
------
# 2.8.17 (2022-03-28)
### 🐣 新增功能
@ -157,7 +201,8 @@ spring:
### 🐞 解决BUG、优化功能
1. 【server】修复非超级管理员部分下载功能无法正常使用
2. 【server】ssh 私钥连接新增 `private key content` 登录 (感谢 [@震秦](https://gitee.com/zhzhenqin) [Gitee PR 159](https://gitee.com/dromara/Jpom/pulls/159)
2. 【server】ssh 私钥连接新增 `private key content` 登录
(感谢 [@震秦](https://gitee.com/zhzhenqin) [Gitee PR 159](https://gitee.com/dromara/Jpom/pulls/159)
3. 【server】修复非默认工作空间节点分发白名单无法保存问题感谢@愿好)
------
@ -307,7 +352,8 @@ spring:
6. 【server】本地构建命令 容器构建支持引用工作空间变量
7. 【server】修复构建触发器无法执行感谢@[老诗人](https://gitee.com/laoshirenggo)
8. 【server】服务端脚本新增工作空间环境变量
9. 修复检查 Jpom 包中没有释放资源(感谢@[大海](https://gitee.com/hasape) [Gitee issues I4T9L0](https://gitee.com/dromara/Jpom/issues/I4T9L0)
9. 修复检查 Jpom
包中没有释放资源(感谢@[大海](https://gitee.com/hasape) [Gitee issues I4T9L0](https://gitee.com/dromara/Jpom/issues/I4T9L0)
------
@ -527,7 +573,8 @@ spring:
9. 【server】发布命令SSH发布命令、本地命令支持变量替换`#{BUILD_ID}`、`#{BUILD_NAME}`、`#{BUILD_RESULT_FILE}`、`#{BUILD_NUMBER_ID}`
10. 【server】新增自动备份全量数据配置 `db.autoBackupIntervalDay` 默认一天备份一次,执行备份时间 凌晨0点或者中午12点
11. 【agent】项目的 webhook 新增项目启动成功后通知,并且参数新增 `type` 指包括:`beforeStop`,`start`,`stop`,`beforeRestart`
12. 【agent】项目新增自启动配置项,在 agent 启动时候检查对应项目是否启动,未启动执行启动逻辑 [Gitee issues I4IJFK](https://gitee.com/dromara/Jpom/issues/I4IJFK)
12. 【agent】项目新增自启动配置项,在 agent
启动时候检查对应项目是否启动,未启动执行启动逻辑 [Gitee issues I4IJFK](https://gitee.com/dromara/Jpom/issues/I4IJFK)
13. 【server】构建新增 webhook实时通知构建进度
14. 【server】节点分发新增分发间隔时间配置
15. 新增控制台日志配置数据 `consoleLog.charset` 避免部分服务器执行命令响应乱码 (感谢@……)
@ -576,132 +623,3 @@ spring:
>
> 8: 一个节点建议不要被多个服务端绑定(可能出现数据工作空间错乱情况)
------
# 2.7.3 (2021-12-02)
### 🐣 新增功能
1. 【server】新增自定义系统网页标题配置`jpom.name`
2. 【server】新增自定义系统网页 logo 配置`jpom.logoFile`
3. 【server】新增自定义系统登录页面标题配置`jpom.loginTitle`
4. 【server】新增自定义系统 logo 文字标题配置`jpom.subTitle`
5. 新增在线下载最新版本更新包功能(在线检测最新版本)
6. 【server】新增菜单`系统管理-数据库备份`,支持 Jpom 使用的 H2 数据库备份、还原
### 🐞 解决BUG、优化功能
1. 【server】恢复构建产物为匹配符无法正常发布问题感谢@Kay
2. 【server】恢复在线升级页面在二级路径下无法使用的问题 (感谢@hu向...🤡)
3. 【server】恢复构建执行命令阻塞问题感谢@小猿同学)
4. 【server】恢复限制 IP 访问和插件端授权信息不正确状态码冲突(感谢@小龙、@大灰灰)
5. 取消 tools.jar 依赖
6. 【server】优化初始化数据库流程避免多次执行相同修改节省启动时间
7. 【fix】恢复项目副本集乱码感谢@ʟᴊx
8. 【server】添加在线升级完成后的回调提示
9. 【server】ssh安装节点按钮动态显示
10. 【server】恢复构建信息中脚本过长无法构建的bug感谢@Dream
11. 在网页的编辑器中修改配置文件时兼容tab键感谢@Dream
> 取消 tools.jar 依赖后Java 项目状态监控使用 `jps` 命令实现
------
# 2.7.2 (fix)
### 🐣 新增功能
### 🐞 解决BUG、优化功能
1. 【agent】解决 nginx 编辑配置文件 url 编码问题
3. 【server】新增配置构建命令支持不检测删除命令 `build.checkDeleteCommand` (感谢@Dream)
------
# 2.7.1 (fix)
### 🐣 新增功能
### 🐞 解决BUG、优化功能
1. 解决插件端请求参数 url 编码无法解析问题(感谢@知识就是力量)
2. 【agent】项目文件夹为空不再提示错误信息
3. 【server】fix 编辑构建选择 ssh 发布无法保存 (感谢 @Peision [Gitee issues I4CQWA](https://gitee.com/dromara/Jpom/issues/I4CQWA)
4. 【server】fix ssh 终端未配置禁用命令不能输入空格问题
------
# 2.7.0 (beta)
### 🐣 新增功能
1. **【server】构建中的仓库独立管理**
2. **【server】构建信息存储方式调整为 h2 数据库,不再存储到 json 文件中**
3. **【server】构建触发器地址变更**
4. 【agent】新增文件管理中允许编辑的文件后缀以及对应后缀的文件编码
5. 项目文件管理中新增编辑按钮,支持编辑文本文件( 新版本 UI 同步新增该功能)
6. 程序启动输出默认 IP 地址和当前运行端口信息
7. bat 管理命令windows启动后输出日志文件,方便排查当前启动情况
8. 【server】上传文件到插件端节点超时配置独立,采用 server 端全局配置,配置参数 `node.uploadFileTimeOut`
(感谢 @LW 根据 Gitee [issues I3O8YE](https://gitee.com/dromara/Jpom/issues/I3O8YE)
9. 【server】角色新增添加权限配置 (感谢@misaka [Gitee pr](https://gitee.com/dromara/Jpom/pulls/141)
10. 【server】节点升级上传新包成功后删除历史包
11. 【server】新版本 UI 菜单系统管理、节点升级只有系统管理员可见
12. 【server】新版本 UI 脚本模板同步添加执行参数(感谢@轻描淡写 [Gitee issues I43G4B](https://gitee.com/dromara/Jpom/issues/I43G4B)
13. 【server】新版本 UI 同步添加 common.js
14. 【agent】项目文件管理新增下载远程文件功能
15. 【agent】节点首页监控新增实际使用内存占比linux系统 (感谢@大灰灰)
16. 【server】ssh 新增操作记录(方便查看执行历史回溯操作)
17. 【server】新增 h2 控制台配置属性,基于 SpringBoot,配置参数`spring.h2.console.enabled`
18. 【server】节点分发支持下载远程文件 (感谢@落泪归枫 [Gitee issues I1LM27](https://gitee.com/dromara/Jpom/issues/I1LM27)
19. 【server】节点分发支持 file 类型项目
20. 【agent】项目新增配置日志文件输出到指定目录
21. 【server】构建产物目录支持通配符`AntPathMatcher`模式 (感谢@saysay [Gitee issues I455FM](https://gitee.com/dromara/Jpom/issues/I455FM)
22. 【server】新增 h2 数据库缓存大小配置 [CACHE_SIZE](http://www.h2database.com/html/features.html#cache_settings) `db.cacheSize
23. 【server】构建触发器新增延迟执行参数感谢@Steve.Liu
24. 【server】增加全局项目搜索功能
25. 【agent】项目增加批量启动关闭重启
26. 【server】节点分发文件支持上传非压缩包感谢@Sam、風中飛絮 [Gitee issues I3YNA5](https://gitee.com/dromara/Jpom/issues/I3YNA5)
27. 【server】nginx 二级代理无法访问(感谢@hu向...🤡)
28. 【server】ssh文件管理新增在线编辑感谢@嗳啨 [Gitee issues I4ADTA](https://gitee.com/dromara/Jpom/issues/I4ADTA)
29. 在线升级支持上传 zip 包自动解析(感谢@Sam
30. 【server】ssh 安装插件端新增等待次数配置(感谢@hu向...🤡)
31. 【server】新增前端接口请求超时配置 `jpom.webApiTimeOut`(感谢@hu向...🤡)
32. 【server】构建支持 tag 通配符 (感谢@落泪归枫 [Gitee issues I1LM1V](https://gitee.com/dromara/Jpom/issues/I1LM1V)
### 🐞 解决BUG、优化功能
1. 【server】添加节点时候限制超时时间避免配置错误一直等待情况
2. 【server】优化限制 IP 白名单相关判断,避免手动修改错误后一直限制访问
3. 【server】添加 QQ 邮箱配置参照说明 [QQ邮箱官方文档](https://service.mail.qq.com/cgi-bin/help?subtype=1&&no=369&&id=28)
4. 【server】fix: 删除临时文件出现 `AccessDeniedException` 更新文件权限为可读(取消只读权限)
5. 【server】拉取 GIT 代码根据仓库路径添加 `synchronized`
6. 【server】节点管理页面支持刷新当前节点页面刷新不再回到首页
7. 【server】 jpom-service.sh 文件加载环境变量修改为 判断模式
8. 【agent】fix: windows 环境保存配置文件错误问题
9. 【agent】fix: 在线升级页面在没有配置白名单时候无法显示节点信息
10. 【server】ssh 快捷安装插件端检查配置文件不在使用 SpringBoot 非 public 工具类
11. 【server】请求节点发生异常打印具体堆栈、接口异常拦截器里面默认不打印堆栈 (根据 Gitee [issues I3O8YE](https://gitee.com/dromara/Jpom/issues/I3O8YE)
12. 【server】节点升级中偶尔出现无法获取到对应的版本信息问题感谢@misaka Gitee issues [I41TDY](https://gitee.com/dromara/Jpom/issues/I41TDY)
13. 本地运行数据目录位置改为`${user.home}/jpom/xxxx`、日志路径改为项目模块下
14. 【agent】升级 `commons-compress` 依赖 (来自 GitHub [advisories](https://github.com/advisories)
15. agent 和 server 间的 websocket 鉴权调整
16. 【server】update: 刷新整个页面的时候重新加载菜单
17. 历史监控图表查询报时间格式化错误(字符串工具类) (感谢@misaka [Gitee pr](https://gitee.com/dromara/Jpom/pulls/142)
18. 【agent】nginx 配置文件取消强制检测 server 节点
19. 【server】仓库密码改为隐藏
20. 解决退出登录验证码没有刷新问题 感谢群友Steve.Liu
21. 【agent】节点分发清空发布无效感谢@Sam
22. 【server】编写分发项目时当分发节点做替换、新增的操作后点击确认控制台报错感谢@tan90°
> 【特别声明】当前版本 仓库和构建并没有接入动态数据权限,如果对权限敏感的用户建议等待下一个版本优化权限后再升级(如有疑问可以微信群沟通)
> ⚠️ 注意1由于构建信息全部存储到 h2 数据库中,之前到构建信息会自动同步,在升级后到第一次启动需观察控制台信息,启动成功后请检查构建信息,仓库信息是否同步正确
>
> ⚠️ 注意2构建的触发器地址有更新需要重新获取触发器地址
>
> ⚠️ 注意3升级到该版本需要保证 agent、server 都保持同步,如果只升级 server 会出现项目控制台等功能无法正常使用
>
> ⚠️ 注意4升级 2.7.x 后不建议降级操作,会涉及到数据不兼容到情况
------

View File

@ -193,7 +193,7 @@ apt install -y wget && \
### 方式二:📦 容器化安装
> ⚠️ 注意:容器化安装方式需要先安装 docker[点击跳转docker安装文档](https://jpom.io/docs/#/%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8/%E5%AE%89%E8%A3%85/%E5%AE%B9%E5%99%A8%E5%8C%96%E5%AE%89%E8%A3%85)
> ⚠️ 注意:容器化安装方式需要先安装 docker[点击跳转docker安装文档](https://jpom.io/pages/b63dc5/)
#### 使用挂载方式存储相关数据(在部分环境可能出现兼容性问题)
@ -224,20 +224,18 @@ docker run -d -p 2122:2122 \
> 容器化安装仅提供服务端版。由于容器和宿主机环境隔离,而导致插件端的很多功能无法正常使用,因此对插件端容器化意义不大。
>
> 安装docker、配置镜像、自动启动、查找安装后所在目录等可参考文档 [https://jpom.io/docs/](https://jpom.io/docs/)
> 安装docker、配置镜像、自动启动、查找安装后所在目录等可参考文档 [https://jpom.io/pages/b63dc5/](https://jpom.io/pages/b63dc5/)
### 方式三:💾 下载安装
> 通过此方式安装有一些安装须知,具体查看:[帮助文档](https://jpom-docs.keepbx.cn/docs/#/安装使用/开始安装)
1. 下载安装包 [https://gitee.com/dromara/Jpom/attach_files](https://gitee.com/dromara/Jpom/attach_files)
2. 解压文件
3. 安装插件端[流程说明](https://jpom-docs.keepbx.cn/docs/#/安装使用/开始安装?id=安装插件端)
3. 安装插件端
1. agent-x.x.x-release 目录为插件端的全部安装文件
2. 上传到对应服务器(整个目录)
3. 启动插件端Windows 环境用 bat 脚本Linux 环境用 sh 脚本。(如果出现乱码或者无法正常执行,请检查编码格式、换行符是否匹配。)
4. 插件端默认运行端口:`2123`
4. 安装服务端[流程说明](https://jpom-docs.keepbx.cn/docs/#/安装使用/开始安装?id=安装服务端)
4. 安装服务端
1. server-x.x.x-release 目录为服务端的全部安装文件
2. 上传到对应服务器(整个目录)
3. 启动服务端Windows 环境用 bat 脚本Linux 环境用 sh 脚本。(如果出现乱码或者无法正常执行,请检查编码格式、换行符是否匹配。)
@ -245,18 +243,17 @@ docker run -d -p 2122:2122 \
### 方式四:⌨️ 编译安装
> 通过此方式安装有一些安装须知,具体查看:[帮助文档](https://jpom-docs.keepbx.cn/docs/#/安装使用/开始安装)
1. 访问 [Jpom](https://gitee.com/dromara/Jpom) 的码云主页,拉取最新完整代码(建议使用 master 分支)
2. 切换到 `web-vue` 目录,执行 `npm install`vue 环境需要提前搭建和安装依赖包详情可以查看 web-vue 目录下 README.md
3. 执行 `npm run build` 进行 vue 项目打包
4. 切换到项目根目录执行:`mvn clean package`
5. 安装插件端[流程说明](https://jpom-docs.keepbx.cn/docs/#/安装使用/开始安装?id=安装插件端)
5. 安装插件端
1. 查看插件端安装包 modules/agent/target/agent-x.x.x-release
2. 打包上传服务器运行(整个目录)
3. 启动插件端Windows 环境用 bat 脚本Linux 环境用 sh 脚本。(如果出现乱码或者无法正常执行,请检查编码格式、换行符是否匹配。)
4. 默认运行端口:`2123`
6. 安装服务端 [流程说明](https://jpom-docs.keepbx.cn/docs/#/安装使用/开始安装?id=安装服务端)
6. 安装服务端
1. 查看插件端安装包 modules/server/target/server-x.x.x-release
2. 打包上传服务器运行(整个目录)
3. 启动服务端Windows 环境用 bat 脚本Linux 环境用 sh 脚本。(如果出现乱码或者无法正常执行,请检查编码格式、换行符是否匹配。)
@ -379,28 +376,28 @@ mvn -f xxxx/pom.xml clean package
### Github Pages
- [文档主页](https://jpom.io/docs/)
- [FQA](https://jpom.io/docs/#/FQA/FQA)
- [名词解释](https://jpom.io/docs/index.html#/FQA/%E5%90%8D%E8%AF%8D%E8%A7%A3%E9%87%8A)
- [文档主页](https://jpom.io/)
- [FQA](https://jpom.io/pages/FQA/)
- [名词解释](https://jpom.io/pages/FQA/proper-noun/)
### Gitee Pages
- [文档主页](https://jpom-docs.keepbx.cn/docs/)
- [FQA](https://jpom-docs.keepbx.cn/docs/#/FQA/FQA)
- [名词解释](https://jpom-docs.keepbx.cn/docs/index.html#/FQA/%E5%90%8D%E8%AF%8D%E8%A7%A3%E9%87%8A)
- [文档主页](https://jpom-docs.keepbx.cn/)
- [FQA](https://jpom-docs.keepbx.cn/pages/FQA/)
- [名词解释](https://jpom-docs.keepbx.cn/pages/FQA/proper-noun/)
### 实践案例
> 里面有部分图片加载可能比较慢
1. [本地构建 + SSH 发布 java 项目](https://jpom.io/docs/index.html#/practice/build_java_ssh_release.md)
2. [本地构建 + 项目发布 node 项目](https://jpom.io/docs/index.html#/practice/build_node_release.md)
3. [本地构建 + SSH 发布 node 项目](https://jpom.io/docs/index.html#/practice/build_node_ssh_release.md)
4. [本地构建 + 自定义管理 python 项目](https://jpom.io/docs/index.html#/practice/project_dsl_python.md)
5. [自定义管理 java 项目](https://jpom.io/docs/index.html#/practice/project_dsl_java.md)
6. [管理编译安装的 nginx](https://jpom.io/docs/index.html#/practice/node_nginx.md)
7. [管理 docker](https://jpom.io/docs/index.html#/practice/docker-cli.md)
8. [容器构建 + 项目发布 java 项目](https://jpom.io/docs/index.html#/practice/build_docker_java_node_release.md)
1. [本地构建 + SSH 发布 java 项目](https://jpom.io/pages/practice/build-java-ssh-release)
2. [本地构建 + 项目发布 node 项目](https://jpom.io/pages/practice/build-node-release)
3. [本地构建 + SSH 发布 node 项目](https://jpom.io/pages/practice/build-node-ssh-release)
4. [本地构建 + 自定义管理 python 项目](https://jpom.io/pages/practice/project-dsl-python)
5. [自定义管理 java 项目](https://jpom.io/pages/practice/project-dsl-java)
6. [管理编译安装的 nginx](https://jpom.io/pages/practice/node-nginx)
7. [管理 docker](https://jpom.io/pages/practice/docker-cli)
8. [容器构建 + 项目发布 java 项目](https://jpom.io/pages/practice/build-docker-java-node-release)
## 🛠️ 整体架构
@ -446,11 +443,11 @@ Jpom 作为开源项目,离不开社区的支持,欢迎任何人修改和提
### 项目分支说明
| 分支 | 说明 |
| ------ | ------------------------------------------------------------ |
| 分支 | 说明 |
|--------|-----------------------------------------------|
| master | 主分支,受保护分支,此分支不接受 PR。在 dev 分支后经过测试没问题后会合并到此分支。 |
| dev | 开发分支,接受 PRPR 请提交到 dev 分支。 |
| docs | 项目文档分支,接受 PR介绍项目功能、汇总常见问题等。 |
| docs | 项目文档分支,接受 PR介绍项目功能、汇总常见问题等。 |
> 目前用到的主要是 dev 和 docs 分支,接受 PR 修改,其他的分支为归档分支,贡献者可以不用管。
@ -459,7 +456,7 @@ Jpom 作为开源项目,离不开社区的支持,欢迎任何人修改和提
1. 快扫描下方左侧微信群二维码和我们一起交流讨论吧!(添加小助手:备注 Jpom 进群)
2. 开源项目离不开社区的支持,如果项目帮助到了你,并且想给我们加个餐,欢迎扫描下方右侧[微信收款码赞赏](https://jpom-docs.keepbx.cn/images/wx-qrcode-praise.png)或通过[码云赞赏](https://gitee.com/dromara/Jpom)(在项目首页下方点击捐赠,支持微信和支付宝)。[赞赏记录](https://jpom-docs.keepbx.cn/docs/index.html#/praise)
3. 微信公众号:[CodeGzh](https://jpom-docs.keepbx.cn/docs/images/CodeGzh-QrCode.jpg) 查看一些基础教程
3. 微信公众号:[CodeGzh](https://jpom-docs.keepbx.cn/images/CodeGzh-QrCode.jpg) 查看一些基础教程
4. 反馈 BUG、提出建议欢迎新建[issues](https://gitee.com/dromara/Jpom/issues),开发人员会不定时查看回复。
6. 参与贡献,请查看[贡献指南](#🔨贡献指南)。

View File

@ -1 +1 @@
2.8.25
2.9.0

View File

@ -29,15 +29,17 @@
<parent>
<artifactId>jpom-parent</artifactId>
<groupId>io.jpom</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>agent</artifactId>
<version>2.8.25</version>
<version>2.9.0</version>
<name>Jpom Agent</name>
<properties>
<start-class>io.jpom.JpomAgentApplication</start-class>
<!--当前程序兼容 jpom 最新版本号-->
<jpom-min-version>1.0.0</jpom-min-version>
</properties>
<dependencies>
<dependency>

View File

@ -29,13 +29,13 @@
<parent>
<artifactId>jpom-parent</artifactId>
<groupId>io.jpom</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<name>Jpom Common</name>
<artifactId>common</artifactId>
<version>2.8.25</version>
<version>2.9.0</version>
<dependencies>

View File

@ -60,6 +60,9 @@ public class ExtConfigEnvironmentPostProcessor implements EnvironmentPostProcess
{
// 兼容一些全局默认配置属性
List<URL> resources = ClassUtil.getResources("bin/extConfigDefault.yml");
if (resources.isEmpty()) {
resources.add(ClassUtil.getResourceURL("bin/extConfigDefault.yml"));
}
for (int i = 0; i < resources.size(); i++) {
URL resource = resources.get(i);
Resource extConfigDefault = new UrlResource(resource);

View File

@ -32,11 +32,25 @@ import cn.hutool.core.util.StrUtil;
*/
public class JpomRuntimeException extends RuntimeException {
public JpomRuntimeException(String message) {
super(message);
}
/**
* 程序是否需要关闭
*/
private Integer exitCode;
public JpomRuntimeException(String message, Throwable throwable) {
super(StrUtil.format("{} {}", message, StrUtil.emptyToDefault(throwable.getMessage(), StrUtil.EMPTY)), throwable);
}
public JpomRuntimeException(String message) {
super(message);
}
public JpomRuntimeException(String message, Integer exitCode) {
super(message);
this.exitCode = exitCode;
}
public JpomRuntimeException(String message, Throwable throwable) {
super(StrUtil.format("{} {}", message, StrUtil.emptyToDefault(throwable.getMessage(), StrUtil.EMPTY)), throwable);
}
public Integer getExitCode() {
return exitCode;
}
}

View File

@ -7,4 +7,4 @@
| |
|_|
➜ Jpom \ (•◡•) / (v2.8.25)
➜ Jpom \ (•◡•) / (v2.9.0)

View File

@ -24,7 +24,7 @@
FROM maven:3.8.5-jdk-8
ENV JPOM_HOME /usr/local/jpom-server
ENV JPOM_PKG server-2.8.25-release.tar.gz
ENV JPOM_PKG server-2.9.0-release.tar.gz
ENV JPOM_DATA_PATH ${JPOM_HOME}/data
ENV JPOM_LOG_PATH ${JPOM_HOME}/log

View File

@ -29,13 +29,13 @@
<parent>
<artifactId>jpom-parent</artifactId>
<groupId>io.jpom</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<name>Jpom Server</name>
<artifactId>server</artifactId>
<version>2.8.25</version>
<version>2.9.0</version>
<properties>
<start-class>io.jpom.JpomServerApplication</start-class>
</properties>

View File

@ -78,11 +78,15 @@ public class JpomServerApplication implements ApplicationEventLoad {
* <p>
* --recover:h2db h2 数据出现奔溃无法启动需要执行恢复逻辑
* <p>
* --close:super_user_mfa 重置超级管理员 mfa
* --close:super_user_mfa 关闭超级管理员 mfa
* <p>
* --backup-h2 备份数据库
* <p>
* --import-h2-sql=/xxxx.sql 导入指定文件 sql
* <p>
* --replace-import-h2-sql=/xxxx.sql 替换导入指定文件 sql会删除掉已经存的数据
* <p>
* --transform-sql 转换 sql 内容(低版本兼容高版本),仅在导入 sql 文件时候生效--import-h2-sql=/xxxx.sql--replace-import-h2-sql=/xxxx.sql
*
* @param args 参数
* @throws Exception 异常
@ -162,21 +166,46 @@ public class JpomServerApplication implements ApplicationEventLoad {
String importH2Sql = StringUtil.getArgsValue(ARGS, "import-h2-sql");
if (StrUtil.isNotEmpty(importH2Sql)) {
// 导入数据
InitDb.addCallback(() -> {
File file = FileUtil.file(importH2Sql);
String sqlPath = FileUtil.getAbsolutePath(file);
if (!FileUtil.isFile(file)) {
consoleExit(2, "sql file does not exist :{}", sqlPath);
}
Console.log("Start importing data:{}", sqlPath);
BackupInfoService backupInfoService = SpringUtil.getBean(BackupInfoService.class);
boolean flag = backupInfoService.restoreWithSql(sqlPath);
if (!flag) {
consoleExit(2, "Failed to import according to sql,{}", sqlPath);
}
Console.log("Import successfully according to sql,{}", sqlPath);
});
importH2Sql(importH2Sql);
}
String replaceImportH2Sql = StringUtil.getArgsValue(ARGS, "replace-import-h2-sql");
if (StrUtil.isNotEmpty(replaceImportH2Sql)) {
// 删除掉旧数据
try {
String dbFiles = instance.deleteDbFiles();
if (dbFiles != null) {
Console.log("Automatically backup data files to {} path", dbFiles);
}
} catch (Exception e) {
e.printStackTrace();
consoleExit(-2, "Failed to import according to sql,{}", replaceImportH2Sql);
}
// 导入数据
importH2Sql(replaceImportH2Sql);
}
//
}
private static void importH2Sql(String importH2Sql) {
InitDb.addCallback(() -> {
File file = FileUtil.file(importH2Sql);
String sqlPath = FileUtil.getAbsolutePath(file);
if (!FileUtil.isFile(file)) {
consoleExit(2, "sql file does not exist :{}", sqlPath);
}
//
if (ArrayUtil.containsIgnoreCase(ARGS, "--transform-sql")) {
DbConfig.getInstance().transformSql(file);
}
//
Console.log("Start importing data:{}", sqlPath);
BackupInfoService backupInfoService = SpringUtil.getBean(BackupInfoService.class);
boolean flag = backupInfoService.restoreWithSql(sqlPath);
if (!flag) {
consoleExit(2, "Failed to import according to sql,{}", sqlPath);
}
Console.log("Import successfully according to sql,{}", sqlPath);
});
}
/**
@ -192,7 +221,7 @@ public class JpomServerApplication implements ApplicationEventLoad {
} else {
Console.error(template, args);
}
Console.log("Need to log out manually: Ctrl+C/Control+C ");
Console.log("has stopped running automaticallyNeed to log out manually: Ctrl+C/Control+C ");
System.exit(status);
}
}

View File

@ -238,8 +238,7 @@ public class BackupInfoService extends BaseDbService<BackupInfoModel> {
return true;
} catch (Exception e) {
// 记录错误日志信息返回数据库备份还原执行失败
log.error("restore H2 Database backup...catch exception...message: {}, cause: {}",
e.getMessage(), e.getCause());
log.error("restore H2 Database backup...catch exception...message: {}", e.getMessage(), e);
return false;
}
}

View File

@ -95,7 +95,7 @@ public class DockerInfoService extends BaseWorkspaceService<DockerInfoModel> imp
dockerInfoModel.setModifyUser(UserModel.SYSTEM_ADMIN);
this.insert(dockerInfoModel);
Console.log("Automatically add local docker host: {}", dockerHost);
} catch (Exception e) {
} catch (Throwable e) {
Console.error("There is no docker service local {}", e.getMessage());
}
}

View File

@ -41,6 +41,7 @@ import io.jpom.system.db.DbConfig;
import lombok.extern.slf4j.Slf4j;
import org.h2.jdbc.JdbcSQLNonTransientConnectionException;
import org.h2.jdbc.JdbcSQLNonTransientException;
import org.h2.mvstore.MVStoreException;
import org.springframework.util.Assert;
import java.util.Collection;
@ -263,13 +264,13 @@ public abstract class BaseDbCommonService<T> {
}
Entity where = new Entity(tableName);
where.set(key, keyValue);
Db db = Db.use();
db.setWrapper((Character) null);
if (consumer != null) {
consumer.accept(where);
}
Entity entity;
try {
Db db = Db.use();
db.setWrapper((Character) null);
if (consumer != null) {
consumer.accept(where);
}
entity = db.get(where);
} catch (Exception e) {
throw warpException(e);
@ -370,9 +371,9 @@ public abstract class BaseDbCommonService<T> {
if (where.isEmpty()) {
throw new JpomRuntimeException("没有删除条件");
}
Db db = Db.use();
db.setWrapper((Character) null);
try {
Db db = Db.use();
db.setWrapper((Character) null);
return db.del(where);
} catch (Exception e) {
throw warpException(e);
@ -674,11 +675,21 @@ public abstract class BaseDbCommonService<T> {
* @param e 异常
*/
protected JpomRuntimeException warpException(Exception e) {
String message = e.getMessage();
if (e instanceof MVStoreException || ExceptionUtil.isCausedBy(e, MVStoreException.class)) {
if (StrUtil.containsIgnoreCase(message, "The write format 1 is smaller than the supported format 2")) {
log.warn(message);
String tip = "升级数据库流程:" + StrUtil.LF;
tip += StrUtil.TAB + "1. 导出低版本数据 【启动程序参数里面添加 --backup-h2】" + StrUtil.LF;
tip += StrUtil.TAB + "2. 将导出的低版本数据( sql 文件) 导入到新版本中【启动程序参数里面添加 --replace-import-h2-sql=/xxxx.sql (路径需要替换为第一步控制台输出的 sql 文件保存路径)】";
return new JpomRuntimeException("数据库版本不兼容,需要处理跨版本升级。" + StrUtil.LF + tip + StrUtil.LF, -1);
}
}
if (e instanceof JdbcSQLNonTransientException || ExceptionUtil.isCausedBy(e, JdbcSQLNonTransientException.class)) {
return new JpomRuntimeException("数据库异常,可能数据库文件已经损坏(可能丢失部分数据),需要重新初始化。可以尝试在启动参数里面添加 --recover:h2db 来自动恢复," + e.getMessage(), e);
return new JpomRuntimeException("数据库异常,可能数据库文件已经损坏(可能丢失部分数据),需要重新初始化。可以尝试在启动参数里面添加 --recover:h2db 来自动恢复," + message, e);
}
if (e instanceof JdbcSQLNonTransientConnectionException) {
return new JpomRuntimeException("数据库异常,可能因为服务器资源不足(内存、硬盘)等原因造成数据异常关闭。需要手动重启服务端来恢复,:" + e.getMessage(), e);
return new JpomRuntimeException("数据库异常,可能因为服务器资源不足(内存、硬盘)等原因造成数据异常关闭。需要手动重启服务端来恢复,:" + message, e);
}
return new JpomRuntimeException("数据库异常", e);
}

View File

@ -35,7 +35,9 @@ import io.jpom.system.ExtConfigBean;
import io.jpom.system.extconf.DbExtConfig;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;
/**
* 数据库配置
@ -45,154 +47,191 @@ import java.util.*;
*/
public class DbConfig {
private static final String DB = "db";
private static final String DB = "db";
/**
* 默认的账号或者密码
*/
public static final String DEFAULT_USER_OR_AUTHORIZATION = "jpom";
/**
* 默认的账号或者密码
*/
public static final String DEFAULT_USER_OR_AUTHORIZATION = "jpom";
private static DbConfig dbConfig;
private static DbConfig dbConfig;
/**
* 是否初始化成功
*/
private volatile boolean init;
/**
* 是否初始化成功
*/
private volatile boolean init;
/**
* 恢复 sql 文件
*/
private File recoverSqlFile;
/**
* 恢复 sql 文件
*/
private File recoverSqlFile;
/**
* 单利模式
*
* @return config
*/
public static DbConfig getInstance() {
if (dbConfig == null) {
dbConfig = new DbConfig();
}
return dbConfig;
}
/**
* 单利模式
*
* @return config
*/
public static DbConfig getInstance() {
if (dbConfig == null) {
dbConfig = new DbConfig();
}
return dbConfig;
}
public void initOk() {
init = true;
}
public void initOk() {
init = true;
}
public void close() {
init = false;
}
public void close() {
init = false;
}
public boolean isInit() {
return init;
}
public boolean isInit() {
return init;
}
/**
* 获取数据库保存路径
*
* @return 默认本地数据目录下面的 db 目录
* @author bwcx_jzy
*/
public File dbLocalPath() {
return FileUtil.file(ExtConfigBean.getInstance().getPath(), DB);
}
/**
* 获取数据库保存路径
*
* @return 默认本地数据目录下面的 db 目录
* @author bwcx_jzy
*/
public File dbLocalPath() {
return FileUtil.file(ExtConfigBean.getInstance().getPath(), DB);
}
/**
* 获取数据库的jdbc 连接
*
* @return jdbc
*/
public String getDbUrl() {
DbExtConfig dbExtConfig = SpringUtil.getBean(DbExtConfig.class);
String dbUrl = dbExtConfig.getUrl();
if (StrUtil.isNotEmpty(dbUrl)) {
return dbUrl;
}
File file = FileUtil.file(this.dbLocalPath(), this.getDbName());
String path = FileUtil.getAbsolutePath(file);
return StrUtil.format("jdbc:h2:{};CACHE_SIZE={};MODE=MYSQL", path, dbExtConfig.getCacheSize().toKilobytes());
}
/**
* 获取数据库的jdbc 连接
*
* @return jdbc
*/
public String getDbUrl() {
DbExtConfig dbExtConfig = SpringUtil.getBean(DbExtConfig.class);
String dbUrl = dbExtConfig.getUrl();
if (StrUtil.isNotEmpty(dbUrl)) {
return dbUrl;
}
File file = FileUtil.file(this.dbLocalPath(), this.getDbName());
String path = FileUtil.getAbsolutePath(file);
return StrUtil.format("jdbc:h2:{};CACHE_SIZE={};MODE=MYSQL", path, dbExtConfig.getCacheSize().toKilobytes());
}
public String getDbName() {
return JpomApplication.getAppType().name();
}
public String getDbName() {
return JpomApplication.getAppType().name();
}
/**
* 加载 本地已经执行的记录
*
* @return sha1 log
* @author bwcx_jzy
*/
public Set<String> loadExecuteSqlLog() {
File localPath = this.dbLocalPath();
File file = FileUtil.file(localPath, "execute.init.sql.log");
if (!FileUtil.isFile(file)) {
// 不存在或者是文件夹
FileUtil.del(file);
return new LinkedHashSet<>();
}
List<String> strings = FileUtil.readLines(file, CharsetUtil.CHARSET_UTF_8);
return new LinkedHashSet<>(strings);
}
/**
* 转换 sql 文件内容,低版本兼容高版本
*
* @param sqlFile sql 文件
*/
public void transformSql(File sqlFile) {
List<String> list = FileUtil.readLines(sqlFile, StandardCharsets.UTF_8);
list = list.stream().map(s -> {
if (StrUtil.startWith(s, "CREATE PRIMARY KEY SYSTEM_LOB_STREAM_PRIMARY_KEY ON SYSTEM_LOB_STREAM(ID, PART);")) {
return "-- " + s;
}
return s;
}).collect(Collectors.toList());
FileUtil.writeLines(list, sqlFile, StandardCharsets.UTF_8);
}
/**
* 清除执行记录
*/
public void clearExecuteSqlLog() {
File localPath = this.dbLocalPath();
File file = FileUtil.file(localPath, "execute.init.sql.log");
FileUtil.del(file);
}
/**
* 加载 本地已经执行的记录
*
* @return sha1 log
* @author bwcx_jzy
*/
public Set<String> loadExecuteSqlLog() {
File localPath = this.dbLocalPath();
File file = FileUtil.file(localPath, "execute.init.sql.log");
if (!FileUtil.isFile(file)) {
// 不存在或者是文件夹
FileUtil.del(file);
return new LinkedHashSet<>();
}
List<String> strings = FileUtil.readLines(file, CharsetUtil.CHARSET_UTF_8);
return new LinkedHashSet<>(strings);
}
/**
* 恢复数据库
*
* @param dsFactory 数据库连接
*/
public void executeRecoverDbSql(DSFactory dsFactory) throws Exception {
if (!FileUtil.isFile(this.recoverSqlFile)) {
return;
}
//
IPlugin plugin = PluginFactory.getPlugin("db-h2");
Map<String, Object> map = new HashMap<>(10);
map.put("backupSqlPath", FileUtil.getAbsolutePath(this.recoverSqlFile));
map.put("dataSource", dsFactory.getDataSource());
plugin.execute("restoreBackupSql", map);
}
/**
* 清除执行记录
*/
public void clearExecuteSqlLog() {
File localPath = this.dbLocalPath();
File file = FileUtil.file(localPath, "execute.init.sql.log");
FileUtil.del(file);
}
/**
* 恢复数据库
*/
public void recoverDb() throws Exception {
File dbLocalPathFile = this.dbLocalPath();
if (!FileUtil.exist(dbLocalPathFile)) {
return;
}
String dbName = this.getDbName();
File recoverBackup = FileUtil.file(dbLocalPathFile, "recover_backup", DateTime.now().toString());
//
IPlugin plugin = PluginFactory.getPlugin("db-h2");
Map<String, Object> map = new HashMap<>(10);
map.put("dbName", dbName);
map.put("dbPath", dbLocalPathFile);
map.put("recoverBackup", recoverBackup);
File backupSql = (File) plugin.execute("recoverToSql", map);
// 清空记录
this.clearExecuteSqlLog();
// 记录恢复的 sql
this.recoverSqlFile = backupSql;
}
/**
* 恢复数据库
*
* @param dsFactory 数据库连接
*/
public void executeRecoverDbSql(DSFactory dsFactory) throws Exception {
if (!FileUtil.isFile(this.recoverSqlFile)) {
return;
}
//
IPlugin plugin = PluginFactory.getPlugin("db-h2");
Map<String, Object> map = new HashMap<>(10);
map.put("backupSqlPath", FileUtil.getAbsolutePath(this.recoverSqlFile));
map.put("dataSource", dsFactory.getDataSource());
plugin.execute("restoreBackupSql", map);
}
/**
* 保存本地已经执行的记录
*
* @author bwcx_jzy
*/
public void saveExecuteSqlLog(Set<String> logs) {
File localPath = this.dbLocalPath();
File file = FileUtil.file(localPath, "execute.init.sql.log");
FileUtil.writeUtf8Lines(logs, file);
}
/**
* 恢复数据库
*/
public void recoverDb() throws Exception {
File dbLocalPathFile = this.dbLocalPath();
if (!FileUtil.exist(dbLocalPathFile)) {
return;
}
String dbName = this.getDbName();
File recoverBackup = FileUtil.file(dbLocalPathFile, "recover_backup", DateTime.now().toString());
//
IPlugin plugin = PluginFactory.getPlugin("db-h2");
Map<String, Object> map = new HashMap<>(10);
map.put("dbName", dbName);
map.put("dbPath", dbLocalPathFile);
map.put("recoverBackup", recoverBackup);
File backupSql = (File) plugin.execute("recoverToSql", map);
// 清空记录
this.clearExecuteSqlLog();
// 记录恢复的 sql
this.recoverSqlFile = backupSql;
}
/**
* 恢复数据库
*/
public String deleteDbFiles() throws Exception {
File dbLocalPathFile = this.dbLocalPath();
if (!FileUtil.exist(dbLocalPathFile)) {
return null;
}
File deleteBackup = FileUtil.file(dbLocalPathFile, "recover_backup", DateTime.now().toString());
//
IPlugin plugin = PluginFactory.getPlugin("db-h2");
Map<String, Object> map = new HashMap<>(10);
map.put("dbName", this.getDbName());
map.put("dbPath", dbLocalPathFile);
map.put("backupPath", deleteBackup);
plugin.execute("deleteDbFiles", map);
// 清空记录
this.clearExecuteSqlLog();
return FileUtil.getAbsolutePath(deleteBackup);
}
/**
* 保存本地已经执行的记录
*
* @author bwcx_jzy
*/
public void saveExecuteSqlLog(Set<String> logs) {
File localPath = this.dbLocalPath();
File file = FileUtil.file(localPath, "execute.init.sql.log");
FileUtil.writeUtf8Lines(logs, file);
}
}

View File

@ -42,9 +42,11 @@ import io.jpom.model.data.UserModel;
import io.jpom.service.h2db.BaseGroupService;
import io.jpom.service.h2db.BaseNodeService;
import io.jpom.service.system.WorkspaceService;
import io.jpom.system.JpomRuntimeException;
import io.jpom.system.ServerExtConfigBean;
import io.jpom.system.db.DbConfig;
import io.jpom.system.extconf.DbExtConfig;
import lombok.Lombok;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
@ -215,6 +217,14 @@ public class InitDb implements DisposableBean, InitializingBean {
instance.loadSshInfo();
instance.loadMonitorInfo();
instance.loadOutgivinInfo();
} catch (JpomRuntimeException jpomRuntimeException) {
Integer exitCode = jpomRuntimeException.getExitCode();
if (exitCode != null) {
Console.error(jpomRuntimeException.getMessage());
System.exit(exitCode);
} else {
throw Lombok.sneakyThrow(jpomRuntimeException);
}
} finally {
BaseServerController.removeEmpty();
}

View File

@ -58,218 +58,218 @@ import java.util.stream.Stream;
* @since 2021-08-02
*/
public class LoadBuildJsonToDB {
private LoadBuildJsonToDB() {
private LoadBuildJsonToDB() {
}
}
/**
* 静态内部类实现单例模式
*/
public static class LoadBuildJsonToDBHolder {
private static final LoadBuildJsonToDB INSTANCE = new LoadBuildJsonToDB();
}
/**
* 静态内部类实现单例模式
*/
public static class LoadBuildJsonToDBHolder {
private static final LoadBuildJsonToDB INSTANCE = new LoadBuildJsonToDB();
}
public static LoadBuildJsonToDB getInstance() {
return LoadBuildJsonToDBHolder.INSTANCE;
}
public static LoadBuildJsonToDB getInstance() {
return LoadBuildJsonToDBHolder.INSTANCE;
}
/**
* read build.json file to list
* and then use list transfer SQL and execute it
*/
public void doJsonToSql() {
File backupOldData = FileUtil.file(ConfigBean.getInstance().getDataPath(), "backup_old_data");
// 读取 build.json 文件内容
File file = FileUtil.file(ConfigBean.getInstance().getDataPath(), ServerConfigBean.BUILD);
List<JSONObject> list = readBuildJsonFileToList(file);
// 判断 list 是否为空
if (null == list) {
if (!FileUtil.exist(FileUtil.file(backupOldData, ServerConfigBean.BUILD))) {
DefaultSystemLog.getLog().warn("There is no any data, the build.json file maybe no content or file is not exist...");
}
return;
}
// 转换成 SQL 执行
initSql(list);
// json 文件转移到备份目录
FileUtil.move(file, FileUtil.mkdir(backupOldData), true);
DefaultSystemLog.getLog().info("{} mv to {}", FileUtil.getAbsolutePath(file), FileUtil.getAbsolutePath(backupOldData));
}
/**
* read build.json file to list
* and then use list transfer SQL and execute it
*/
public void doJsonToSql() {
File backupOldData = FileUtil.file(ConfigBean.getInstance().getDataPath(), "backup_old_data");
// 读取 build.json 文件内容
File file = FileUtil.file(ConfigBean.getInstance().getDataPath(), ServerConfigBean.BUILD);
List<JSONObject> list = readBuildJsonFileToList(file);
// 判断 list 是否为空
if (null == list) {
if (!FileUtil.exist(FileUtil.file(backupOldData, ServerConfigBean.BUILD))) {
//DefaultSystemLog.getLog().warn("There is no any data, the build.json file maybe no content or file is not exist...");
}
return;
}
// 转换成 SQL 执行
initSql(list);
// json 文件转移到备份目录
FileUtil.move(file, FileUtil.mkdir(backupOldData), true);
DefaultSystemLog.getLog().info("{} mv to {}", FileUtil.getAbsolutePath(file), FileUtil.getAbsolutePath(backupOldData));
}
/**
* list data to SQL
* this method is core logic
* 1. load fields will be insert into database from class with reflection
* 2. iterate list data, and transfer each element to SQL (element's properties mapping to SQL param name and value)
* 3. use param map generate SQL
* 4. exec SQL
* ---------------------
* 这个方法是核心逻辑
* 1.通过反射加载字段将其插入到数据库中
* 2. 迭代列表数据并将每个元素转移到参数集合元素的属性映射到SQL参数名称和值
* 3. 使用参数集合对象生成SQL
* 4. 执行SQL
*
* @param list data from build.json
*/
private void initSql(List<JSONObject> list) {
// 加载类里面的属性用反射获取
final List<String> repositoryFieldList = getClassFieldList(RepositoryModel.class);
final List<String> buildInfoFieldList = getClassFieldList(BuildInfoModel.class);
final Map<String, String> repositoryCache = new HashMap<>(list.size());
/**
* list data to SQL
* this method is core logic
* 1. load fields will be insert into database from class with reflection
* 2. iterate list data, and transfer each element to SQL (element's properties mapping to SQL param name and value)
* 3. use param map generate SQL
* 4. exec SQL
* ---------------------
* 这个方法是核心逻辑
* 1.通过反射加载字段将其插入到数据库中
* 2. 迭代列表数据并将每个元素转移到参数集合元素的属性映射到SQL参数名称和值
* 3. 使用参数集合对象生成SQL
* 4. 执行SQL
*
* @param list data from build.json
*/
private void initSql(List<JSONObject> list) {
// 加载类里面的属性用反射获取
final List<String> repositoryFieldList = getClassFieldList(RepositoryModel.class);
final List<String> buildInfoFieldList = getClassFieldList(BuildInfoModel.class);
final Map<String, String> repositoryCache = new HashMap<>(list.size());
// 遍历对象集合
list.forEach(buildModelVo -> {
DefaultSystemLog.getLog().debug("buildModelVo: {}", JSON.toJSONString(buildModelVo));
// 遍历对象集合
list.forEach(buildModelVo -> {
DefaultSystemLog.getLog().debug("buildModelVo: {}", JSON.toJSONString(buildModelVo));
// 拿到构造 SQL 的参数
String gitUrl = buildModelVo.getString("gitUrl");
//buildModelVo.getGitUrl();
String repositoryId = repositoryCache.get(gitUrl);
if (StrUtil.isEmpty(repositoryId)) {
// 先存储仓库信息
Map<String, Object> repositoryParamMap = initSqlParamMap(repositoryFieldList, buildModelVo);
// add def protocol
repositoryParamMap.put("PROTOCOL", GitProtocolEnum.HTTP.getCode());
// 构造 insert SQL 语句
String insertRepositorySql = initInsertSql(repositoryParamMap, RepositoryModel.class);
// 插入数据库
insertToDB(insertRepositorySql);
repositoryId = (String) repositoryParamMap.get("ID");
// cache
repositoryCache.put(gitUrl, repositoryId);
}
// 拿到构造 SQL 的参数
String gitUrl = buildModelVo.getString("gitUrl");
//buildModelVo.getGitUrl();
String repositoryId = repositoryCache.get(gitUrl);
if (StrUtil.isEmpty(repositoryId)) {
// 先存储仓库信息
Map<String, Object> repositoryParamMap = initSqlParamMap(repositoryFieldList, buildModelVo);
// add def protocol
repositoryParamMap.put("PROTOCOL", GitProtocolEnum.HTTP.getCode());
// 构造 insert SQL 语句
String insertRepositorySql = initInsertSql(repositoryParamMap, RepositoryModel.class);
// 插入数据库
insertToDB(insertRepositorySql);
repositoryId = (String) repositoryParamMap.get("ID");
// cache
repositoryCache.put(gitUrl, repositoryId);
}
Map<String, Object> buildInfoParamMap = initSqlParamMap(buildInfoFieldList, buildModelVo);
// 绑定仓库ID
buildInfoParamMap.put("REPOSITORYID", repositoryId);
// 构建发布操作信息
JSONObject jsonObject = new JSONObject();
String releaseMethodDataId = buildModelVo.getString("releaseMethodDataId");
jsonObject.put("releaseMethodDataId", releaseMethodDataId);
jsonObject.put("afterOpt", buildModelVo.getInteger("afterOpt"));
jsonObject.put("clearOld", buildModelVo.getBoolean("clearOld"));
jsonObject.put("releaseCommand", buildModelVo.getString("releaseCommand"));
jsonObject.put("releasePath", buildModelVo.getString("releasePath"));
// 保存信息
buildInfoParamMap.put("EXTRADATA", jsonObject.toJSONString());
buildInfoParamMap.put("RELEASEMETHODDATAID", releaseMethodDataId);
String insertBuildInfoSql = initInsertSql(buildInfoParamMap, BuildInfoModel.class);
Map<String, Object> buildInfoParamMap = initSqlParamMap(buildInfoFieldList, buildModelVo);
// 绑定仓库ID
buildInfoParamMap.put("REPOSITORYID", repositoryId);
// 构建发布操作信息
JSONObject jsonObject = new JSONObject();
String releaseMethodDataId = buildModelVo.getString("releaseMethodDataId");
jsonObject.put("releaseMethodDataId", releaseMethodDataId);
jsonObject.put("afterOpt", buildModelVo.getInteger("afterOpt"));
jsonObject.put("clearOld", buildModelVo.getBoolean("clearOld"));
jsonObject.put("releaseCommand", buildModelVo.getString("releaseCommand"));
jsonObject.put("releasePath", buildModelVo.getString("releasePath"));
// 保存信息
buildInfoParamMap.put("EXTRADATA", jsonObject.toJSONString());
buildInfoParamMap.put("RELEASEMETHODDATAID", releaseMethodDataId);
String insertBuildInfoSql = initInsertSql(buildInfoParamMap, BuildInfoModel.class);
insertToDB(insertBuildInfoSql);
});
}
insertToDB(insertBuildInfoSql);
});
}
/**
* exec insert SQL to DB
*
* @param sql SQL for insert
*/
private void insertToDB(String sql) {
DSFactory dsFactory = GlobalDSFactory.get();
int rows = 0;
try {
rows = Db.use(dsFactory.getDataSource()).execute(sql);
} catch (SQLException e) {
DefaultSystemLog.getLog().warn("exec SQL: {} failed", sql, e);
}
DefaultSystemLog.getLog().info("exec SQL: {} complete, and affected rows is: {}", sql, rows);
}
/**
* exec insert SQL to DB
*
* @param sql SQL for insert
*/
private void insertToDB(String sql) {
DSFactory dsFactory = GlobalDSFactory.get();
int rows = 0;
try {
rows = Db.use(dsFactory.getDataSource()).execute(sql);
} catch (SQLException e) {
DefaultSystemLog.getLog().warn("exec SQL: {} failed", sql, e);
}
DefaultSystemLog.getLog().info("exec SQL: {} complete, and affected rows is: {}", sql, rows);
}
/**
* init insert SQL with param map and table name
*
* @param paramMap
* @param clazz 实体类
* @return
*/
private String initInsertSql(Map<String, Object> paramMap, Class<?> clazz) {
TableName tableName = clazz.getAnnotation(TableName.class);
Assert.notNull(tableName, "not find table name");
// 构造 insert SQL 语句
StringBuffer sqlBuffer = new StringBuffer("merge into {} ( ");
StringBuilder sqlFieldNameBuffer = new StringBuilder();
StringBuilder sqlFieldValueBuffer = new StringBuilder();
for (int i = 0; i < paramMap.size(); i++) {
sqlFieldNameBuffer.append("`{}`,");
sqlFieldValueBuffer.append("'{}',");
}
sqlBuffer.append(sqlFieldNameBuffer.substring(0, sqlFieldNameBuffer.length() - 1))
.append(" )")
.append(" values ( ")
.append(sqlFieldValueBuffer.substring(0, sqlFieldValueBuffer.length() - 1))
.append(" )");
/**
* init insert SQL with param map and table name
*
* @param paramMap
* @param clazz 实体类
* @return
*/
private String initInsertSql(Map<String, Object> paramMap, Class<?> clazz) {
TableName tableName = clazz.getAnnotation(TableName.class);
Assert.notNull(tableName, "not find table name");
// 构造 insert SQL 语句
StringBuffer sqlBuffer = new StringBuffer("merge into {} ( ");
StringBuilder sqlFieldNameBuffer = new StringBuilder();
StringBuilder sqlFieldValueBuffer = new StringBuilder();
for (int i = 0; i < paramMap.size(); i++) {
sqlFieldNameBuffer.append("`{}`,");
sqlFieldValueBuffer.append("'{}',");
}
sqlBuffer.append(sqlFieldNameBuffer.substring(0, sqlFieldNameBuffer.length() - 1))
.append(" )")
.append(" values ( ")
.append(sqlFieldValueBuffer.substring(0, sqlFieldValueBuffer.length() - 1))
.append(" )");
// 构造 SQL 参数
List<Object> params = new ArrayList<>();
params.add(tableName.value());
params.addAll(paramMap.keySet());
params.addAll(paramMap.values());
return StrUtil.format(sqlBuffer, params.toArray());
}
// 构造 SQL 参数
List<Object> params = new ArrayList<>();
params.add(tableName.value());
params.addAll(paramMap.keySet());
params.addAll(paramMap.values());
return StrUtil.format(sqlBuffer, params.toArray());
}
/**
* init param map for create insert SQL
*
* @param fieldList 字段名 list
* @param jsonObject json
* @return map key value
*/
private Map<String, Object> initSqlParamMap(List<String> fieldList, JSONObject jsonObject) {
Map<String, Object> map = new HashMap<>(fieldList.size());
/**
* init param map for create insert SQL
*
* @param fieldList 字段名 list
* @param jsonObject json
* @return map key value
*/
private Map<String, Object> initSqlParamMap(List<String> fieldList, JSONObject jsonObject) {
Map<String, Object> map = new HashMap<>(fieldList.size());
fieldList.forEach(fieldName -> {
// 判断类里面是否有这个属性
Object filedValue = jsonObject.get(fieldName);
if (filedValue == null) {
return;
}
// 添加到参数对象中
String sqlFiledName = fieldName.toUpperCase();
map.put(sqlFiledName, filedValue);
});
// 同步数据创建时间
String modifyTime = jsonObject.getString("modifyTime");
if (StrUtil.isNotEmpty(modifyTime)) {
map.put("CREATETIMEMILLIS", DateUtil.parse(modifyTime).getTime());
}
return map;
}
fieldList.forEach(fieldName -> {
// 判断类里面是否有这个属性
Object filedValue = jsonObject.get(fieldName);
if (filedValue == null) {
return;
}
// 添加到参数对象中
String sqlFiledName = fieldName.toUpperCase();
map.put(sqlFiledName, filedValue);
});
// 同步数据创建时间
String modifyTime = jsonObject.getString("modifyTime");
if (StrUtil.isNotEmpty(modifyTime)) {
map.put("CREATETIMEMILLIS", DateUtil.parse(modifyTime).getTime());
}
return map;
}
/**
* read build.json file to list
*
* @return List<BuildModelVo>
*/
private List<JSONObject> readBuildJsonFileToList(File file) {
if (!file.exists()) {
DefaultSystemLog.getLog().debug("there is no build.json file...");
return null;
}
try {
// 读取 build.json 文件里面的内容转换成实体对象集合
JSONObject jsonObject = (JSONObject) JsonFileUtil.readJson(file.getAbsolutePath());
return jsonObject.keySet().stream()
.map(jsonObject::get)
.flatMap((Function<Object, Stream<JSONObject>>) o -> Stream.of((JSONObject) o))
.collect(Collectors.toList());
} catch (FileNotFoundException e) {
DefaultSystemLog.getLog().error("read build.json file failed...caused: {}...message: {}", e.getCause(), e.getMessage());
}
return null;
}
/**
* read build.json file to list
*
* @return List<BuildModelVo>
*/
private List<JSONObject> readBuildJsonFileToList(File file) {
if (!file.exists()) {
DefaultSystemLog.getLog().debug("there is no build.json file...");
return null;
}
try {
// 读取 build.json 文件里面的内容转换成实体对象集合
JSONObject jsonObject = (JSONObject) JsonFileUtil.readJson(file.getAbsolutePath());
return jsonObject.keySet().stream()
.map(jsonObject::get)
.flatMap((Function<Object, Stream<JSONObject>>) o -> Stream.of((JSONObject) o))
.collect(Collectors.toList());
} catch (FileNotFoundException e) {
DefaultSystemLog.getLog().error("read build.json file failed...caused: {}...message: {}", e.getCause(), e.getMessage());
}
return null;
}
/**
* 获取 clazz 类里面的属性转换成集合返回
*
* @param clazz 实体类
* @return List<String>
*/
private List<String> getClassFieldList(Class<?> clazz) {
final Field[] fields = ReflectUtil.getFieldsDirectly(clazz, true);
return Arrays.stream(fields)
.filter(field -> Modifier.isPrivate(field.getModifiers()))
.flatMap(field -> Arrays.stream(new String[]{field.getName()}))
.collect(Collectors.toList());
}
/**
* 获取 clazz 类里面的属性转换成集合返回
*
* @param clazz 实体类
* @return List<String>
*/
private List<String> getClassFieldList(Class<?> clazz) {
final Field[] fields = ReflectUtil.getFieldsDirectly(clazz, true);
return Arrays.stream(fields)
.filter(field -> Modifier.isPrivate(field.getModifiers()))
.flatMap(field -> Arrays.stream(new String[]{field.getName()}))
.collect(Collectors.toList());
}
}

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-plugins-parent</artifactId>
<groupId>io.jpom.plugins</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-plugins-parent</artifactId>
<groupId>io.jpom.plugins</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -30,6 +30,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.db.ds.DSFactory;
import cn.jiangzeyin.common.DefaultSystemLog;
import org.h2.store.FileLister;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Recover;
import org.h2.tools.RunScript;
import org.h2.tools.Shell;
@ -77,6 +78,11 @@ public class DefaultDbH2PluginImpl implements IDefaultPlugin {
String dbName = (String) parameter.get("dbName");
File recoverBackup = (File) parameter.get("recoverBackup");
return this.recover(dbPath, dbName, recoverBackup);
} else if (StrUtil.equals("deleteDbFiles", method)) {
File dbPath = (File) parameter.get("dbPath");
String dbName = (String) parameter.get("dbName");
File backupPath = (File) parameter.get("backupPath");
this.deleteDbFiles(dbPath, dbName, backupPath);
} else {
throw new IllegalArgumentException("不支持的类型");
}
@ -111,6 +117,31 @@ public class DefaultDbH2PluginImpl implements IDefaultPlugin {
return FileUtil.file(recoverBackup, dbName + ".h2.sql");
}
/**
* 删除数据
*
* @param dbPath 数据库路径
* @param dbName 数据库名
* @throws SQLException sql
*/
private void deleteDbFiles(File dbPath, String dbName, File backupPath) throws SQLException {
String dbLocalPath = FileUtil.getAbsolutePath(dbPath);
ArrayList<String> list = FileLister.getDatabaseFiles(dbLocalPath, dbName, true);
if (CollUtil.isEmpty(list)) {
return;
}
if (backupPath != null) {
FileUtil.mkdir(backupPath);
// 备份数据
for (String s : list) {
FileUtil.move(FileUtil.file(s), backupPath, true);
}
}
// 删除数据
DeleteDbFiles deleteDbFiles = new DeleteDbFiles();
deleteDbFiles.runTool("-dir", dbLocalPath, "-db", dbName);
}
/**
* 备份 SQL
*
@ -145,11 +176,11 @@ public class DefaultDbH2PluginImpl implements IDefaultPlugin {
* - table 表示需要备份的表名称后面跟多个表名用英文逗号分割
*/
String[] params = new String[]{
"-url", url,
"-user", user,
"-password", password,
"-driver", "org.h2.Driver",
"-sql", sql
"-url", url,
"-user", user,
"-password", password,
"-driver", "org.h2.Driver",
"-sql", sql
};
try (FastByteArrayOutputStream arrayOutputStream = new FastByteArrayOutputStream()) {
try (PrintStream printStream = new PrintStream(arrayOutputStream)) {

View File

@ -127,7 +127,7 @@ CREATE TABLE IF NOT EXISTS PUBLIC.SSH_INFO
`name` VARCHAR(50) comment '名称',
host varchar(100) not null comment 'ssh host IP',
port int not null comment '端口',
user varchar(100) not null comment '用户',
`user` varchar(100) not null comment '用户',
password varchar(100) comment '密码',
charset varchar(100) comment '编码格式',
fileDirs CLOB comment '文件目录',

View File

@ -0,0 +1,50 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import org.junit.Test;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author bwcx_jzy
* @since 2022/6/10
*/
public class TestH2Sql {
@Test
public void test() {
File sqlFile = FileUtil.file("/Users/user/jpom/server/db/backup/20220609131122.sql");
List<String> list = FileUtil.readLines(sqlFile, StandardCharsets.UTF_8);
list = list.stream().map(s -> {
if (StrUtil.startWith(s, "CREATE PRIMARY KEY SYSTEM_LOB_STREAM_PRIMARY_KEY ON SYSTEM_LOB_STREAM(ID, PART);")) {
return "-- " + s;
}
return s;
}).collect(Collectors.toList());
FileUtil.writeLines(list, sqlFile, StandardCharsets.UTF_8);
}
}

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-plugins-parent</artifactId>
<groupId>io.jpom.plugins</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-plugins-parent</artifactId>
<groupId>io.jpom.plugins</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-plugins-parent</artifactId>
<groupId>io.jpom.plugins</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-plugins-parent</artifactId>
<groupId>io.jpom.plugins</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-parent</artifactId>
<groupId>io.jpom</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<packaging>pom</packaging>
@ -44,7 +44,7 @@
<module>git-clone</module>
</modules>
<modelVersion>4.0.0</modelVersion>
<version>2.8.25</version>
<version>2.9.0</version>
<groupId>io.jpom.plugins</groupId>
<artifactId>jpom-plugins-parent</artifactId>
<name>Jpom Plugins</name>

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-plugins-parent</artifactId>
<groupId>io.jpom.plugins</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -29,7 +29,7 @@
<parent>
<artifactId>jpom-plugins-parent</artifactId>
<groupId>io.jpom.plugins</groupId>
<version>2.8.25</version>
<version>2.9.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

14
pom.xml
View File

@ -38,7 +38,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath />
<relativePath/>
</parent>
<groupId>io.jpom</groupId>
<artifactId>jpom-parent</artifactId>
@ -47,22 +47,22 @@
简而轻的低侵入式在线构建、自动部署、日常运维、项目监控软件
</description>
<inceptionYear>2017</inceptionYear>
<version>2.8.25</version>
<version>2.9.0</version>
<url>https://gitee.com/dromara/Jpom</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<!--当前程序兼容 jpom 最新版本号-->
<jpom-min-version>1.0.0</jpom-min-version>
<spring-boot.version>2.6.3</spring-boot.version>
<jpom-min-version>2.9.0</jpom-min-version>
<spring-boot.version>2.6.7</spring-boot.version>
<common-boot.version>2.2.4</common-boot.version>
<h2.version>1.4.200</h2.version>
<common-boot.version>2.3.0</common-boot.version>
<h2.version>2.1.212</h2.version>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
<maven.test.skip>true</maven.test.skip>
<!-- <maven.javadoc.skip>true</maven.javadoc.skip>-->
<hutool-all.version>5.7.22</hutool-all.version>
<hutool-all.version>5.8.2</hutool-all.version>
</properties>
<dependencies>
<dependency>

View File

@ -29,12 +29,12 @@
# https://hub.docker.com/r/jpomdocker/jpom
# 服务端
docker buildx build --platform linux/amd64,linux/arm64 -t jpomdocker/jpom:2.8.25 -f ./modules/server/DockerfileRelease --push .
docker buildx build --platform linux/amd64,linux/arm64 -t jpomdocker/jpom:2.9.0 -f ./modules/server/DockerfileRelease --push .
#
docker buildx build --platform linux/amd64,linux/arm64 -t jpomdocker/jpom:latest -f ./modules/server/DockerfileRelease --push .
# docker logs --tail="100" jpom-server
# docker run -d -p 2122:2122 --name jpom-server -v /etc/localtime:/etc/localtime:ro -v jpom-server-vol:/usr/local/jpom-server jpomdocker/jpom:mac-arm-2.8.25
# docker run -d -p 2122:2122 --name jpom-server -v /etc/localtime:/etc/localtime:ro -v jpom-server-vol:/usr/local/jpom-server jpomdocker/jpom:mac-arm-2.9.0
# docker stop jpom-server
# docker rm jpom-server
# docker exec -it jpom-server /bin/bash

View File

@ -24,7 +24,7 @@
# 版本
jpom_version=2.8.25
jpom_version=2.9.0
function checkItem()
{

View File

@ -1,6 +1,6 @@
{
"name": "jpom-vue",
"version": "2.8.25",
"version": "2.9.0",
"private": true,
"scripts": {
"serve": "vue-cli-service --max-old-space-size=900 serve --mode dev",

View File

@ -1,5 +1,5 @@
<template>
<div id="xterm" class="xterm" style="height: 70vh" />
<div id="xterm" class="xterm" />
</template>
<script>
import "xterm/css/xterm.css";
@ -131,9 +131,11 @@ export default {
};
</script>
<style scoped lang="stylus">
.xterm{
margin-left: -8px;
<style scoped>
.xterm {
display: flex;
flex-flow: column;
height: 100%;
flex: 1;
}
</style>

View File

@ -68,7 +68,15 @@
</template>
<template slot="operation" slot-scope="text, record">
<a-space>
<a-button size="small" type="primary" @click="handleTerminal(record)">终端</a-button>
<a-dropdown>
<a-button size="small" type="primary" @click="handleTerminal(record, false)">终端<a-icon type="down" /></a-button>
<a-menu slot="overlay">
<a-menu-item key="1">
<a-button size="small" type="primary" icon="fullscreen" @click="handleTerminal(record, true)">全屏终端</a-button>
</a-menu-item>
</a-menu>
</a-dropdown>
<a-tooltip placement="topLeft" title="如果按钮不可用,请去 ssh 编辑中添加允许管理的授权文件夹">
<a-button size="small" type="primary" :disabled="!record.fileDirs" @click="handleFile(record)">文件</a-button>
</a-tooltip>
@ -255,7 +263,25 @@
<ssh-file v-if="drawerVisible" :ssh="temp" />
</a-drawer>
<!-- Terminal -->
<a-modal v-model="terminalVisible" width="80vw" :title="temp.name" :footer="null" :maskClosable="false">
<a-modal
:dialogStyle="{
maxWidth: '100vw',
top: this.terminalFullscreen ? 0 : false,
paddingBottom: 0,
}"
:width="this.terminalFullscreen ? '100vw' : '80vw'"
:bodyStyle="{
padding: '0px 10px',
paddingTop: '10px',
marginRight: '10px',
height: `${this.terminalFullscreen ? 'calc(100vh - 56px)' : '70vh'}`,
}"
v-model="terminalVisible"
:title="temp.name"
:footer="null"
:maskClosable="false"
:destroyOnClose="true"
>
<terminal v-if="terminalVisible" :sshId="temp.id" />
</a-modal>
<!-- 操作日志 -->
@ -369,6 +395,7 @@ export default {
drawerTitle: "",
drawerVisible: false,
terminalVisible: false,
terminalFullscreen: false,
viewOperationLog: false,
viewOperationLoading: false,
viewOperationLogList: [],
@ -442,7 +469,7 @@ export default {
title: "操作",
dataIndex: "operation",
scopedSlots: { customRender: "operation" },
width: 180,
width: 200,
align: "center",
// ellipsis: true,
},
@ -565,9 +592,10 @@ export default {
});
},
//
handleTerminal(record) {
handleTerminal(record, terminalFullscreen) {
this.temp = Object.assign({}, record);
this.terminalVisible = true;
this.terminalFullscreen = terminalFullscreen;
},
//
handleViewLog(record) {