230 Commits

Author SHA1 Message Date
shizhendong
04ddd58d02 feat: ASW-205 新增 application rename 接口 2024-11-28 15:37:07 +08:00
zhangshuai
3611f9226c feat: ASW-199 调整 cron 校验 2024-11-28 15:02:42 +08:00
zhangshuai
0c5c22f567 feat: ASW-199 rcode 补充提交 2024-11-28 14:55:49 +08:00
zhangshuai
e91b0639ce feat: ASW-199 job 接口开发 2024-11-28 14:33:59 +08:00
shizhendong
feedb7a4b4 feat: 补充 workspace_delete 菜单按钮 2024-11-28 14:20:38 +08:00
zhangshuai
28909428fa fix: 修复job 取消后未删除队列中job 2024-11-28 10:30:49 +08:00
zhangshuai
b398e32f2d fix: job 添加job_run和job_edit按钮 2024-11-27 15:09:02 +08:00
zhangshuai
7cafa08aee fix: 修复下载 pcap 文件名称错误 2024-11-26 16:13:47 +08:00
zhangshuai
b189c5d9d0 feat: ASW-192 playbook支持python 脚本类型 2024-11-26 10:13:20 +08:00
zhangshuai
afcbbe30b6 fix:ASW-191 job 增加 artifacts 参数 2024-11-25 15:03:55 +08:00
shizhendong
5444bf2c0b feat: ASW-185 application release 补充编辑接口
1. application release 补充编辑接口
2. sys_menu 添加 application_release 菜单和按钮
2024-11-22 15:22:29 +08:00
shizhendong
3cc928d7a7 feat: ASW-182 新增 tag,release 接口 2024-11-21 14:59:54 +08:00
zhangshuai
70feac12fc fix: 调整 job 执行流程 2024-11-20 16:47:19 +08:00
zhangshuai
0133dc72b2 fix: 调整 job 属性名称 2024-11-20 13:35:30 +08:00
shizhendong
30b3f1c101 refactor: application 模块代码重构,将部分 jgit 代码拆分开 2024-11-20 09:58:05 +08:00
zhangshuai
ec4c58db4e feat: ASW-181 playbook 修改接口开发 2024-11-19 11:34:24 +08:00
shizhendong
9ba7619752 fix: 解决 commit diff 接口重复获取 commitId 信息导致对比信息有误 2024-11-19 10:22:37 +08:00
zhangshuai
62305f77bc fix: playbook 详情缺少 data 属性 2024-11-19 10:11:43 +08:00
zhangshuai
b54b8a77df fix: playbook 添加 edit 按钮 2024-11-19 09:58:46 +08:00
zhangshuai
84c2559ec1 fix: 修复请求 exec playbook 失败释放 session 问题 2024-11-18 17:00:13 +08:00
zhangshuai
fd9f1cb7e4 fix: ASW-176 job 支持 playbook param 参数 2024-11-18 16:46:28 +08:00
zhangshuai
a83da4f1bb fix: ASW-176 job 支持 playbook param 参数 2024-11-18 16:37:40 +08:00
shizhendong
310d393f8a fix: 调整 application 导入导出接口,asw 不记录 condition.attributeType 属性 2024-11-18 10:50:50 +08:00
shizhendong
55a2dabc0d fix: application 新增|导入 接口默认不再添加 icon.png 文件 2024-11-18 09:39:09 +08:00
shizhendong
b368080eaf fix: diff commit 遇到 merge 时,parentId 取 parent[0] 当作 oldCommit 2024-11-15 18:07:01 +08:00
zhangshuai
19369da30b feat: ASW-175 package修改接口开发 2024-11-15 16:31:59 +08:00
shizhendong
d8aed83e7b fix: 字段调整 application signatures ’negate_option‘-> 'negateOption' 2024-11-15 16:26:01 +08:00
shizhendong
6a4aa58d03 fix: 调整 mr merge 提交信息 2024-11-15 16:08:12 +08:00
shizhendong
489e1b9320 fix: 调整 mr merge 提交信息 2024-11-15 15:52:48 +08:00
shizhendong
7f7d0347ce fix: 处理 mr 过程中分支被删除的情况 2024-11-15 15:34:26 +08:00
shizhendong
f322ce25ff fix: application 导入导出接口路径调整 2024-11-15 11:25:56 +08:00
shizhendong
629c9569da feat: package 添加编辑菜单权限 2024-11-15 11:02:51 +08:00
zhangshuai
1ada41d49e fix: ASW-172 修复 asw 资源文件不返回 path 2024-11-15 09:27:15 +08:00
shizhendong
81d58ae868 fix: ASW-167 解决本地仓库 切换main分支 报错问题 2024-11-14 17:22:51 +08:00
zhangshuai
2db15d0f4e fix: ASW-165 调整文件目录,保持规范 2024-11-14 17:02:03 +08:00
zhangshuai
acacbaec72 fix: ASW-164 每三秒检查 playbook 执行状态,保证线程已创建 2024-11-14 14:52:14 +08:00
zhangshuai
b6f6e17d8e Merge remote-tracking branch 'origin/dev-1.1' into dev-1.1 2024-11-14 14:35:08 +08:00
zhangshuai
d14bc9864a fix: ASW-164 修复 job被取消未释放Environment资源 2024-11-14 14:34:49 +08:00
shizhendong
892a961fe1 feat: ASW-149 signature.json 格式更新,支持保存 object 名称类型
1. application 导入导出 数据源切换为 git
2024-11-14 14:05:14 +08:00
shizhendong
fe55a7fcf8 fix: 解决 meta.json 解析错误导致无法查看 application lastCommit 问题 2024-11-13 18:09:25 +08:00
shizhendong
beb8ccdce6 fix: meta.json 格式错误不影响查看 application 2024-11-13 18:02:54 +08:00
shizhendong
facde0a4e1 fix: ASW-162 解决 mr commits,changes 展示信息有误问题 2024-11-13 17:20:24 +08:00
zhangshuai
cba1251bf3 fix: 修复 pcap 按钮名称错误问题 2024-11-13 14:58:53 +08:00
shizhendong
cd61c898e5 fix: merge 动作中,项目克隆到本地后,设置 config.name 2024-11-13 10:37:39 +08:00
shizhendong
37f22f2817 fix: application mr 合并失败后,抛出异常,保持 mr open 状态 2024-11-13 10:14:08 +08:00
zhangshuai
1da79665be fix: ASW-161 pcap接口更新 2024-11-13 09:53:04 +08:00
shizhendong
304895a81b fix: mr 解决冲突接口 改为: clone,ckeckout,merge,add,commit,push 操作流程 2024-11-12 18:11:30 +08:00
zhangshuai
e6c075f653 fix: pcap 补充 jobId 2024-11-12 15:51:52 +08:00
zhangshuai
e2e0934d34 fix: ASW-146 不展示 system 系统用户 2024-11-12 15:20:27 +08:00
zhangshuai
09946cb5f9 fix: ASW-146 不展示 system 系统用户 2024-11-11 10:35:57 +08:00
zhangshuai
d357daa290 fix: ASW-146 修复 job 执行时未修改 environment 状态和未添加session记录 2024-11-11 09:41:41 +08:00
shizhendong
c7c971d508 fix: application diff 接口调整,action 支持 rename,copy 2024-11-08 15:27:59 +08:00
zhangshuai
e5ed7cf47b fix: ASW-148 断开连接后关闭检测线程 2024-11-08 14:58:48 +08:00
zhangshuai
c83e56fc80 fix: ASW-148 使用虚拟线程检查 session 超时 2024-11-08 12:27:38 +08:00
zhangshuai
37299e7180 fix: ASW-148 novnc 实现超时关闭功能 2024-11-08 12:24:09 +08:00
zhangshuai
de19724c1f fix: ASW-148 novnc 实现超时关闭功能 2024-11-08 11:24:51 +08:00
shizhendong
c7d493f150 fix: application 列表页面按照 commit.createdAt 倒叙排序 2024-11-07 16:57:14 +08:00
shizhendong
336c4ed778 fix: application 详情接口查询 app lastCommit 效率优化测试 2024-11-07 15:05:16 +08:00
zhangshuai
e29ffc62d9 Merge remote-tracking branch 'origin/dev-1.1' into dev-1.1 2024-11-07 14:57:00 +08:00
zhangshuai
19090ae43d fix: 修复 获取job 执行结果重复执行问题 2024-11-07 14:56:42 +08:00
shizhendong
2d3040feec fix: application 列表接口查询 app lastCommit 效率优化测试 2024-11-07 14:46:12 +08:00
zhangshuai
bad7573f5d fix: 修复 获取job 执行结果重复执行问题 2024-11-07 09:29:07 +08:00
zhangshuai
912c0f4ae9 fix: ASW-138 playbook 接口调整,增加 type 属性 2024-11-06 17:29:01 +08:00
shizhendong
5ab545a492 fix: ASW-143 application Merge request 状态调整 2024-11-06 16:05:48 +08:00
shizhendong
549126c4ab fix: ASW-134 application 名称从 git 目录中获取 2024-11-05 15:13:03 +08:00
shizhendong
62925cd6b7 fix: 初始化 meta.json 文件格式调整,json 缩进因子改为 2,和前端保持一致 2024-11-05 14:45:51 +08:00
zhangshuai
f2cc7f8cdb fix: 取消 pcap MD5 校验 2024-11-05 14:09:09 +08:00
shizhendong
caf296f49e fix: ASW-132 application basic.json 更名为 meta.json 2024-11-05 11:00:12 +08:00
zhangshuai
a59b97557a fix: 调整 job 与playbook menu 顺序 2024-11-05 10:31:05 +08:00
zhangshuai
2c817a95d6 Merge remote-tracking branch 'origin/dev-1.1' into dev-1.1 2024-11-05 09:55:46 +08:00
zhangshuai
7e7ef56794 fix: job 日志获取前校验 offset 2024-11-05 09:55:18 +08:00
shizhendong
25992507a6 fix: 按文档调整 git update 接口,create,update 动作必须存在内容 2024-11-04 18:04:58 +08:00
shizhendong
e210cb7cd0 fix: 允许新增空文件 2024-11-04 17:39:03 +08:00
shizhendong
bc26bcabd4 fix: 临时处理 asw 和 opensearch 组件角色对应关系 2024-11-04 17:36:18 +08:00
zhangshuai
9ccd0ecc1f feat: ASW-131 job 详情接口开发 2024-11-04 14:57:20 +08:00
zhangshuai
85de514b1a fix: job 执行结束修改结束时间 2024-11-04 14:20:22 +08:00
zhangshuai
8bcbd80ac9 fix:调整 asw 不响应 null 2024-11-04 10:34:37 +08:00
shizhendong
f4a24d3215 feat: commit diff 接口返回 commit详情信息 2024-11-04 10:09:24 +08:00
shizhendong
c05f37af1a fix: branch commits 接口返回分页参数 2024-11-04 09:43:09 +08:00
zhangshuai
5943b5c514 feat:ASW-129 playbook job 定时任务调整 2024-11-04 09:28:20 +08:00
shizhendong
54bcd63e33 feat: ASW-123 新增 application MR diff,conflict,commit 接口
1. 参考 gitlab 实现 merge request 功能
2024-11-01 18:26:05 +08:00
zhangshuai
7decd03eaa fix: ASW-125 以引用 package 不可删除 2024-10-30 16:48:55 +08:00
zhangshuai
69eef064c2 fix: 调整 package 下载文件名称为 filename 2024-10-30 14:29:59 +08:00
zhangshuai
635c4cdade fix: ASW-119 调整 playbook job 执行成功后 pcap name 2024-10-30 13:55:21 +08:00
zhangshuai
5350efe1e0 fix: 调整 android xapk 文件校验 2024-10-30 09:27:49 +08:00
zhangshuai
b1e4a60c46 fix: ASW-122 调整 playbook 下载文件名称 2024-10-29 17:06:40 +08:00
zhangshuai
dee4829605 fix: ASW-122 修复 package name 解析失败问题 2024-10-29 16:58:04 +08:00
zhangshuai
634ac2b8bb fix: 调整 playbook 上传时,保存文件的名称 2024-10-29 13:53:56 +08:00
zhangshuai
2e4f823211 fix: playbook 添加文件校验 2024-10-29 09:37:17 +08:00
shizhendong
300389ebec fix: merge 失败时返回错误状态码 2024-10-28 17:34:24 +08:00
shizhendong
7eb5a6502e fix: merge 失败时返回错误状态码 2024-10-28 17:33:07 +08:00
zhangshuai
2567a1e367 fix: 添加 playbook 按钮 2024-10-28 15:57:45 +08:00
shizhendong
874c95fa9c feat: ASW-113 新增 application MR 接口 2024-10-28 11:01:29 +08:00
zhangshuai
890f5ccda9 fix: 生成 default dashboard 时,先检查 index-pattern 是否存在,存在即删除 2024-10-25 17:09:38 +08:00
zhangshuai
8deb34d134 fix: 内置 workspace 生成 dashboard 2024-10-25 16:51:43 +08:00
shizhendong
3adafde3fe fix: 临时处理 asw 和 opensearch 组件角色对应关系 2024-10-25 16:08:25 +08:00
zhangshuai
b19582293b fix: 调整 workspace 图表模板,之渲染 indexName dashboardName 2024-10-25 14:24:14 +08:00
zhangshuai
0e48531e21 fix: 调整 workspace dashboard name 2024-10-25 11:29:17 +08:00
zhangshuai
e310d0a9a0 Merge remote-tracking branch 'refs/remotes/origin/dev-1.0' into dev-1.1
# Conflicts:
#	src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceServiceImpl.java
2024-10-25 11:12:28 +08:00
zhangshuai
c3ec839d18 feat: ASW-109 pcap页面增加跳转到opensearch dashboard按钮 2024-10-25 10:54:31 +08:00
zhangshuai
a6a9e5c2e7 feat: ASW-109 pcap页面增加跳转到opensearch dashboard按钮 2024-10-25 10:53:09 +08:00
shizhendong
cb26993d5f fix: 修复 refs/heads/branch 更新错误问题 2024-10-24 17:09:25 +08:00
shizhendong
8a2ece5591 fix: ASW-111 job startTimestamp,endTimeStamp 没有设置值时,返回 null 2024-10-23 15:15:29 +08:00
shizhendong
39a43c3329 Merge remote-tracking branch 'origin/dev-1.0' into dev-1.1
# Conflicts:
#	src/main/java/net/geedge/asw/common/util/Constants.java
2024-10-23 15:05:31 +08:00
zhangshuai
9b92bacd86 fix: 调整 job 定时任务 2024-10-23 14:59:50 +08:00
shizhendong
806ba7a01b feat: job 列表接口新增 params.q 查询参数
1. 模糊查询 package,environment,playbook
2024-10-23 11:29:51 +08:00
shizhendong
b4b3184bbd fix: 统一列表接口获取分页对象方式 2024-10-23 11:15:56 +08:00
shizhendong
b2c5a5aad4 fix: 调整 job 接口参数校验规则 2024-10-23 10:21:50 +08:00
shizhendong
1a3cf55ff2 fix: list application 接口获取 app lastCommit 使用虚拟线程执行 2024-10-22 17:58:38 +08:00
shizhendong
862b8ccd98 fix: git init 默认提交 README.md 文件,解决新仓库中没有分支问题 2024-10-22 17:04:55 +08:00
shizhendong
a7d9c06388 feat: ASW-105 内置 role 数据
1. Owner,maintainer,developer,guest
2024-10-22 15:45:27 +08:00
zhangshuai
402f37b220 fix: 模糊搜索 appName 不区分大小写 2024-10-22 09:58:58 +08:00
zhangshuai
e68a16a500 feat: ASW-100 job 定时调度功能开发 2024-10-21 16:45:45 +08:00
shizhendong
8f451c9375 feat: workspace 创建/删除 同步更新 GIT 仓库
1. workspace 创建时初始化 GIT 仓库
2. workspace 删除时删除 GIT 仓库目录
2024-10-21 16:39:25 +08:00
shizhendong
eb5396437d fix: 调整 workspace 新增/修改接口 visibility 属性校验规则 2024-10-21 16:05:16 +08:00
shizhendong
63fd0ded17 fix: ASW-108 调整 workspace 新增/修改接口 members 属性校验规则 2024-10-21 15:51:56 +08:00
shizhendong
c96e57cb30 Merge remote-tracking branch 'origin/dev-1.0' into dev-1.1
# Conflicts:
#	src/main/resources/db/migration/R__AZ_sys_i18n.sql
2024-10-21 15:42:31 +08:00
shizhendong
d5115bc75c fix: application 列表接口查询 app lastCommit 效率优化测试 2024-10-21 15:35:38 +08:00
zhangshuai
fe0a344ec4 fix: 调整 job 列表 workspaceId 为path参数 2024-10-21 14:37:52 +08:00
shizhendong
4a5783b20e fix: workspace 本地仓库目录修改为:./workspace/{workspace_id} 2024-10-21 14:23:02 +08:00
zhangshuai
661af68cbf fix: 修复 member 修改时 member 被清空 2024-10-18 17:56:06 +08:00
zhangshuai
705f8c7c71 fix:ASW-107 修复 member 修改时 member 被清空 2024-10-18 17:54:28 +08:00
shizhendong
6c6327382f fix: branch 强制删除,解决未合并的分支无法删除问题 2024-10-18 15:23:09 +08:00
shizhendong
d09ab8483c feat: branch 列表属性添加 'default',表示是否为默认分支 2024-10-18 15:11:56 +08:00
shizhendong
cc5788cf0b fix: 调整 applicaiton 接口校验规则 2024-10-17 18:09:15 +08:00
shizhendong
878d9d5d89 fix: application 返回 icon base64 字符串添加 ’data:image/png;base64,‘ 前缀 2024-10-17 15:01:24 +08:00
shizhendong
08f873e8cd fix: application 列表接口支持名称搜索
1. application 列表接口支持名称搜索
2. application 列表接口返回 icon 字段
3. master 改为 main, 初始分支是 main 分支
2024-10-17 14:48:55 +08:00
zhangshuai
524e9f8880 fix:开启job 菜单 2024-10-17 10:02:29 +08:00
shizhendong
dcc02903f7 feat: new application 时,basic.josn signature.json 初始化文件内容
1. 默认文件内容定义在 application.yml 中
2024-10-16 17:43:25 +08:00
shizhendong
938925e76c feat: ASW-88 本地 GIT 仓库管理 application 文件
1. 使用 JGIT 完成对 application 文件的操作
2024-10-16 16:13:56 +08:00
zhangshuai
24d928d7ba fix:ASW-99 调整 xapk 校验 2024-10-15 16:06:27 +08:00
zhangshuai
59e01f71e5 fix:ASW-99 调整 aapt 工具路径 2024-10-15 14:11:12 +08:00
zhangshuai
95e950ecd0 fix:ASW-99 修复 package 上传缺少必要的文件格式校验 2024-10-15 14:06:03 +08:00
shizhendong
dc5f2ebb74 fix: 调整 branch list 接口响应格式 2024-10-11 11:20:33 +08:00
shizhendong
9a85280ca2 feat: ASW-87 新增 application branch 接口 2024-10-11 11:05:51 +08:00
zhangshuai
46d9614603 fix: menu 添加 package_download 按钮 2024-10-11 10:59:27 +08:00
zhangshuai
910027435b fix: pcap 接口调整 2024-10-10 17:44:39 +08:00
zhangshuai
d9964d52ba fix: job 接口调整 2024-10-10 17:15:15 +08:00
zhangshuai
a10e37bbd1 feat: ASW-97 playbook接口开发 2024-10-10 11:30:40 +08:00
shizhendong
dc45c3d62e fix: mvc 响应 文件时,对 filename 进行 url 编码 2024-09-26 10:27:34 +08:00
shizhendong
89c5a94715 feat: ASW-90 新增 pcakage 相关接口 2024-09-26 09:23:08 +08:00
shizhendong
00f6bf65a1 feat: add pcakage menu 2024-09-24 13:57:28 +08:00
shizhendong
c4bdd18974 feat: ASW-86 pcap 解析接口响应解析结果 2024-09-23 15:26:43 +08:00
zhangshuai
d18baeab7c feat:ASW-80 Environment terminal ws代理接口开发 2024-09-20 16:32:38 +08:00
shizhendong
e17feb87b2 feat: ASW-82 新增 user profile 接口 2024-09-19 15:14:37 +08:00
shizhendong
d281ee05d4 feat: ASW-73 新增 environment 状态检查定时任务 2024-09-18 17:38:59 +08:00
shizhendong
2df576ab10 fix: 调整 application export 格式,asw condition 和 tsg object 一对一 2024-09-12 18:08:29 +08:00
zhangshuai
9706eee814 fix: 修复 session已关闭的 vnc连接未断开
1.修复 session已关闭的 vnc连接未断开
2.创建 session 时,检查 env 状态
2024-09-12 14:21:42 +08:00
zhangshuai
ed5dd781b4 fix: mySession 返回 session 信息 2024-09-11 13:41:25 +08:00
zhangshuai
b80b0fdf43 fix: 调整 env list sql 2024-09-11 10:31:00 +08:00
zhangshuai
55d374fa98 fix: 调整 novnc websocket path 2024-09-11 09:39:41 +08:00
zhangshuai
daf362aa0b fix: 调整 env 详情接口 2024-09-10 18:10:56 +08:00
zhangshuai
160c7e9117 fix: 调整 websocket token 认证,使用 sa-token 2024-09-10 18:03:44 +08:00
zhangshuai
b0db257f81 fix: 调整 mgt 修改接口 2024-09-10 16:45:49 +08:00
zhangshuai
6698bc6a66 fix: mgt list 接口 返回 workspaces 参数 2024-09-10 16:18:39 +08:00
zhangshuai
584cce1629 fix: 默认工作空间可以调整 member 2024-09-10 15:33:56 +08:00
zhangshuai
0b08291d7c feat: ASW-61 Environment 管理接口开发 2024-09-10 15:27:52 +08:00
zhangshuai
0c59be48f9 fix: 调整 stop tcpdump 接口 2024-09-10 09:21:53 +08:00
zhangshuai
120265c6ac fix: 调整 stop tcpdump 接口传参 2024-09-09 15:20:30 +08:00
zhangshuai
79ca9b3b02 feat: ASW-62 Environment session 停止捕包接口开发 2024-09-09 14:51:12 +08:00
zhangshuai
3ec0a33d3a feat: ASW-62 Environment session 停止捕包接口开发 2024-09-09 14:47:29 +08:00
shizhendong
b037e46f8e fix: 调整 opensearch-dashboard 展示字段 ts(float),time(keyword,format="yyyy-MM-dd'T'HH:mm:ss.SSSZ") 2024-09-06 18:36:20 +08:00
zhangshuai
df01d6a618 feat: 查询env时 只查询最后一次创建的session 2024-09-06 16:08:01 +08:00
zhangshuai
ce1b53a657 feat: env session 接口 开发 2024-09-06 15:52:17 +08:00
zhangshuai
6f9a738a37 fix: 修复 application signature 请求失败问题 2024-09-06 10:37:51 +08:00
zhangshuai
575dbdab52 fix: attribute 接口返回 用户 userName 信息 2024-09-06 10:33:22 +08:00
zhangshuai
69f0946775 fix: application&role接口返回 用户 userName 信息 2024-09-06 10:31:28 +08:00
zhangshuai
d1f0ec1a58 fix: workspace&suer接口返回 用户 userName 信息 2024-09-06 10:13:07 +08:00
zhangshuai
ac391799de fix: member 接口增加 q 请求参数 2024-09-06 10:07:35 +08:00
zhangshuai
af7635d508 fix: 调整 device 菜单名称 2024-09-06 09:59:49 +08:00
zhangshuai
11e1b85ba5 fix: workspace member 接口添加事务 2024-09-06 09:18:16 +08:00
shizhendong
70c8d98d99 feat: OMPUB-1449 opensearch-dashboard 展示 ts 字段转化为可视化的时间格式
1. 1725518539.484784 -> 2024-09-05 06:42:19.484
2024-09-05 15:56:00 +08:00
zhangshuai
b7e3048e64 fix: 调整同 workspace 下 user 不能重复 2024-09-05 15:21:06 +08:00
shizhendong
5d77111530 fix: 调整 pcap id 生成格式 2024-09-05 14:45:49 +08:00
zhangshuai
6bed4ad795 fix: 调整同 workspace 下 user 不能重复 2024-09-05 14:34:54 +08:00
shizhendong
77c62ca317 feat: mybatis-plus id格式改为带有中划线的 uuid 格式 2024-09-05 14:34:31 +08:00
shizhendong
00a647eb35 fix: 修复 href name 重复验证不准确问题 2024-09-05 14:31:23 +08:00
zhangshuai
bba9a543b0 Merge remote-tracking branch 'origin/dev-1.0' into dev-1.0 2024-09-05 13:54:47 +08:00
zhangshuai
90301ae75e fix: 调整 workspace member 响应结果增加 username 2024-09-05 13:54:19 +08:00
shizhendong
bec7e90774 feat: ASW-59 application 新增接口调整;增加 href 属性 2024-09-05 11:24:05 +08:00
zhangshuai
0f4c12b38e fix: 调整 workspace member 响应结果 2024-09-05 10:44:12 +08:00
zhangshuai
6ec9ba3981 fix: env status 调整 2024-09-04 15:50:30 +08:00
zhangshuai
b6fa061aae fix: env api websocket path 2024-09-04 15:29:40 +08:00
zhangshuai
ee091445a4 fix: 修改 token 检验 2024-09-04 15:17:30 +08:00
zhangshuai
db049e78f4 fix: 调整 文件上传大小限制 2024-09-04 14:39:10 +08:00
zhangshuai
a9251f238c fix: 调整 env api path 路径 2024-09-04 14:28:00 +08:00
shizhendong
64f5a46928 feat: ASW-49 新增 application 导入导出接口
1. 实现 ASW-Controller,TSG application 格式互转
2024-09-04 13:46:58 +08:00
zhangshuai
f4b22ff416 fix:environment_session表 索引设置错误 2024-09-04 11:01:53 +08:00
zhangshuai
fec4beed2d feat:ASW-56 device 我的设备 接口开发
1.调整 device 接口 path
2.调整 device  替换成 environment
2024-09-04 10:59:42 +08:00
zhangshuai
1d0b3c6c9b fix: 调整workspace member 菜单 2024-09-03 17:12:47 +08:00
zhangshuai
d75d109b19 fix: 调整device busy 按钮名称 2024-09-03 17:08:22 +08:00
zhangshuai
8f810462ef fix: 补充workspace member 菜单按钮及默认权限配置 2024-09-03 17:06:11 +08:00
zhangshuai
7a936b6187 feat: ASW-52 device novnc websocket 代理接口开发 2024-09-03 14:12:38 +08:00
zhangshuai
96c9b992af fix: sharkdApi 协议支持可配置 2024-09-02 14:38:57 +08:00
zhangshuai
7137793307 feat: device api 代理接口开发 2024-08-30 16:09:59 +08:00
zhangshuai
df66435492 fix: 修复 application list 返回值错误问题 2024-08-30 13:40:11 +08:00
zhangshuai
a8e496b073 fix: 添加 device 菜单与按钮 2024-08-29 15:25:04 +08:00
shizhendong
737613d2ee feat: ASW-53 opensearch index 创建时配置自定义映射 mapping settings 2024-08-29 10:40:35 +08:00
shizhendong
66214087d5 fix: 调整 stream_id 获取方式,改为 zeek 解析获取 stream_id 字段,之后按协议补充 tcp_stream,udp_stream 2024-08-29 10:39:17 +08:00
zhangshuai
9e206505e5 fix: 修复用户详情接口请求失败问题 2024-08-28 11:26:07 +08:00
zhangshuai
09622b5ed3 feat: role 接口开发 2024-08-28 09:14:17 +08:00
shizhendong
16fc3a4bc2 feat: ASW-46 新增 device 接口 2024-08-27 15:58:41 +08:00
shizhendong
9051cd3fac fix: pcap 保存时 name=源文件名称,文件存储时 name={pcap_id}+源文件后缀 2024-08-27 14:15:20 +08:00
zhangshuai
5bae5dfdd3 fix: 调整 pcap webshark 接口 2024-08-27 10:59:59 +08:00
shizhendong
bbbe71ae33 fix: 调用 pcap-common 接口时添加 url,id 参数项 2024-08-26 17:58:36 +08:00
zhangshuai
fda9a1b2b1 fix: application 查询时不返回 attachment path 2024-08-26 17:07:54 +08:00
zhangshuai
46e2b0632f fix: application 接口 添加 properties 属性 2024-08-26 16:58:53 +08:00
shizhendong
8e835e4ea3 fix: 单独保存 pcap common 文件,不对源文件进行覆盖
1. 单独保存 pcap common 文件,不对源文件进行覆盖
2. 删除解析结果时,重置 summary 内容
2024-08-26 15:02:20 +08:00
zhangshuai
1db74870e0 feat:attachment 下载接口开发 2024-08-26 14:41:28 +08:00
zhangshuai
3d95329f01 feat:ASW-48 attribute接口开发 2024-08-26 13:45:52 +08:00
zhangshuai
7c99ed06bb fix: note 校验为非必填 2024-08-26 11:34:56 +08:00
shizhendong
11396c6dd1 feat: pcap 文件增加 comment 信息 2024-08-26 10:08:31 +08:00
zhangshuai
a1f2bede19 feat: ASW-40 application接口开发 2024-08-23 10:02:04 +08:00
zhangshuai
13a9d8f674 fix: 调整 Permissions 接口
1.Permissions 只查询已分配的 workspace
2.内置 readonly 角色
2024-08-22 17:44:01 +08:00
zhangshuai
756d8c5cd1 feat: ASW-47 user 接口开发 2024-08-22 16:57:18 +08:00
zhangshuai
6ea9ad9413 fix: 调整 pcap explore 接口 2024-08-21 14:42:10 +08:00
shizhendong
6bb44b101d feat: pcap session stream_url 添加 navi 前缀 2024-08-19 10:35:21 +08:00
zhangshuai
bf2ee90390 fix: 补充 RCode 国际化 2024-08-19 09:30:35 +08:00
zhangshuai
c57ade402f fix: pcap webshark upload error 响应错误码 2024-08-15 17:54:17 +08:00
zhangshuai
f14763512e fix: pcap webshark upload error 响应错误码 2024-08-15 17:54:07 +08:00
zhangshuai
b480734c94 feat: pcap 增加 unparse2session 接口 2024-08-15 17:30:13 +08:00
zhangshuai
1c35979b24 fix: pcap 响应 summary 内容 2024-08-15 16:55:16 +08:00
zhangshuai
68ccb87c76 fix:调整 webshark 接口返回值 2024-08-15 16:18:28 +08:00
zhangshuai
a98566f5d5 fix:调整 webshark 默认端口 2024-08-15 15:12:30 +08:00
zhangshuai
634166c4b3 feat: ASW-38 webshark 访问接口开发 2024-08-15 15:09:43 +08:00
shizhendong
ecb57f6c6c feat: session 增加 stream_id,stream_url
1. pcap 新增 summary 统计信息
2024-08-13 14:08:33 +08:00
zhangshuai
58b38fbe91 fix: 调整 application developer 为 provider 2024-08-12 15:22:55 +08:00
zhangshuai
582856c066 fix: 取消展示更新记录链接 2024-08-12 15:07:35 +08:00
shizhendong
76e52d91e9 fix: 调整 application explore 接口相应内容 2024-08-12 14:07:09 +08:00
zhangshuai
f3d048c240 fix: application 添加 packageName website developer 属性 2024-08-09 17:21:29 +08:00
184 changed files with 13042 additions and 2215 deletions

View File

@@ -61,8 +61,9 @@ dev_build:
- export FILE_NAME=$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA.tar.gz
- mvn clean install -Dmaven.test.skip=true
- cd ./target
- "git log -100 --pretty=format:'%ad : %s' > git-log.html"
- tar -zcvf $FILE_NAME asw-controller.jar git-log.html
# - "git log -100 --pretty=format:'%ad : %s' > git-log.html"
# - tar -zcvf $FILE_NAME asw-controller.jar git-log.html
- tar -zcvf $FILE_NAME asw-controller.jar
- md5sum $FILE_NAME > $CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-latest.tar.gz.md5sum.txt
# 将 文件 上传到 minio
- mc alias set asw $MINIO_HOST $MINIO_USER $MINIO_PWD

27
pom.xml
View File

@@ -179,6 +179,33 @@
<version>2.12.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit -->
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>7.0.0.202409031743-r</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit.archive -->
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.archive</artifactId>
<version>7.0.0.202409031743-r</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j -->
<dependency>
<groupId>net.lingala.zip4j</groupId>
<artifactId>zip4j</artifactId>
<version>2.11.5</version>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,33 @@
package net.geedge.asw.common.config;
import net.geedge.asw.common.util.T;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean;
import java.util.Properties;
@Configuration
public class FreeMarkerConfig {
@Value("${asw.template.path:static}")
private String templatePath;
@Bean
public FreeMarkerConfigurationFactoryBean factoryBean() {
FreeMarkerConfigurationFactoryBean freeMarkerConfigurationFactoryBean = new FreeMarkerConfigurationFactoryBean();
// 设置 FreeMarker 模板位置
boolean exist = T.FileUtil.exist(templatePath);
templatePath = exist ? templatePath : "classpath:" + templatePath;
freeMarkerConfigurationFactoryBean.setTemplateLoaderPath(templatePath);
// 其他配置
Properties settings = new Properties();
settings.setProperty("default_encoding", "utf-8");
settings.setProperty("number_format", "0.##");
freeMarkerConfigurationFactoryBean.setFreemarkerSettings(settings);
return freeMarkerConfigurationFactoryBean;
}
}

View File

@@ -1,11 +1,15 @@
package net.geedge.asw.common.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
@Configuration(proxyBeanMethods = false)
public class MybatisPlusConfig {
@@ -19,4 +23,29 @@ public class MybatisPlusConfig {
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MARIADB));//如果配置多个插件,切记分页最后添加
return interceptor;
}
@Bean
public IdentifierGenerator identifierGenerator() {
return new IdentifierGenerator() {
@Override
public Number nextId(Object entity) {
return DefaultIdentifierGenerator.getInstance().nextId(entity);
}
/**
* 自定义 UUID 生成格式带有中划线示例格式c2ce91d1-d1f4-4629-aae4-414df36d87ca
*
* @param entity
* @return
*/
@Override
public String nextUUID(Object entity) {
ThreadLocalRandom random = ThreadLocalRandom.current();
return (new UUID(random.nextLong(), random.nextLong())).toString();
}
};
}
}

View File

@@ -0,0 +1,101 @@
package net.geedge.asw.common.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import java.util.HashMap;
import java.util.Map;
/**
* 查询参数
*/
public class Query {
private Class clz;
public Class<? extends Object> getClz() {
return clz;
}
public void setClz(Class clz) {
this.clz = clz;
}
public Query(Class clz) {
this.clz = clz;
}
public Page getPage(Map<String, Object> params) {
return this.getPage(params, null, false);
}
public Page getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
//分页参数
long curPage = 1;
long limit = Constants.PAGESIZE;
if(params.get(Constants.PAGE) != null){
curPage = Long.parseLong((String)params.get(Constants.PAGE));
}
if(params.get(Constants.LIMIT) != null){
limit = Long.parseLong((String)params.get(Constants.LIMIT));
if(limit == -1){
limit = Long.MAX_VALUE;
curPage = 0;
}
}
//分页对象
Page page = new Page(curPage, limit);
//分页参数
params.put(Constants.PAGE, page);
//排序字段 orderBy=id
//防止SQL注入因为sidx、order是通过拼接SQL实现排序的会有SQL注入风险
String orderField = SQLFilter.sqlInject((String)params.get(Constants.ORDER));
if (StrUtil.isNotEmpty(orderField)) {
boolean matcheFlag = orderField.trim().matches("-?[a-zA-Z_.-]+");
if (!matcheFlag) {
throw new ASWException(RCode.ERROR);
}
// 获取表名
Class<?> clz = this.getClz();
String tableName = "";
if (clz != null) {
TableName table = this.getClz().getAnnotation(TableName.class);
tableName = table.value();
}
// 通过表名获取排序字段映射
Map<String, String> columnAliasMap = Constants.TABLE_NAME_ORDER_FIELD_MAPPING.get(tableName);
columnAliasMap = T.MapUtil.isEmpty(columnAliasMap) ? new HashMap<>():columnAliasMap;
if (orderField.startsWith("-")) {
orderField = orderField.substring(1, orderField.length());
orderField = columnAliasMap.get(orderField) != null ? columnAliasMap.get(orderField) : orderField;
return page.addOrder(OrderItem.desc(orderField));
} else {
orderField = columnAliasMap.get(orderField) != null ? columnAliasMap.get(orderField) : orderField;
return page.addOrder(OrderItem.asc(orderField));
}
}
// 默认排序
if (StrUtil.isNotEmpty(defaultOrderField)) {
if (isAsc) {
return page.addOrder(OrderItem.asc(defaultOrderField));
} else {
return page.addOrder(OrderItem.desc(defaultOrderField));
}
}
return page;
}
}

View File

@@ -0,0 +1,49 @@
/**
*
*/
package net.geedge.asw.common.config;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import java.util.regex.Pattern;
/**
* SQL过滤
*
* @author Mark sunlightcs@gmail.com
*/
public class SQLFilter {
private static String reg = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|(\\b(select|update|union|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)";
private static Pattern sqlPattern = Pattern.compile(reg, Pattern.CASE_INSENSITIVE);
/**
* SQL注入过滤
* @param str 待验证的字符串
*/
public static String sqlInject(String str) {
if (T.StrUtil.isBlank(str)) {
return null;
}
//转换成小写
String str1 = str.toLowerCase();
String s = "";
if (str1.startsWith("-")) {
s = str1.substring(1);
} else {
s = str1;
}
if (sqlPattern.matcher(s).matches()) {
throw new ASWException(RCode.ERROR);
}
return str;
}
}

View File

@@ -0,0 +1,109 @@
package net.geedge.asw.common.config;
import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import net.geedge.asw.module.job.entity.JobEntity;
import net.geedge.asw.module.job.util.JobQueueManager;
import net.geedge.asw.module.job.service.IJobService;
import net.geedge.asw.module.job.util.JobConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* setup初始化操作
*/
@Component
public class SetupRunner implements CommandLineRunner{
private static final Log log = Log.get();
@Autowired
private IJobService jobService;
@Autowired
private JobQueueManager jobQueueManager;
@Autowired
private IEnvironmentService environmentService;
@Autowired
private IEnvironmentSessionService environmentSessionService;
@Override
public void run(String... args) throws Exception {
log.info("Setup inited");
List<JobEntity> pendingJobs = jobService.list(new LambdaQueryWrapper<JobEntity>().eq(JobEntity::getStatus, JobConstant.JobStatus.PENDING.getValue()));
pendingJobs.forEach(jobQueueManager::addJob);
log.info("[SetupRunner] [init pending job to JobQueueManager]");
log.info("[SetupRunner] [begin interrupted running job]");
List<JobEntity> runningJobs = jobService.list(new LambdaQueryWrapper<JobEntity>().eq(JobEntity::getStatus, JobConstant.JobStatus.RUNNING.getValue()));
for (JobEntity runningJob : runningJobs) {
String id = runningJob.getId();
EnvironmentEntity environment = environmentService.getById(runningJob.getEnvId());
JSONObject paramJSONObject = environment.getParamJSONObject();
String url = paramJSONObject.getStr("url");
String token = paramJSONObject.getStr("token");
HttpRequest requestStatus = T.HttpUtil.createGet(String.format("%s/api/v1/env/playbook/%s", url, runningJob.getId()));
requestStatus.header("Authorization", token);
HttpResponse response = requestStatus.execute();
if (response.isOk()){
String body = response.body();
JSONObject result = T.JSONUtil.toBean(body, JSONObject.class);
JSONObject data = result.getJSONObject("data");
String status = data.getStr("status");
if (JobConstant.JobStatus.RUNNING.getValue().equals(status)){
HttpRequest request = T.HttpUtil.createRequest(Method.DELETE, String.format("%s/api/v1/env/playbook/%s", url, runningJob.getId()));
request.header("Authorization", token);
request.execute();
}
}
Thread runningThread = Constants.RUNNING_JOB_THREAD.get(id);
if (runningThread != null) {
runningThread.interrupt();
}
Thread resultThread = Constants.RESULT_JOB_THREAD.get(id);
if (resultThread != null) {
resultThread.interrupt();
}
EnvironmentSessionEntity session = environmentSessionService.getOne(new LambdaQueryWrapper<EnvironmentSessionEntity>()
.eq(EnvironmentSessionEntity::getJobId, id)
.eq(EnvironmentSessionEntity::getStatus, 1));
if (T.ObjectUtil.isNotEmpty(session)) {
environmentService.removeSession(session.getId());
}
T.FileUtil.appendString("Job execution interrupted.", FileUtil.file(runningJob.getLogPath()), "UTF-8");
// update state
jobService.update(new LambdaUpdateWrapper<JobEntity>()
.eq(JobEntity::getId, id)
.set(JobEntity::getStatus, "failed")
);
}
log.info("[SetupRunner] [interrupted running job end!]");
}
}

View File

@@ -0,0 +1,17 @@
package net.geedge.asw.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration
public class ThreadPoolConfig {
@Bean
public ExecutorService virtualThreadExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
}

View File

@@ -0,0 +1,105 @@
package net.geedge.asw.common.config.job;
import cn.hutool.log.Log;
import jakarta.annotation.PostConstruct;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.environment.job.JobEnvironmentStatusChecker;
import net.geedge.asw.module.job.job.JobPlaybookExecResultChecker;
import net.geedge.asw.module.job.job.JobPlaybookExecutor;
import net.geedge.asw.module.sys.service.ISysConfigService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import java.util.TimeZone;
@Configuration
public class JobConfig {
private static final Log log = Log.get();
private static final String JOB_NAME_PREFIX = "ASW_JOB";
private static final String JOB_DEFAULT_GROUP = "SYSTEM";
/**
* get job key
* job_name=ASW_JOB_{name}
* group_name=SYSTEM
*/
private static JobKey getJobKey(String name) {
String jobName = T.StrUtil.concat(true, JOB_NAME_PREFIX, "_", name);
return new JobKey(jobName, JOB_DEFAULT_GROUP);
}
@Autowired
private Scheduler scheduler;
@Autowired
private Environment environment;
@Autowired
private ISysConfigService sysConfigService;
@Bean
public JobDetail JobEnvironmentStatusChecker() {
return JobBuilder.newJob(JobEnvironmentStatusChecker.class)
.withIdentity(getJobKey(JobEnvironmentStatusChecker.class.getSimpleName()))
.storeDurably()
.build();
}
@Bean
public JobDetail JobPlaybookExecutor() {
return JobBuilder.newJob(JobPlaybookExecutor.class)
.withIdentity(getJobKey(JobPlaybookExecutor.class.getSimpleName()))
.storeDurably()
.build();
}
@Bean
public JobDetail JobPlaybookExecResultChecker() {
return JobBuilder.newJob(JobPlaybookExecResultChecker.class)
.withIdentity(getJobKey(JobPlaybookExecResultChecker.class.getSimpleName()))
.storeDurably()
.build();
}
@PostConstruct
public void init() throws SchedulerException {
// JobEnvironmentStatusChecker
createCronScheduleJob(JobEnvironmentStatusChecker(), environment.getProperty("asw.cron.JobEnvironmentStatusChecker", "0 0/1 * * * ? *"));
// JobPlaybookExecutor
createCronScheduleJob(JobPlaybookExecutor(), environment.getProperty("asw.cron.JobPlaybookExecutor", "0/30 * * * * ?"));
// JobPlaybookExecResultChecker
createCronScheduleJob(JobPlaybookExecResultChecker(), environment.getProperty("asw.cron.JobPlaybookExecResultChecker", "0/10 * * * * ?"));
}
/**
* create cron schedule job
* 先删后增
*/
private void createCronScheduleJob(JobDetail jobDetail, String cronExpression) throws SchedulerException {
JobKey key = jobDetail.getKey();
boolean jobExists = scheduler.checkExists(key);
if (log.isDebugEnabled()) {
log.debug("[createCronScheduleJob] [key: {}] [exists: {}]", key.toString(), jobExists);
}
if (jobExists) {
scheduler.deleteJob(key);
log.debug("[createCronScheduleJob] [key: {}] [deleted]", key.toString());
}
String timezone = sysConfigService.getValue("timezone");
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).inTimeZone(TimeZone.getTimeZone(timezone));
CronTrigger cronTrigger = TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withSchedule(cronScheduleBuilder)
.build();
scheduler.scheduleJob(jobDetail, cronTrigger);
}
}

View File

@@ -0,0 +1,204 @@
package net.geedge.asw.common.config.websocket;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletionStage;
@Component
public class EnvironmentNovncWebSocketHandler extends TextWebSocketHandler {
private static final Log log = Log.get();
/**
* env id
*/
private String envId;
/**
* session
*/
private String sessionId;
/**
* user id
*/
private String userId;
private long lastReceivedTime;
private Integer sessionTimeout;
private IEnvironmentService environmentService;
private IEnvironmentSessionService environmentSessionService;
public EnvironmentNovncWebSocketHandler() {
}
public EnvironmentNovncWebSocketHandler(IEnvironmentService deviceService, IEnvironmentSessionService environmentSessionService, Integer sessionTimeout) {
this.environmentService = deviceService;
this.environmentSessionService = environmentSessionService;
this.sessionTimeout = sessionTimeout;
}
private void initFieldVal(WebSocketSession session) {
this.envId = (String) session.getAttributes().get("envId");
this.sessionId = (String) session.getAttributes().get("sessionId");
this.userId = (String) session.getAttributes().get("userId");
Constants.ENV_NOVNC_WEBSOCKET_SESSION.put(sessionId, session);
lastReceivedTime = System.currentTimeMillis(); // 初始化接收时间
Thread checkSessionTimeout = startConnectionMonitor(session);
session.getAttributes().put("checkSessionTimeoutThread", checkSessionTimeout);
}
private Thread startConnectionMonitor(WebSocketSession session) {
Thread thread = Thread.ofVirtual().start(() -> {
while (true) {
try {
if (System.currentTimeMillis() - lastReceivedTime > sessionTimeout) {
if (session.isOpen()) {
log.info("current no connection info, clean no real use connection finshed, sessionId: {}", sessionId);
// update session status
environmentSessionService.update(new LambdaUpdateWrapper<EnvironmentSessionEntity>()
.set(EnvironmentSessionEntity::getStatus, 2)
.eq(EnvironmentSessionEntity::getId, sessionId));
session.close(CloseStatus.NORMAL.withReason("current no connection info, clean no real use connection finshed."));
break;
}
}
Thread.sleep(300000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
} catch (IOException e) {
log.error(e, "Error clean no real use connection");
}
}
});
return thread;
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
this.initFieldVal(session);
// token
if (T.StrUtil.isEmpty(userId)) {
log.warn("Websocket token authentication failed");
session.close(CloseStatus.NORMAL.withReason("Websocket token authentication failed"));
return;
}
// env session
EnvironmentSessionEntity environmentSession = environmentSessionService.getOne(new LambdaQueryWrapper<EnvironmentSessionEntity>().eq(EnvironmentSessionEntity::getId, sessionId).eq(EnvironmentSessionEntity::getStatus, 1));
if (environmentSession == null) {
log.warn("environment session does not exist. session id: {}", sessionId);
session.close(CloseStatus.NORMAL.withReason("Environment session does not exist"));
return;
}
log.info("WebSocket connectioned. after connection established open environment begin... environment id: {}", envId);
EnvironmentEntity deviceEntity = environmentService.queryInfo(envId);
JSONObject paramJSONObject = deviceEntity.getParamJSONObject();
String urlStr = String.format("%s%s", paramJSONObject.getStr("url"), Constants.ENV_NOVNC_WEBSOCKET_PATH);
urlStr = urlStr.replace("http", "ws");
WebSocket webSocket = null;
try {
HttpClient client = HttpClient.newHttpClient();
webSocket = client.newWebSocketBuilder()
.buildAsync(URI.create(urlStr), new WebSocketListener(session))
.get();
} catch (Exception e) {
log.error(e, "Environment WebSocket connectioned. after connection established open environment error. session id: {}", sessionId);
if (ObjectUtil.isNotNull(webSocket)) {
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "Normal closure");
}
if (session != null) {
session.close(CloseStatus.NORMAL.withReason("Environment WebSocket connectioned. after connection established open environment error!"));
IoUtil.close(session);
Constants.ENV_NOVNC_WEBSOCKET_SESSION.remove(sessionId);
}
}
log.info("[afterConnectionEstablished] [environment server: {}]", T.JSONUtil.toJsonStr(paramJSONObject));
session.getAttributes().put("envWebsocket", webSocket);
}
// WebSocket 监听器实现
private static class WebSocketListener implements WebSocket.Listener {
private WebSocketSession session;
public WebSocketListener(WebSocketSession session) {
this.session = session;
}
@Override
public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer data, boolean last) {
try {
// env -> asw
session.sendMessage(new BinaryMessage(data, true));
} catch (IOException e) {
throw new RuntimeException(e);
}
return WebSocket.Listener.super.onBinary(webSocket, data, last);
}
@Override
public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
log.info("Environment webSocket connection closed, Status: " + statusCode + ", Reason: " + reason);
return WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
}
}
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
try {
// asw -> env
WebSocket envSocket = (WebSocket) session.getAttributes().get("envWebsocket");
if (envSocket != null) {
envSocket.sendBinary(message.getPayload(), true);
lastReceivedTime = System.currentTimeMillis(); // 更新接收时间
}
} catch (Exception e) {
log.error(e, "[handleBinaryMessage] [error]");
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
log.info("[afterConnectionClosed] [WebSocket connection closed] [websocket uri: {}]", session.getUri());
WebSocket envWebsocket = (WebSocket) session.getAttributes().get("envWebsocket");
if (envWebsocket != null) {
envWebsocket.sendClose(WebSocket.NORMAL_CLOSURE, "Normal closure");
}
Thread checkSessionTimeoutThread = (Thread) session.getAttributes().get("checkSessionTimeoutThread");
if (checkSessionTimeoutThread != null) {
checkSessionTimeoutThread.interrupt();
}
Constants.ENV_NOVNC_WEBSOCKET_SESSION.remove(sessionId);
super.afterConnectionClosed(session, status);
}
}

View File

@@ -0,0 +1,160 @@
package net.geedge.asw.common.config.websocket;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.util.concurrent.CompletionStage;
@Component
public class EnvironmentTerminalWebSocketHandler extends TextWebSocketHandler {
private static final Log log = Log.get();
/**
* env id
*/
private String envId;
/**
* session
*/
private String sessionId;
/**
* user id
*/
private String userId;
private IEnvironmentService environmentService;
private IEnvironmentSessionService environmentSessionService;
public EnvironmentTerminalWebSocketHandler(IEnvironmentService environmentService, IEnvironmentSessionService environmentSessionService) {
this.environmentService = environmentService;
this.environmentSessionService = environmentSessionService;
}
private void initFieldVal(WebSocketSession session) {
this.envId = (String) session.getAttributes().get("envId");
this.sessionId = (String) session.getAttributes().get("sessionId");
this.userId = (String) session.getAttributes().get("userId");
Constants.ENV_TERMINAL_WEBSOCKET_SESSION.put(sessionId, session);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
this.initFieldVal(session);
// token
if (T.StrUtil.isEmpty(userId)) {
log.warn("Websocket token authentication failed");
session.close(CloseStatus.NORMAL.withReason("Websocket token authentication failed"));
return;
}
// env session
EnvironmentSessionEntity environmentSession = environmentSessionService.getOne(new LambdaQueryWrapper<EnvironmentSessionEntity>().eq(EnvironmentSessionEntity::getId, sessionId).eq(EnvironmentSessionEntity::getStatus, 1));
if (environmentSession == null) {
log.warn("environment session does not exist. session id: {}", sessionId);
session.close(CloseStatus.NORMAL.withReason("Environment session does not exist"));
return;
}
log.info("WebSocket connectioned. after connection established open environment terminal begin... environment id: {}", envId);
EnvironmentEntity deviceEntity = environmentService.queryInfo(envId);
JSONObject paramJSONObject = deviceEntity.getParamJSONObject();
String urlStr = String.format("%s%s", paramJSONObject.getStr("url"), Constants.ENV_TERMINAL_WEBSOCKET_PATH);
urlStr = urlStr.replace("http", "ws");
WebSocket webSocket = null;
try {
HttpClient client = HttpClient.newHttpClient();
webSocket = client.newWebSocketBuilder()
.buildAsync(URI.create(urlStr), new WebSocketListener(session))
.get();
} catch (Exception e) {
log.error(e, "Environment terminal webSocket connectioned. after connection established open environment terminal error. session id: {}", sessionId);
if (ObjectUtil.isNotNull(webSocket)) {
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "Normal closure");
}
if (ObjectUtil.isNotNull(session)) {
session.close(CloseStatus.NORMAL.withReason("Environment terminal webSocket connectioned. after connection established open environment terminal error!"));
IoUtil.close(session);
Constants.ENV_TERMINAL_WEBSOCKET_SESSION.remove(sessionId);
}
}
log.info("[afterConnectionEstablished] [environment terminal url: {}]", urlStr);
session.getAttributes().put("terminalWebsocket", webSocket);
}
// WebSocket 监听器实现
private static class WebSocketListener implements WebSocket.Listener {
private WebSocketSession session;
public WebSocketListener(WebSocketSession session) {
this.session = session;
}
@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence message, boolean last) {
try {
// env -> asw
session.sendMessage(new TextMessage(message));
} catch (IOException e) {
throw new RuntimeException(e);
}
return WebSocket.Listener.super.onText(webSocket, message, last);
}
@Override
public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
log.info("Environment terminal webSocket connection closed, Status: " + statusCode + ", Reason: " + reason);
return WebSocket.Listener.super.onClose(webSocket, statusCode, reason);
}
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
WebSocket terminalWebsocket = (WebSocket) session.getAttributes().get("terminalWebsocket");
try {
if (terminalWebsocket != null) {
terminalWebsocket.sendText(message.getPayload(), true);
}
} catch (Exception e) {
}
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
log.info("[afterConnectionClosed] [Terminal webSocket connection closed] [uri: {}]", session.getUri());
WebSocket envWebsocket = (WebSocket) session.getAttributes().get("terminalWebsocket");
if (envWebsocket != null) {
envWebsocket.sendClose(WebSocket.NORMAL_CLOSURE, "Normal closure");
}
Constants.ENV_TERMINAL_WEBSOCKET_SESSION.remove(sessionId);
super.afterConnectionClosed(session, status);
}
}

View File

@@ -0,0 +1,53 @@
package net.geedge.asw.common.config.websocket;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.T;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
public class EnvironmentWebSocketInterceptor extends HttpSessionHandshakeInterceptor {
private static final Log log = Log.get();
private String regex = "^/api/v1/env/([^/]+)/session/([^/]+)/(novnc|terminal)$";
@Override
public synchronized boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
String servletPath = servletRequest.getServletRequest().getServletPath();
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(servletPath);
if (matcher.find()) {
attributes.put("envId", matcher.group(1));
attributes.put("sessionId", matcher.group(2));
}
try {
String token = servletRequest.getServletRequest().getParameter("token");
StpUtil.setTokenValue(token);
String userId = StpUtil.getLoginIdAsString();
attributes.put("userId", userId);
}catch (Exception e){
log.error("Websocket token authentication failed");
attributes.put("userId", T.StrUtil.EMPTY);
}
}
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
super.afterHandshake(request, response, wsHandler, exception);
}
}

View File

@@ -0,0 +1,36 @@
package net.geedge.asw.common.config.websocket;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Value("${session.timeout:1800000}") // 默认为 30 分钟
private Integer sessionTimeout;
@Autowired
private IEnvironmentService deviceService;
@Autowired
private IEnvironmentSessionService environmentSessionService;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new EnvironmentNovncWebSocketHandler(deviceService, environmentSessionService, sessionTimeout), "/api/v1/env/{envId}/session/{sessionId}/novnc")
.addInterceptors(new EnvironmentWebSocketInterceptor())
.setAllowedOrigins("*");
registry.addHandler(new EnvironmentTerminalWebSocketHandler(deviceService, environmentSessionService), "/api/v1/env/{envId}/session/{sessionId}/terminal")
.addInterceptors(new EnvironmentWebSocketInterceptor())
.setAllowedOrigins("*");
}
}

View File

@@ -1,7 +1,12 @@
package net.geedge.asw.common.util;
import org.springframework.web.socket.WebSocketSession;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Constants {
@@ -15,6 +20,7 @@ public class Constants {
*/
public static final String TEMP_PATH = System.getProperty("user.dir") + File.separator + "tmp";
/**
* 国际化语言列表
*/
@@ -26,4 +32,142 @@ public class Constants {
*/
public static final List<String> VISIBILITY_LIST = T.ListUtil.of("public", "private");
/**
* 当前页码
*/
public static final String PAGE = "current";
/**
* 每页显示记录数
*/
public static final String LIMIT = "size";
/**
* 每页显示条数
*/
public static final long PAGESIZE = 20;
/**
* 排序方式
*/
public static final String ORDER = "orderBy";
/**
* 表名 和 排序字段对应关系 KEY: tablename
*/
public static final Map<String, Map<String, String>> TABLE_NAME_ORDER_FIELD_MAPPING = T.MapUtil.newHashMap();
static {
Map<String, String> applicationOrderFieldMap = new HashMap<>();
TABLE_NAME_ORDER_FIELD_MAPPING.put("application", applicationOrderFieldMap);
}
/**
* env api path prefix
*/
public static final String ENV_API_PREFIX = "/api/v1/env";
public static final String AUTH_TOKEN_CODE = "Authorization";
public static final Map<String, String> CORS_HEADER = T.MapUtil
.builder("Access-Control-Allow-Credentials", "true")
.put("Access-Control-Allow-Methods", "GET,PUT,POST,PATCH,DELETE,HEAD,OPTIONS")
.put("Access-Control-Max-Age", "18000").put("Access-Control-Allow-Origin", "*").build();
/**
* env api novnc websocket path
*/
public static final String ENV_NOVNC_WEBSOCKET_PATH = "/api/v1/env/novnc";
public static final String ENV_TERMINAL_WEBSOCKET_PATH = "/api/v1/env/terminal";
/**
* env api stop tcpdump path
*/
public static final String ENV_API_TCPDUMP_PATH = "/api/v1/env/pcap";
/**
* env api status path
*/
public static final String ENV_API_STATUS_PATH = "/api/v1/env/status";
/**
* novnc websocket 连接信息对应的 env session id 用以进行主动断开服务器连接功能
*/
public static final Map<String, WebSocketSession> ENV_NOVNC_WEBSOCKET_SESSION = T.MapUtil.newHashMap();
/**
* terminal websocket 连接信息对应的 env session id 用以进行主动断开服务器连接功能
*/
public static final Map<String, WebSocketSession> ENV_TERMINAL_WEBSOCKET_SESSION = T.MapUtil.newHashMap();
public static final ConcurrentHashMap<String, Thread> RUNNING_JOB_THREAD = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<String, Thread> RESULT_JOB_THREAD = new ConcurrentHashMap<>();
/**
* Android package type
*/
public static final List<String> ANDROID_PACKAGE_TYPE_LIST = T.ListUtil.of("xapk", "apk");
/**
* job cfg type
*/
public static final List<String> JOB_CFG_TYPE_LIST= T.ListUtil.of("asap", "cron");
/**
* job cfg status
*/
public static final List<String> JOB_CFG_STATUS_LIST= T.ListUtil.of("enabled", "disabled");
public static final String EMPTY_FILE_MD5 = "d41d8cd98f00b204e9800998ecf8427e";
/**
* 系统内置角色
*/
public static enum BuiltInRoleEnum {
OWNER("owner"),
MAINTAINER("maintainer"),
DEVELOPER("developer"),
GUEST("guest");
private String id;
BuiltInRoleEnum(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
/**
* 文件类型
*/
public static enum FileTypeEnum {
PCAP("pcap"),
PACKAGE("package"),
PLAYBOOK("playbook"),
JOB("job"),
RELEASE("release");
private String type;
FileTypeEnum(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
}

View File

@@ -0,0 +1,14 @@
package net.geedge.asw.common.util;
import java.io.File;
public class FileResourceUtil {
private static String ROOT_PATH = T.WebPathUtil.getRootPath();
public static File createFile(String resourcePath, String workspaceId, String type, String id, String name) {
String sub = T.StrUtil.sub(id, 0, 2);
File file = T.FileUtil.file(ROOT_PATH, resourcePath, workspaceId, type, sub, id, name);
return file;
}
}

View File

@@ -0,0 +1,21 @@
/**
*
*
*
*/
package net.geedge.asw.common.util;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}

View File

@@ -0,0 +1,82 @@
/**
*
*
*
*/
package net.geedge.asw.common.util;
import cn.hutool.log.Log;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
/**
* IP地址
*
* @author Mark sunlightcs@gmail.com
*/
public class IPUtils {
private static Log logger = Log.get();
/**
* 获取IP地址
*
* 使用Nginx等反向代理软件 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话X-Forwarded-For的值并不止一个而是一串IP地址X-Forwarded-For中第一个非unknown的有效IP字符串则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
logger.error("IPUtils ERROR ",e);
}
// //使用代理则获取第一个IP地址
// if(StringUtils.isEmpty(ip) && ip.length() > 15) {
// if(ip.indexOf(",") > 0) {
// ip = ip.substring(0, ip.indexOf(","));
// }
// }
return ip;
}
/**
* 功能判断一个IP是不是在一个网段下的
* 格式isInRange("192.168.8.3", "192.168.9.10/22");
*/
public static boolean isInRange(String ip, String cidr) {
String[] ips = ip.split("\\.");
int ipAddr = (Integer.parseInt(ips[0]) << 24)
| (Integer.parseInt(ips[1]) << 16)
| (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]);
int type = Integer.parseInt(cidr.replaceAll(".*/", ""));
int mask = 0xFFFFFFFF << (32 - type);
String cidrIp = cidr.replaceAll("/.*", "");
String[] cidrIps = cidrIp.split("\\.");
int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24)
| (Integer.parseInt(cidrIps[1]) << 16)
| (Integer.parseInt(cidrIps[2]) << 8)
| Integer.parseInt(cidrIps[3]);
return (ipAddr & mask) == (cidrIpAddr & mask);
}
}

View File

@@ -22,6 +22,14 @@ public enum RCode {
ROLE_ID_CANNOT_EMPTY(100010, "role id cannot be empty"),// 权限 ID 不能为空
USER_NOT_EXIST(100011, "user does not exist"),
ROLE_NOT_EXIST(100012, "role does not exist"),
SYS_USER_NAME_CANNOT_EMPTY(100013, "username cannot be empty"),
SYS_ACCESS_LEVEL_CANNOT_EMPTY(100014, "accessLevel cannot be empty"),
SYS_WORKSPACE_ROLES_CANNOT_EMPTY(100015, "workspaceRoles cannot be empty"),
SYS_USER_BUILT_IN(100016, "Built-in user are not allowed to delete or update"),
SYS_ROLE_BUILT_IN(100017, "Built-in role are not allowed to delete or update"),
SYS_ROLE_NOT_DELETE(100018, "Used role cannot be deleted"),
SYS_USER_OLDPWD_INCORRECT(100019, "Incorrect old password. Please try again."),
SYS_SYSTEM_USER_NOT_LOGIN(100020, "System user cannot login."),
// Application
@@ -33,11 +41,35 @@ public enum RCode {
APP_DESCRIPTION_CANNOT_EMPTY(201006, "application description cannot be empty"),
APP_DUPLICATE_RECORD(201007, "application duplicate record"),
APP_NOT_EXIST(201008, "application does not exist"),
APP_PACKAGE_NAME_FORMAT_ERROR(201009, "application package name format error"),
APP_TAGS_FORMAT_ERROR(201010, "application tags format error"),
APP_SIGNATURE_FORMAT_ERROR(201011, "application signature format error"),
APP_SIGNATURE_CONTENT_CANNOT_EMPTY(201012, "application signature content cannot be empty"),
APP_SIGNATURE_NOT_EXIST(201013, "application signature does not exist"),
APP_NOTE_CONTENT_CANNOT_EMPTY(201014, "application note content cannot be empty"),
APP_ATTACHMENT_NOT_EXIST(201015, "application attachment does not exist"),
APP_PROPERTIES_FORMAT_ERROR(201016, "application properties format error"),
APP_IMPORT_FILE_FORMAT_ERROR(201017, "application import file format error"),
// Package
PACKAGE_ID_CANNOT_EMPTY(202001, "package id cannot be empty"),
PACKAGE_DESCRIPTION_CANNOT_EMPTY(202002, "package description cannot be empty"),
PACKAGE_FILE_TYPE_ERROR(202003, "package invalid file"),
PACKAGE_CANNOT_DELETE(202004, "The referenced package cannot be deleted"),
// GIT
GIT_COMMIT_CONFLICT_ERROR(203001, "Commit failed; fix conflicts and then commit the result"),
GIT_MERGE_FAILED(203002, "Merge failed,error message: {0}"),
GIT_PARENT_COMMITID_NOT_FOUND(203003, "Parent commitId not found"),
GIT_BINARY_CONFLICT_ERROR(203004, "Binary file conflict found; resolve conflicts in binary files manually"),
GIT_MERGE_NOT_SUPPORTED(203005, "Cannot merge in the {0} state"),
GIT_MERGE_TARGET_BRANCH_NOT_EXIST(203006, "The target branch {0} does not exist."),
GIT_TAG_ALREADY_EXISTS(203007, "Tag {0} already exists"),
GIT_TAG_NOT_FOUND(203008, "Tag {0} not found"),
GIT_TAG_ALREADY_IN_USE(203009,"Tag is already in use. Choose another tag."),
// Runner
@@ -46,7 +78,8 @@ public enum RCode {
// Playbook
PLAYBOOK_ID_CANNOT_EMPTY(302001, "playbook id cannot be empty"),
PLAYBOOK_NAME_DUPLICATE(302002, "playbook name duplicate "),
PLAYBOOK_INVALID_FILE(302003, "playbook Invalid file"),
// Workspace
WORKSPACE_ID_CANNOT_EMPTY(401001, "workspace id cannot be empty"),
@@ -58,6 +91,33 @@ public enum RCode {
WORKSPACE_CANNOT_DELETE(401007, "Built-in workspace cannot be deleted"),
WORKSPACE_VISIBILITY_ERROR(401008, "workspace visibility error"),
WORKSPACE_BUILT_IN(401009, "Built-in workspace cannot be update"),
WORKSPACE_NOT_EXIST(401010, "Workspace does not exist"),
WORKSPACE_MEMBER_USER_ID_REPEAT(401011, "Workspace member user repeat"),
//PCAP
PCAP_UPLOAD_WEB_SHARK_ERROR(501001, "web shark upload pcap error"),
//environment
ENVIRONMENT_SESSION_NOT_EXIST(601001, "environment session does not exist"),
ENVIRONMENT_NOT_EXIST(601002, "environment does not exist"),
ENVIRONMENT_USED(601003, "The environment is already in use"),
ENVIRONMENT_STATUS_ERROR(601004, "The environment status is unavailable"),
ENVIRONMENT_ID_CANNOT_EMPTY(601005, "environment id cannot be empty"),
//job
JOB_CFG_TYPE_CANNOT_EMPTY(701001, "Job configuration type cannot be empty"),
JOB_CFG_STATUS_CANNOT_EMPTY(701002, "Job configuration status cannot be empty"),
JOB_CFG_STATUS_ERROR(701003, "Job configuration status error"),
JOB_CFG_TYPE_ERROR(701004, "Job configuration type error"),
JOB_CFG_CRON_CANNOT_EMPTY(701005, "Job configuration cron cannot be empty"),
JOB_CFG_CRON_ERROR(701006, "Job configuration cron is not a valid cron expression"),
JOB_CFG_NANE_ALREADY_EXISTS(701007, "Job configuration name already exists"),
JOB_CFG_NOT_EXIST(701008, "Job configuration does not exist"),
SUCCESS(200, "success"); // 成功

View File

@@ -1,16 +1,15 @@
package net.geedge.asw.common.util;
import java.io.IOException;
import org.springframework.http.MediaType;
import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.j256.simplemagic.ContentInfo;
import com.j256.simplemagic.ContentInfoUtil;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import java.io.IOException;
import java.net.URLEncoder;
public class ResponseUtil {
@@ -25,7 +24,7 @@ public class ResponseUtil {
* @throws IOException
*/
public static void downloadFile(HttpServletResponse response, String contentType, String filename, byte[] data) throws IORuntimeException, IOException {
String fileName = T.URLUtil.encode(filename, T.CharsetUtil.CHARSET_UTF_8);
String fileName = URLEncoder.encode(filename, "UTF-8");
ReflectUtil.invoke(response, "addHeader", "Content-Disposition", "attachment; filename=" + fileName);
ReflectUtil.invoke(response, "addHeader", "Content-Length", "" + data.length);
ReflectUtil.invoke(response, "setHeader", "Access-Control-Expose-Headers", "Content-Disposition");
@@ -46,7 +45,7 @@ public class ResponseUtil {
public static void downloadFile(HttpServletResponse response, String filename, byte[] data)
throws IORuntimeException, IOException {
response.setContentType(ResponseUtil.getDownloadContentType(filename));
String fileName = T.URLUtil.encode(filename, T.CharsetUtil.CHARSET_UTF_8);
String fileName = URLEncoder.encode(filename, "UTF-8");
// response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
// response.addHeader("Content-Length", "" + data.length);
// response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");

View File

@@ -310,27 +310,6 @@ public class T {
* @author xiaoleilu
*/
public static class PageUtil extends cn.hutool.core.util.PageUtil {
public static final Integer DEFAULT_PAGENO = 1;
public static final Integer DEFAULT_PAGESIZE = 20;
public static Page getPage(Map<String, Object> params) {
// 分页参数
Integer pageNo = T.MapUtil.getInt(params, "current", DEFAULT_PAGENO);
Integer pageSize = T.MapUtil.getInt(params, "size", DEFAULT_PAGESIZE);
if (pageSize == -1) {
pageNo = 0;
pageSize = Integer.MAX_VALUE;
}
Page page = Page.of(pageNo, pageSize);
String orderBy = T.MapUtil.getStr(params, "orderBy");
if (T.StrUtil.isNotEmpty(orderBy)) {
page.addOrder(T.PageUtil.decodeOrderByStr(orderBy));
}
return page;
}
public static OrderItem decodeOrderByStr(String orderBy) {
if (cn.hutool.core.util.StrUtil.isBlank(orderBy)) {
return null;

View File

@@ -0,0 +1,28 @@
package net.geedge.asw.common.util;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.IOException;
public class TemplateUtil {
public static Template stringToTemplate(String templateStr,String templateKey) throws IOException {
// 创建配置类
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
//创建模板加载器
StringTemplateLoader templateLoader = new StringTemplateLoader();
// 存入模板
templateLoader.putTemplate(templateKey, templateStr); //template = 虚拟名称, 用来当作获取静态文件的key
//加载模板加载器
configuration.setTemplateLoader(templateLoader);
//得到模板
Template template = configuration.getTemplate(templateKey, "utf-8");
return template;
}
}

View File

@@ -387,6 +387,13 @@ public class VerifyUtil {
return this;
}
public VerifyUtil json(RCode code) {
if (!T.JSONUtil.isTypeJSON(T.StrUtil.toStringOrNull(value))) {
throw ASWException.builder().rcode(code).build();
}
return this;
}
/**
* 多参数校验,不能同时为空
*

View File

@@ -1,6 +1,6 @@
package net.geedge.asw.module.app.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.hutool.json.JSONObject;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R;
@@ -8,128 +8,190 @@ import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.service.IApplicationService;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/v1/application")
@RequestMapping("/api/v1/workspace")
public class ApplicationController {
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IApplicationService applicationService;
@Autowired
private ISysUserService userService;
@GetMapping("/{id}")
public R detail(@PathVariable String id) {
ApplicationEntity entity = applicationService.getById(id);
if (T.ObjectUtil.isNull(entity)){
throw new ASWException(RCode.APP_NOT_EXIST);
}
SysUserEntity createUser = userService.getById(entity.getCreateUserId());
SysUserEntity updateUser = userService.getById(entity.getUpdateUserId());
entity.setCreateUser(createUser);
entity.setUpdateUser(updateUser);
return R.ok().putData("record", entity);
@GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}")
public R infoApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@PathVariable("applicationName") String applicationName) {
Map<Object, Object> record = applicationService.infoApplication(workspaceId, branchName, applicationName);
return R.ok().putData("record", record);
}
@GetMapping("/{id}/{version}")
public R detail(@PathVariable String id,
@PathVariable(required = false) String version) {
ApplicationEntity entity = applicationService.getById(id);
if (T.StrUtil.isNotEmpty(version)){
entity = applicationService.queryByApplicationAndLog(id, version);
}
if (T.ObjectUtil.isNull(entity)){
throw new ASWException(RCode.APP_NOT_EXIST);
}
SysUserEntity createUser = userService.getById(entity.getCreateUserId());
SysUserEntity updateUser = userService.getById(entity.getUpdateUserId());
entity.setCreateUser(createUser);
entity.setUpdateUser(updateUser);
return R.ok().putData("record", entity);
@GetMapping("/{workspaceId}/branch/{branchName}/application")
public R listApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@RequestParam(value = "q", required = false) String q) {
List<Map<Object, Object>> records = applicationService.listApplication(workspaceId, branchName, q);
return R.ok().putData("records", records);
}
@PostMapping("/{workspaceId}/branch/{branchName}/application")
public synchronized R newApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@RequestBody Map<String, String> body) {
String applicationName = T.MapUtil.getStr(body, "name");
T.VerifyUtil.is(applicationName).notEmpty(RCode.PARAM_CANNOT_EMPTY);
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull()
.and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
Page page = applicationService.queryList(params);
return R.ok(page);
}
@PostMapping
public R add(@RequestBody ApplicationEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getName()).notEmpty(RCode.APP_NAME_CANNOT_EMPTY)
//.and(entity.getLongName()).notEmpty(RCode.APP_LONGNAME_CANNOT_EMPTY)
//.and(entity.getProperties()).notEmpty(RCode.APP_PROPERTIES_CANNOT_EMPTY)
//.and(entity.getSurrogates()).notEmpty(RCode.APP_SURROGATES_CANNOT_EMPTY)
//.and(entity.getDescription()).notEmpty(RCode.APP_DESCRIPTION_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
ApplicationEntity applicationEntity = applicationService.saveApplication(entity);
return R.ok().putData("id", applicationEntity.getId());
}
@PutMapping
public R update(@RequestBody ApplicationEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY)
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
//.and(entity.getLongName()).notEmpty(RCode.APP_LONGNAME_CANNOT_EMPTY)
//.and(entity.getProperties()).notEmpty(RCode.APP_PROPERTIES_CANNOT_EMPTY)
//.and(entity.getSurrogates()).notEmpty(RCode.APP_SURROGATES_CANNOT_EMPTY)
//.and(entity.getDescription()).notEmpty(RCode.APP_DESCRIPTION_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
ApplicationEntity applicationEntity = applicationService.updateApplication(entity);
return R.ok().putData("id", applicationEntity.getId());
}
@DeleteMapping
public R delete(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
applicationService.removeApplication(T.ListUtil.of(ids));
applicationService.newApplication(workspaceId, branchName, applicationName);
return R.ok();
}
@GetMapping("/{id}/log")
public R queryLogList(@PathVariable("id") String id) {
List<ApplicationEntity> applicationEntityList = applicationService.queryLogList(id);
return R.ok().putData("record", applicationEntityList);
}
@PostMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/rename")
public R renameApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@PathVariable("applicationName") String applicationName,
@RequestBody Map<String, String> body) {
String newName = T.MapUtil.getStr(body, "name");
T.VerifyUtil.is(newName).notEmpty(RCode.PARAM_CANNOT_EMPTY);
@GetMapping("/{id}/{oldVersion}/{newVersion}")
public R applicationCompare(@PathVariable("id") String id,
@PathVariable("oldVersion") String oldVersion,
@PathVariable("newVersion") String newVersion) {
List<ApplicationEntity> list = applicationService.compare(id, oldVersion, newVersion);
return R.ok().putData("record", list);
}
@PostMapping("/{id}/{version}/restore")
public R restore(@PathVariable("id") String id,
@PathVariable("version") String version) {
applicationService.restore(id, version);
// url decode
applicationName = T.URLUtil.decode(applicationName, StandardCharsets.UTF_8);
applicationService.renameApplication(workspaceId, branchName, applicationName, newName);
return R.ok();
}
@GetMapping("/analyze")
public void analyze(@RequestParam String workspaceId,
@RequestParam String pcapIds,
@PostMapping("/{workspaceId}/branch/{branchName}/application/commit")
public synchronized R updateApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@RequestBody Map<String, Object> body) {
String lastCommitId = T.MapUtil.getStr(body, "lastCommitId");
String message = T.MapUtil.getStr(body, "message");
T.VerifyUtil.is(lastCommitId).notEmpty(RCode.PARAM_CANNOT_EMPTY)
.and(message).notEmpty(RCode.PARAM_CANNOT_EMPTY);
List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, T.ListUtil.list(true));
if (T.CollUtil.isEmpty(files)) {
return R.ok();
}
for (Map<String, String> file : files) {
String action = T.MapUtil.getStr(file, "action");
String path = T.MapUtil.getStr(file, "path");
if (T.StrUtil.hasEmpty(action, path)) {
return R.error(RCode.PARAM_CANNOT_EMPTY);
}
if (T.StrUtil.equalsAny(action, "create", "update")) {
String content = T.MapUtil.getStr(file, "content");
T.VerifyUtil.is(content).notEmpty(RCode.PARAM_CANNOT_EMPTY);
}
}
applicationService.updateApplication(workspaceId, branchName, lastCommitId, message, files);
return R.ok();
}
@DeleteMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}")
public synchronized R deleteApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@PathVariable("applicationName") String applicationName) {
applicationService.deleteApplication(workspaceId, branchName, applicationName);
return R.ok();
}
@GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/commit")
public R listApplicationCommit(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@PathVariable("applicationName") String applicationName,
@RequestParam(required = false) String file) {
List<Map<Object, Object>> records = applicationService.listApplicationCommit(workspaceId, branchName, applicationName, file);
return R.ok().putData("records", records);
}
@GetMapping("/{workspaceId}/branch/{branchName}/application/{applicationName}/commit/{commitId}/content")
public R infoApplicationFileContent(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@PathVariable("applicationName") String applicationName,
@PathVariable("commitId") String commitId,
@RequestParam(required = true) String file) {
Map<Object, Object> record = applicationService.infoApplicationFileContent(workspaceId, branchName, applicationName, commitId, file);
return R.ok().putData("record", record);
}
@PostMapping("/{workspaceId}/branch/{branchName}/application/import")
public synchronized R importApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@RequestParam(defaultValue = "tsg2402") String format,
@RequestParam(value = "files") List<MultipartFile> fileList) {
// validate
WorkspaceEntity workspace = workspaceService.getById(workspaceId);
T.VerifyUtil.is(workspace).notNull(RCode.WORKSPACE_NOT_EXIST);
List<JSONObject> dataList = T.ListUtil.list(true);
try {
for (MultipartFile multipartFile : fileList) {
String str = T.IoUtil.readUtf8(multipartFile.getInputStream());
JSONObject jsonObject = T.JSONUtil.parseObj(str);
if (null == jsonObject.getJSONArray("applications")) {
continue;
}
dataList.add(jsonObject);
}
} catch (Exception e) {
throw new ASWException(RCode.APP_IMPORT_FILE_FORMAT_ERROR);
}
// 名称重复校验
List<Object> applicationList = dataList.stream()
.map(entries -> entries.getJSONArray("applications"))
.flatMap(Collection::stream)
.collect(Collectors.toList());
long distinctNameCount = applicationList.stream()
.map(obj -> ((JSONObject) obj).getStr("app_name"))
.distinct()
.count();
long size = applicationList.size();
if (T.ObjectUtil.notEqual(size, distinctNameCount)) {
throw new ASWException(RCode.APP_DUPLICATE_RECORD);
}
// import
List<ApplicationEntity> entityList = applicationService.importAppByFormat(workspaceId, branchName, format, dataList);
List<Map<String, String>> records = entityList.stream()
.map(entity -> Map.of("name", entity.getName()))
.collect(Collectors.toList());
return R.ok().putData("records", records);
}
@GetMapping("/{workspaceId}/branch/{branchName}/application/export")
public void exportApplication(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@RequestParam String names,
@RequestParam(defaultValue = "tsg2402") String format,
HttpServletResponse response) throws IOException {
applicationService.redirectDiscoverPage(workspaceId, pcapIds, response);
// validate
List<ApplicationEntity> appList = applicationService.listApplication(workspaceId, branchName, T.StrUtil.splitToArray(names, ","));
T.VerifyUtil.is(appList).notEmpty(RCode.APP_NOT_EXIST);
// format
byte[] bytes = applicationService.exportAppByFormat(appList, format);
// response
T.ResponseUtil.downloadFile(response, T.StrUtil.concat(true, "application_", System.currentTimeMillis() + ".json"), bytes);
}
}

View File

@@ -0,0 +1,91 @@
package net.geedge.asw.module.app.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.*;
import net.geedge.asw.module.app.entity.ApplicationReleaseEntity;
import net.geedge.asw.module.app.service.IApplicationReleaseService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/workspace")
public class ApplicationReleaseController {
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IApplicationReleaseService releaseService;
@GetMapping("/{workspaceId}/release/{id}")
public R detail(@PathVariable("workspaceId") String workspaceId, @PathVariable("id") String id) {
ApplicationReleaseEntity record = releaseService.queryInfo(id);
return R.ok().putData("record", record);
}
@GetMapping("/{workspaceId}/release")
public R list(@PathVariable("workspaceId") String workspaceId, @RequestParam Map<String, Object> params) {
// workspaceId
params = T.MapUtil.defaultIfEmpty(params, new HashMap<>());
params.put("workspaceId", workspaceId);
Page page = releaseService.queryList(params);
return R.ok(page);
}
@PostMapping("/{workspaceId}/release")
public R add(@PathVariable("workspaceId") String workspaceId, @RequestBody Map<String, String> requestBody) {
String name = T.MapUtil.getStr(requestBody, "name", "");
String tagName = T.MapUtil.getStr(requestBody, "tagName", "");
String description = T.MapUtil.getStr(requestBody, "description", "");
if (T.StrUtil.hasEmpty(name, tagName)) {
throw new ASWException(RCode.PARAM_CANNOT_EMPTY);
}
ApplicationReleaseEntity record = releaseService.saveRelease(workspaceId, name, tagName, description);
return R.ok().putData("record", record);
}
@PutMapping("/{workspaceId}/release")
public R update(@PathVariable("workspaceId") String workspaceId, @RequestBody Map<String, String> requestBody) {
String id = T.MapUtil.getStr(requestBody, "id", "");
String name = T.MapUtil.getStr(requestBody, "name", "");
String description = T.MapUtil.getStr(requestBody, "description", "");
if (T.StrUtil.hasEmpty(id, name)) {
throw new ASWException(RCode.PARAM_CANNOT_EMPTY);
}
ApplicationReleaseEntity record = releaseService.updateRelease(id, name, description);
return R.ok().putData("record", record);
}
@DeleteMapping("/{workspaceId}/release/{id}")
public R delete(@PathVariable("workspaceId") String workspaceId, @PathVariable("id") String id) {
releaseService.removeRelease(id);
return R.ok();
}
@GetMapping("/{workspaceId}/release/{id}/file")
public void download(@PathVariable("workspaceId") String workspaceId,
@PathVariable("id") String id,
HttpServletResponse response) throws IOException {
ApplicationReleaseEntity release = releaseService.getById(id);
T.VerifyUtil.is(release).notNull(RCode.SYS_RECORD_NOT_FOUND);
WorkspaceEntity workspace = workspaceService.getById(workspaceId);
T.VerifyUtil.is(workspace).notNull(RCode.SYS_RECORD_NOT_FOUND);
String fileName = T.StrUtil.concat(true, workspace.getName(), "-", release.getTagName(), ".zip");
byte[] fileBytes = T.FileUtil.readBytes(T.FileUtil.file(release.getPath()));
ResponseUtil.downloadFile(response, MediaType.APPLICATION_OCTET_STREAM_VALUE, fileName, fileBytes);
}
}

View File

@@ -0,0 +1,111 @@
package net.geedge.asw.module.app.controller;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.service.IBranchService;
import net.geedge.asw.module.app.util.JGitUtils;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/workspace")
public class BranchController {
@Autowired
private IBranchService branchService;
@Autowired
private IWorkspaceService workspaceService;
@GetMapping("/{workspaceId}/branch")
public R listBranch(@PathVariable("workspaceId") String workspaceId,
@RequestParam(value = "search", required = false) String search) {
List<Map<Object, Object>> list = branchService.listBranch(workspaceId, search);
return R.ok().putData("records", list);
}
@GetMapping("/{workspaceId}/branch/{branchName}")
public R infoBranch(@PathVariable("workspaceId") String workspaceId, @PathVariable("branchName") String branchName) {
Map<Object, Object> record = branchService.infoBranch(workspaceId, branchName);
return R.ok().putData("record", record);
}
@PostMapping("/{workspaceId}/branch")
public synchronized R newBranch(@PathVariable("workspaceId") String workspaceId, @RequestBody Map<String, String> requestBody) {
String branch = T.MapUtil.getStr(requestBody, "branch", "");
String ref = T.MapUtil.getStr(requestBody, "ref", "");
if (T.StrUtil.hasEmpty(branch, ref)) {
throw new ASWException(RCode.PARAM_CANNOT_EMPTY);
}
Map<Object, Object> record = branchService.newBranch(workspaceId, branch, ref);
return R.ok().putData("record", record);
}
@DeleteMapping("/{workspaceId}/branch/{branchName}")
public synchronized R deleteBranch(@PathVariable("workspaceId") String workspaceId, @PathVariable("branchName") String branchName) {
branchService.deleteBranch(workspaceId, branchName);
return R.ok();
}
@GetMapping("/{workspaceId}/branch/{branchName}/commits")
public R listBranchCommit(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@RequestParam(required = false) String path,
@RequestParam(required = false) Integer current,
@RequestParam(required = false) Integer size) {
List<Map<Object, Object>> records = branchService.listBranchCommit(workspaceId, branchName, path);
long total = T.CollUtil.size(records);
if (null != current && null != size) {
int fromIndex = (current - 1) * size;
int toIndex = Math.min(fromIndex + size, records.size());
records = records.subList(fromIndex, toIndex);
}
return R.ok().putData("records", records).put("total", total);
}
@GetMapping({
"/{workspaceId}/branch/{branchName}/commit/{commitId}/diff",
"/{workspaceId}/branch/{branchName}/commit/{commitId}/diff/{oldCommitId}"
})
public R branchCommitDiff(@PathVariable("workspaceId") String workspaceId,
@PathVariable("branchName") String branchName,
@PathVariable("commitId") String newCommitId,
@PathVariable(value = "oldCommitId", required = false) String oldCommitId) throws IOException {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
if (T.StrUtil.isEmpty(oldCommitId)) {
// oldCommitId 为空默认对比 parent commitId
RevCommit parentCommit = JGitUtils.getParentCommit(repository, branchName, newCommitId);
oldCommitId = parentCommit.getName();
}
RevCommit oldCommit = JGitUtils.infoCommit(repository, oldCommitId);
RevCommit newCommit = JGitUtils.infoCommit(repository, newCommitId);
List<Map<Object, Object>> diffList = JGitUtils.getDiffFileListInCommits(repository, newCommitId, oldCommitId);
Map<Object, Object> record = T.MapUtil.builder()
.put("oldBranch", branchName)
.put("newBranch", branchName)
.put("oldCommitId", oldCommitId)
.put("newCommitId", newCommitId)
.put("oldCommit", JGitUtils.buildAswCommitInfo(oldCommit))
.put("newCommit", JGitUtils.buildAswCommitInfo(newCommit))
.put("files", diffList)
.build();
return R.ok().putData("record", record);
}
}
}

View File

@@ -0,0 +1,109 @@
package net.geedge.asw.module.app.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
import net.geedge.asw.module.app.service.IApplicationMergeService;
import net.geedge.asw.module.app.service.impl.ApplicationMergeServiceImpl.MergeRequestStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/workspace")
public class MergeRequestController {
@Autowired
private IApplicationMergeService applicationMergeService;
@GetMapping("/{workspaceId}/mr")
public R listMr(@PathVariable("workspaceId") String workspaceId, @RequestParam Map<String, Object> params) {
// workspaceId
params = T.MapUtil.defaultIfEmpty(params, new HashMap<>());
params.put("workspaceId", workspaceId);
Page page = applicationMergeService.queryList(params);
return R.ok(page);
}
@GetMapping("/{workspaceId}/mr/{mrId}")
public R infoMr(@PathVariable("mrId") String mrId) {
ApplicationMergeEntity record = applicationMergeService.queryInfo(mrId);
return R.ok().putData("record", record);
}
@GetMapping("/{workspaceId}/mr/{mrId}/commits")
public R listMrCommit(@PathVariable("mrId") String mrId) throws IOException {
List<Map<Object, Object>> records = applicationMergeService.listMrCommit(mrId);
return R.ok().putData("records", records);
}
@GetMapping("/{workspaceId}/mr/{mrId}/diffs")
public R mrDiff(@PathVariable("mrId") String mrId) {
Map<Object, Object> record = applicationMergeService.mrCommitDiff(mrId);
return R.ok().putData("record", record);
}
@GetMapping("/{workspaceId}/mr/{mrId}/conflicts")
public R getMrConflictList(@PathVariable("mrId") String mrId) throws IOException {
Map<Object, Object> record = applicationMergeService.getMrConflictList(mrId);
return R.ok().putData("record", record);
}
@PostMapping("/{workspaceId}/mr/{mrId}/conflicts")
public R resolveMrConflicts(@PathVariable("mrId") String mrId, @RequestBody Map<String, Object> body) throws IOException{
String commitId = T.MapUtil.getStr(body, "commitId");
String commitMessage = T.MapUtil.getStr(body, "commitMessage");
List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, new ArrayList());
T.VerifyUtil.is(commitId).notEmpty(RCode.PARAM_CANNOT_EMPTY)
.and(commitMessage).notEmpty(RCode.PARAM_CANNOT_EMPTY)
.and(files).notEmpty(RCode.PARAM_CANNOT_EMPTY);
for (Map<String, String> m : files) {
String path = T.MapUtil.getStr(m, "path");
String content = T.MapUtil.getStr(m, "content");
T.VerifyUtil.is(path).notEmpty(RCode.PARAM_CANNOT_EMPTY)
.and(content).notEmpty(RCode.PARAM_CANNOT_EMPTY);
}
applicationMergeService.resolveMrConflicts(mrId, body);
return R.ok();
}
@PostMapping("/{workspaceId}/mr")
public R newMr(@PathVariable("workspaceId") String workspaceId, @RequestBody ApplicationMergeEntity entity) throws IOException {
T.VerifyUtil.is(entity).notNull()
.and(entity.getTitle()).notEmpty(RCode.PARAM_CANNOT_EMPTY)
.and(entity.getTargetBranch()).notEmpty(RCode.PARAM_CANNOT_EMPTY)
.and(entity.getTargetBranch()).notEmpty(RCode.PARAM_CANNOT_EMPTY);
// workspaceId
entity.setWorkspaceId(workspaceId);
ApplicationMergeEntity record = applicationMergeService.newMr(entity);
return R.ok().putData("record", record);
}
@PutMapping("/{workspaceId}/mr/{mrId}")
public synchronized R mergeMr(@PathVariable("mrId") String mrId, @RequestBody(required = false) Map<String, Object> body) {
String action = T.MapUtil.getStr(body, "action");
T.VerifyUtil.is(action).notEmpty(RCode.PARAM_CANNOT_EMPTY);
ApplicationMergeEntity record = applicationMergeService.mergeMr(mrId, action);
if(T.BooleanUtil.and(
T.ObjectUtil.equals("merge", action),
T.ObjectUtil.notEqual(MergeRequestStatus.MERGED.toString(), record.getStatus())
)){
return R.error(RCode.GIT_MERGE_FAILED).putData("record", record);
}
return R.ok().putData("record", record);
}
}

View File

@@ -1,65 +1,81 @@
package net.geedge.asw.module.app.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.ResponseUtil;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.PackageEntity;
import net.geedge.asw.module.app.service.IPackageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/package")
@RequestMapping("/api/v1/workspace")
public class PackageController {
@Autowired
private IPackageService packageService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
PackageEntity entity = packageService.getById(id);
@GetMapping("/{workspaceId}/package/{id}")
public R detail(@PathVariable("workspaceId") String workspaceId, @PathVariable("id") String id) {
PackageEntity entity = packageService.queryInfo(id);
return R.ok().putData("record", entity);
}
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull()
.and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
@GetMapping("/{workspaceId}/package")
public R list(@PathVariable("workspaceId") String workspaceId, @RequestParam Map<String, Object> params) {
// workspaceId
params = T.MapUtil.defaultIfEmpty(params, new HashMap<>());
params.put("workspaceId", workspaceId);
Page page = packageService.queryList(params);
return R.ok(page);
}
@PostMapping
public R add(@RequestBody PackageEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
.and(entity.getDescription()).notEmpty(RCode.PACKAGE_DESCRIPTION_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
@PostMapping("/{workspaceId}/package")
public R add(@PathVariable(value = "workspaceId", required = true) String workspaceId,
@RequestParam(value = "description", required = false) String description,
@RequestParam(value = "file") MultipartFile file) {
PackageEntity pkgEntity = packageService.savePackage(entity);
return R.ok().putData("id", pkgEntity.getId());
PackageEntity entity = packageService.savePackage(workspaceId, description, file.getResource());
return R.ok().putData("record", entity);
}
@PutMapping
public R update(@RequestBody PackageEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY)
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
.and(entity.getDescription()).notEmpty(RCode.PACKAGE_DESCRIPTION_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
@PutMapping("/{workspaceId}/package")
public R update(@PathVariable(value = "workspaceId", required = true) String workspaceId,
@RequestParam(value = "packageId", required = true) String packageId,
@RequestParam(value = "description", required = false) String description,
@RequestParam(value = "name", required = false) String name) {
PackageEntity pkgEntity = packageService.updatePackage(entity);
return R.ok().putData("id", pkgEntity.getId());
PackageEntity entity = packageService.updatePackage(workspaceId, packageId, name, description);
return R.ok().putData("record", entity);
}
@DeleteMapping
@DeleteMapping("/{workspaceId}/package")
public R delete(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
packageService.removePackage(T.ListUtil.of(ids));
return R.ok();
}
@GetMapping("/{workspaceId}/package/{id}/download")
public void download(@PathVariable("workspaceId") String workspaceId,
@PathVariable("id") String id,
HttpServletResponse response) throws IOException {
PackageEntity entity = packageService.getById(id);
T.VerifyUtil.is(entity).notNull(RCode.SYS_RECORD_NOT_FOUND);
File pkgFile = T.FileUtil.file(entity.getPath());
ResponseUtil.downloadFile(response, MediaType.APPLICATION_OCTET_STREAM_VALUE, pkgFile.getName(), T.FileUtil.readBytes(pkgFile));
}
}

View File

@@ -0,0 +1,52 @@
package net.geedge.asw.module.app.controller;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.service.ITagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/workspace")
public class TagController {
@Autowired
private ITagService tagService;
@GetMapping("/{workspaceId}/tag/{tagName}")
public R infoTag(@PathVariable("workspaceId") String workspaceId, @PathVariable("tagName") String name) {
Map<Object, Object> record = tagService.infoTag(workspaceId, name);
return R.ok().putData("record", record);
}
@GetMapping("/{workspaceId}/tag")
public R listTag(@PathVariable("workspaceId") String workspaceId,
@RequestParam(value = "search", required = false) String search) {
List<Map<Object, Object>> list = tagService.listTag(workspaceId, search);
return R.ok().putData("records", list);
}
@PostMapping("/{workspaceId}/tag")
public synchronized R newTag(@PathVariable("workspaceId") String workspaceId, @RequestBody Map<String, String> requestBody) {
String name = T.MapUtil.getStr(requestBody, "name", "");
String ref = T.MapUtil.getStr(requestBody, "ref", "");
String description = T.MapUtil.getStr(requestBody, "description", "");
if (T.StrUtil.hasEmpty(name, ref)) {
throw new ASWException(RCode.PARAM_CANNOT_EMPTY);
}
Map<Object, Object> record = tagService.newTag(workspaceId, name, ref, description);
return R.ok().putData("record", record);
}
@DeleteMapping("/{workspaceId}/tag/{tagName}")
public synchronized R deleteTag(@PathVariable("workspaceId") String workspaceId, @PathVariable("tagName") String name) {
tagService.deleteTag(workspaceId, name);
return R.ok();
}
}

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.app.entity.ApplicationAttachmentEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApplicationAttachmentDao extends BaseMapper<ApplicationAttachmentEntity>{
}

View File

@@ -1,26 +0,0 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
@Mapper
public interface ApplicationDao extends BaseMapper<ApplicationEntity>{
List<ApplicationEntity> queryList(Page page, Map<String, Object> params);
@Select("select * from ( select * from application union select * from application_log ) app where app.id = #{id} and app.op_version = #{version}")
ApplicationEntity queryByApplicationAndLog(String id, String version);
List<ApplicationEntity> queryLogList(String id);
List<ApplicationEntity> compare(@Param("params") Map<String, Object> params);
}

View File

@@ -0,0 +1,15 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.app.entity.ApplicationHrefEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface ApplicationHrefDao extends BaseMapper<ApplicationHrefEntity> {
List<ApplicationHrefEntity> queryList(@Param("applicationId") String applicationId);
}

View File

@@ -0,0 +1,16 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface ApplicationMergeDao extends BaseMapper<ApplicationMergeEntity> {
List<ApplicationMergeEntity> queryList(Page page, Map<String, Object> params);
}

View File

@@ -1,10 +1,10 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.app.entity.SignatureEntity;
import net.geedge.asw.module.app.entity.ApplicationNoteEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SignatureDao extends BaseMapper<SignatureEntity>{
public interface ApplicationNoteDao extends BaseMapper<ApplicationNoteEntity>{
}

View File

@@ -0,0 +1,17 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.app.entity.ApplicationReleaseEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface ApplicationReleaseDao extends BaseMapper<ApplicationReleaseEntity> {
List<ApplicationReleaseEntity> queryList(Page page, Map<String, Object> params);
}

View File

@@ -0,0 +1,15 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.app.entity.ApplicationSignatureEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
public interface ApplicationSignatureDao extends BaseMapper<ApplicationSignatureEntity>{
List<ApplicationSignatureEntity> queryList(@Param("params") Map<Object, Object> params);
}

View File

@@ -0,0 +1,25 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("application_attachment")
public class ApplicationAttachmentEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String applicationId;
private String name;
private String path;
private Long createTimestamp;
private String createUserId;
}

View File

@@ -1,36 +1,26 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
@Data
@TableName("application")
public class ApplicationEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String longName;
private String properties;
private String developer;
private String website;
private String description;
private String surrogates;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
@TableField(typeHandler = JacksonTypeHandler.class)
private Object packageName;
private String workspaceId;
private Integer opVersion;
@TableField(typeHandler = JacksonTypeHandler.class)
private Object properties;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
@TableField(typeHandler = JacksonTypeHandler.class)
private Object signature;
}

View File

@@ -0,0 +1,30 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import java.io.Serializable;
@Data
@TableName(value = "application_href", autoResultMap = true)
public class ApplicationHrefEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String applicationId;
private String name;
private String url;
@TableField(updateStrategy = FieldStrategy.NEVER)
private Long createTimestamp;
@TableField(updateStrategy = FieldStrategy.NEVER)
private String createUserId;
@TableField(exist = false)
private SysUserEntity createUser;
}

View File

@@ -1,24 +1,61 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import java.util.List;
@Data
@TableName("application_log")
@TableName(value = "application_log", autoResultMap = true)
public class ApplicationLogEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String longName;
private String tags;
private String packageName;
private String website;
private String provider;
@TableField(typeHandler = JacksonTypeHandler.class)
private String properties;
private String description;
private String surrogates;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
private String workspaceId;
private Integer opVersion;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
@TableField(exist = false)
private ApplicationSignatureEntity signature;
@TableField(exist = false)
private ApplicationNoteEntity note;
@TableField(exist = false)
private List<ApplicationAttachmentEntity> attatchments;
}

View File

@@ -0,0 +1,37 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
@Data
@TableName(value = "application_merge", autoResultMap = true)
public class ApplicationMergeEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String sourceBranch;
private String targetBranch;
private String startCommitId;
private String endCommitId;
private String title;
private Integer removeSourceBranch = 0;
private String description;
private String status;
private String message;
private Long createTimestamp;
private String createUserId;
private String workspaceId;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private Object commit;
}

View File

@@ -6,24 +6,20 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("signature")
public class SignatureEntity {
@TableName("application_note")
public class ApplicationNoteEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String appId;
private String name;
private String tags;
private String description;
private Integer displayFlag;
private String conditions;
private Long opVersion;
private String applicationId;
private String content;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
private String workspaceId;
private String createUserId;
private Long opVersion;
}

View File

@@ -0,0 +1,40 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
@Data
@TableName(value = "application_release", autoResultMap = true)
public class ApplicationReleaseEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String tagName;
private String path;
private String description;
private String createUserId;
private Long createTimestamp;
private String updateUserId;
private Long updateTimestamp;
private String workspaceId;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
@TableField(exist = false)
private Object commit;
}

View File

@@ -0,0 +1,30 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
@Data
@TableName("application_signature")
public class ApplicationSignatureEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String applicationId;
private String content;
private Long createTimestamp;
private String createUserId;
private Long opVersion;
@TableField(exist = false)
private SysUserEntity createUser;
}

View File

@@ -4,7 +4,9 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
@Data
@TableName("package")
@@ -13,12 +15,16 @@ public class PackageEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String logo;
private String icon;
private String description;
private String platform;
private String version;
private String identifier;
@JsonIgnore
private String path;
private Long size;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
@@ -27,6 +33,9 @@ public class PackageEntity {
private String workspaceId;
@TableField(exist = false)
private String workbookId;
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
}

View File

@@ -0,0 +1,17 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.module.app.entity.ApplicationAttachmentEntity;
import org.springframework.core.io.Resource;
import java.io.IOException;
public interface IApplicationAttachmentService extends IService<ApplicationAttachmentEntity>{
ApplicationAttachmentEntity saveAttachment(Resource fileResource, String applicationId);
void removedAttachment(String applicationId, String ids);
void download(HttpServletResponse response, String applicationId, String attachmentId) throws IOException;
}

View File

@@ -0,0 +1,17 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationHrefEntity;
import java.util.List;
public interface IApplicationHrefService extends IService<ApplicationHrefEntity> {
List<ApplicationHrefEntity> queryList(String applicationId);
List<ApplicationHrefEntity> updateBatchHref(List<ApplicationHrefEntity> hrefList);
List<ApplicationHrefEntity> updateBatchHref(String applicationId, List<ApplicationHrefEntity> hrefList);
}

View File

@@ -0,0 +1,29 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface IApplicationMergeService extends IService<ApplicationMergeEntity> {
ApplicationMergeEntity queryInfo(String mrId);
Page queryList(Map<String, Object> params);
ApplicationMergeEntity newMr(ApplicationMergeEntity entity) throws IOException;
List<Map<Object, Object>> listMrCommit(String mrId) throws IOException;
Map<Object, Object> mrCommitDiff(String mrId);
Map<Object, Object> getMrConflictList(String mrId) throws IOException;
void resolveMrConflicts(String mrId, Map<String, Object> body) throws IOException;
ApplicationMergeEntity mergeMr(String mrId, String action);
}

View File

@@ -0,0 +1,9 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationNoteEntity;
public interface IApplicationNoteService extends IService<ApplicationNoteEntity>{
void saveNote(ApplicationNoteEntity note, String applicationId);
}

View File

@@ -0,0 +1,23 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationReleaseEntity;
import java.util.Map;
public interface IApplicationReleaseService extends IService<ApplicationReleaseEntity> {
ApplicationReleaseEntity queryInfo(String id);
Page queryList(Map<String, Object> params);
ApplicationReleaseEntity saveRelease(String workspaceId, String name, String tagName, String description);
ApplicationReleaseEntity updateRelease(String id, String name, String description);
void removeRelease(String id);
void removeRelease(String workspaceId, String tagName);
}

View File

@@ -1,32 +1,33 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
import cn.hutool.json.JSONObject;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface IApplicationService extends IService<ApplicationEntity>{
public interface IApplicationService {
Page queryList(Map<String, Object> params);
Map<Object, Object> infoApplication(String workspaceId, String branch, String name);
ApplicationEntity saveApplication(ApplicationEntity entity);
List<ApplicationEntity> listApplication(String workspaceId, String branch, String... names);
ApplicationEntity updateApplication(ApplicationEntity entity);
List<Map<Object, Object>> listApplication(String workspaceId, String branch, String q);
void removeApplication(List<String> ids);
void newApplication(String workspaceId, String branch, String name);
ApplicationEntity queryByApplicationAndLog(String id, String version);
void renameApplication(String workspaceId, String branch, String oldName, String newName);
List<ApplicationEntity> compare(String id, String oldVersion, String newVersion);
void deleteApplication(String workspaceId, String branch, String name);
List<ApplicationEntity> queryLogList(String id);
void updateApplication(String workspaceId, String branch, String lastCommitId, String message, List<Map<String, String>> updateContent);
void restore(String id, String version);
List<Map<Object, Object>> listApplicationCommit(String workspaceId, String branch, String name, String file);
void redirectDiscoverPage(String workspaceId, String pcapIds, HttpServletResponse response) throws IOException;
Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String name, String commitId, String file);
byte[] exportAppByFormat(List<ApplicationEntity> appList, String format);
List<ApplicationEntity> importAppByFormat(String workspaceId, String branch, String format, List<JSONObject> dataList);
}

View File

@@ -0,0 +1,20 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationSignatureEntity;
import java.util.List;
public interface IApplicationSignatureService extends IService<ApplicationSignatureEntity>{
void saveSignature(ApplicationSignatureEntity signature, String applicationId);
List<ApplicationSignatureEntity> queryList(String applicationId);
List<ApplicationSignatureEntity> compare(String applicationId, String oldVersion, String newVersion);
void restore(String id, String version);
ApplicationSignatureEntity queryLastVersionSignatureByAppId(String applicationId);
}

View File

@@ -0,0 +1,18 @@
package net.geedge.asw.module.app.service;
import java.util.List;
import java.util.Map;
public interface IBranchService {
List<Map<Object, Object>> listBranch(String workspaceId, String search);
Map<Object, Object> infoBranch(String workspaceId, String branch);
Map<Object, Object> newBranch(String workspaceId, String name, String ref);
void deleteBranch(String workspaceId, String branch);
List<Map<Object, Object>> listBranchCommit(String workspaceId, String branch, String path);
}

View File

@@ -3,17 +3,20 @@ package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.PackageEntity;
import org.springframework.core.io.Resource;
import java.util.List;
import java.util.Map;
public interface IPackageService extends IService<PackageEntity>{
PackageEntity queryInfo(String id);
Page queryList(Map<String, Object> params);
PackageEntity savePackage(PackageEntity entity);
PackageEntity updatePackage(PackageEntity entity);
PackageEntity savePackage(String workspaceId, String description, Resource fileResource);
void removePackage(List<String> ids);
PackageEntity updatePackage(String workspaceId, String packageId, String name, String description);
}

View File

@@ -1,8 +0,0 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.SignatureEntity;
public interface ISignatureService extends IService<SignatureEntity>{
}

View File

@@ -0,0 +1,19 @@
package net.geedge.asw.module.app.service;
import cn.hutool.json.JSONObject;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import java.util.List;
import java.util.Map;
public interface ITSGApplicationService {
Map<Object, Object> aswToTsg2402(List<ApplicationEntity> appList);
List<ApplicationEntity> tsg2402ToAsw(String workspaceId, List<JSONObject> dataList);
Map<Object, Object> aswToTsg2410(List<ApplicationEntity> appList);
List<ApplicationEntity> tsg2410ToAsw(String workspaceId, List<JSONObject> dataList);
}

View File

@@ -0,0 +1,16 @@
package net.geedge.asw.module.app.service;
import java.util.List;
import java.util.Map;
public interface ITagService {
Map<Object, Object> infoTag(String workspaceId, String name);
List<Map<Object, Object>> listTag(String workspaceId, String search);
Map<Object, Object> newTag(String workspaceId, String name, String branch, String message);
void deleteTag(String workspaceId, String name);
}

View File

@@ -0,0 +1,102 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationAttachmentDao;
import net.geedge.asw.module.app.entity.ApplicationAttachmentEntity;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.service.IApplicationAttachmentService;
import net.geedge.asw.module.app.service.IApplicationService;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaTypeFactory;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@Service
public class ApplicationAttachmentServiceImpl extends ServiceImpl<ApplicationAttachmentDao, ApplicationAttachmentEntity> implements IApplicationAttachmentService {
private static final Log log = Log.get();
// @Autowired
// private IApplicationService applicationService;
@Override
public ApplicationAttachmentEntity saveAttachment(Resource fileResource, String applicationId) {
// ApplicationEntity app = applicationService.getById(applicationId);
// ApplicationAttachmentEntity entity = new ApplicationAttachmentEntity();
// try {
// entity.setName(fileResource.getFilename());
// entity.setCreateTimestamp(System.currentTimeMillis());
// entity.setCreateUserId(StpUtil.getLoginIdAsString());
// entity.setApplicationId(applicationId);
//
// // path
// File destination = T.FileUtil.file(T.WebPathUtil.getRootPath(), app.getId(), fileResource.getFilename());
// FileUtils.copyInputStreamToFile(fileResource.getInputStream(), destination);
// entity.setPath(destination.getPath());
//
// // 根据文件 applicationId path 判断是否已上存在,存在则响应当前实体
// ApplicationAttachmentEntity attachment = this.getOne(new LambdaQueryWrapper<ApplicationAttachmentEntity>()
// .eq(ApplicationAttachmentEntity::getApplicationId, applicationId)
// .eq(ApplicationAttachmentEntity::getPath, destination.getPath()));
// if (T.ObjectUtil.isNotNull(attachment)) {
// return attachment;
// }
//
// // save
// this.save(entity);
//
// } catch (IOException e) {
// log.error(e, "[saveAttachment] [error] [applicationId: {}]", applicationId);
// throw new ASWException(RCode.ERROR);
// }
// return entity;
return null;
}
@Override
public void removedAttachment(String applicationId, String ids) {
List<String> idList = Arrays.asList(ids.split(","));
for (String id : idList) {
ApplicationAttachmentEntity attachment = this.getOne(new LambdaQueryWrapper<ApplicationAttachmentEntity>()
.eq(ApplicationAttachmentEntity::getApplicationId, applicationId)
.eq(ApplicationAttachmentEntity::getId, id));
T.FileUtil.del(FileUtil.file(attachment.getPath()));
this.removeById(id);
}
}
@Override
public void download(HttpServletResponse response, String applicationId, String attachmentId) throws IOException {
ApplicationAttachmentEntity attachment = this.getOne(new LambdaQueryWrapper<ApplicationAttachmentEntity>()
.eq(ApplicationAttachmentEntity::getApplicationId, applicationId)
.eq(ApplicationAttachmentEntity::getId, attachmentId));
if (T.ObjectUtil.isNull(attachment)) {
throw new ASWException(RCode.APP_ATTACHMENT_NOT_EXIST);
}
File file = FileUtil.file(attachment.getPath());
response.setStatus(200);
response.setContentType( MediaTypeFactory.getMediaType(file.getName()).toString());
response.setContentLength(Integer.parseInt(String.valueOf(file.length())));
response.setHeader("Content-disposition", "attachment; filename=" + file.getName());
response.getOutputStream().write(T.FileUtil.readBytes(file));
response.flushBuffer();
}
}

View File

@@ -0,0 +1,59 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationHrefDao;
import net.geedge.asw.module.app.entity.ApplicationHrefEntity;
import net.geedge.asw.module.app.service.IApplicationHrefService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class ApplicationHrefServiceImpl extends ServiceImpl<ApplicationHrefDao, ApplicationHrefEntity> implements IApplicationHrefService {
private static final Log log = Log.get();
@Override
public List<ApplicationHrefEntity> queryList(String applicationId) {
return this.getBaseMapper().queryList(applicationId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public List<ApplicationHrefEntity> updateBatchHref(List<ApplicationHrefEntity> hrefList) {
for (ApplicationHrefEntity entity : hrefList) {
// validate
ApplicationHrefEntity one = this.getOne(new LambdaQueryWrapper<ApplicationHrefEntity>()
.eq(ApplicationHrefEntity::getApplicationId, entity.getApplicationId())
.eq(ApplicationHrefEntity::getName, entity.getName())
.ne(T.ObjectUtil.isNotEmpty(entity.getId()), ApplicationHrefEntity::getId, entity.getId()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
// save or update
this.saveOrUpdate(entity);
}
return hrefList;
}
@Override
public List<ApplicationHrefEntity> updateBatchHref(String applicationId, List<ApplicationHrefEntity> hrefList) {
for (ApplicationHrefEntity entity : hrefList) {
entity.setApplicationId(applicationId);
}
return this.updateBatchHref(hrefList);
}
}

View File

@@ -0,0 +1,414 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.config.Query;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationMergeDao;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
import net.geedge.asw.module.app.service.IApplicationMergeService;
import net.geedge.asw.module.app.service.IBranchService;
import net.geedge.asw.module.app.util.JGitUtils;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class ApplicationMergeServiceImpl extends ServiceImpl<ApplicationMergeDao, ApplicationMergeEntity> implements IApplicationMergeService {
private final static Log log = Log.get();
@Autowired
private ISysUserService userService;
@Autowired
private IBranchService branchService;
@Autowired
private IWorkspaceService workspaceService;
@Override
public ApplicationMergeEntity queryInfo(String mrId) {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
SysUserEntity user = userService.getById(entity.getCreateUserId());
entity.setCreateUser(user);
return entity;
}
@Override
public Page queryList(Map<String, Object> params) {
Page page = new Query(ApplicationMergeEntity.class).getPage(params);
List<ApplicationMergeEntity> list = this.getBaseMapper().queryList(page, params);
page.setRecords(list);
return page;
}
@Override
public ApplicationMergeEntity newMr(ApplicationMergeEntity entity) throws IOException {
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
// 默认状态
entity.setStatus(MergeRequestStatus.OPEN.toString());
// start_commit_id
String workspaceId = entity.getWorkspaceId();
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
ObjectId branchAId = repository.resolve(entity.getSourceBranch());
ObjectId branchBId = repository.resolve(entity.getTargetBranch());
RevCommit mergeBaseCommit = JGitUtils.getMergeBaseCommit(repository, branchAId, branchBId);
entity.setStartCommitId(mergeBaseCommit.getName());
}
// save
this.save(entity);
SysUserEntity user = userService.getById(entity.getCreateUserId());
entity.setCreateUser(user);
return entity;
}
@Override
public List<Map<Object, Object>> listMrCommit(String mrId) throws IOException {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
String oldCommitId = entity.getStartCommitId();
String newCommitId = null;
MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
switch (mergeRequestStatus) {
case OPEN: {
newCommitId = JGitUtils.getBranchLatestCommit(repository, entity.getSourceBranch()).getName();
break;
}
case CLOSED:
case MERGED: {
newCommitId = entity.getEndCommitId();
break;
}
}
// newCommitId-oldCommitId 期间的提交信息
List<RevCommit> revCommits = JGitUtils.listCommitRange(repository, newCommitId, oldCommitId);
List<Map<Object, Object>> list = T.ListUtil.list(true);
revCommits.forEach(revCommit -> list.add(JGitUtils.buildAswCommitInfo(revCommit)));
return list;
}
}
@Override
public Map<Object, Object> mrCommitDiff(String mrId) {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
String sourceBranch = entity.getSourceBranch();
String targetBranch = entity.getTargetBranch();
// 获取 sourceBranch,targetBranch 合并冲突文件
List<String> conflictFileList = T.ListUtil.list(true);
// src branch changed files
String oldCommitId = entity.getStartCommitId();
String newCommitId = null;
MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
switch (mergeRequestStatus) {
case OPEN: {
RevCommit branchLatestCommit = JGitUtils.getBranchLatestCommit(repository, sourceBranch);
newCommitId = branchLatestCommit.getName();
if (JGitUtils.isBranchExists(repository, targetBranch)) {
// open 状态下,获取 sourceBranch,targetBranch 冲突文件
conflictFileList = JGitUtils.getConflictFilePathInBranches(repository, sourceBranch, targetBranch);
}
break;
}
case CLOSED:
case MERGED: {
newCommitId = entity.getEndCommitId();
break;
}
}
List<Map<Object, Object>> diffFileListInCommits = JGitUtils.getDiffFileListInCommits(repository, newCommitId, oldCommitId);
List<String> finalConflictFileList = conflictFileList;
diffFileListInCommits.parallelStream()
.forEach(m -> {
T.MapUtil.renameKey(m, "oldContent", "sourceContent");
T.MapUtil.renameKey(m, "newContent", "targetContent");
T.MapUtil.renameKey(m, "oldPath", "sourcePath");
T.MapUtil.renameKey(m, "newPath", "targetPath");
String sourcePath = T.MapUtil.getStr(m, "sourcePath");
String targetPath = T.MapUtil.getStr(m, "targetPath");
m.put("conflict", false);
if (finalConflictFileList.contains(sourcePath) || finalConflictFileList.contains(targetPath)) {
m.put("conflict", true);
}
});
RevCommit sourceCommit = JGitUtils.infoCommit(repository, oldCommitId);
RevCommit targetCommit = JGitUtils.infoCommit(repository, newCommitId);
Map<Object, Object> m = T.MapUtil.builder()
.put("sourceBranch", sourceBranch)
.put("targetBranch", targetBranch)
.put("sourceCommitId", oldCommitId)
.put("targetCommitId", newCommitId)
.put("sourceCommit", JGitUtils.buildAswCommitInfo(sourceCommit))
.put("targetCommit", JGitUtils.buildAswCommitInfo(targetCommit))
.put("files", diffFileListInCommits)
.build();
return m;
} catch (IOException e) {
log.error(e, "[mrCommitDiff] [error] [id: {}]", mrId);
throw new RuntimeException(e);
}
}
@Override
public Map<Object, Object> getMrConflictList(String mrId) throws IOException {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
String sourceBranch = entity.getSourceBranch();
String targetBranch = entity.getTargetBranch();
List<Map<Object, Object>> conflictFileInfoInBranches = JGitUtils.getConflictFileInfoInBranches(repository, sourceBranch, targetBranch);
if (T.CollUtil.isNotEmpty(conflictFileInfoInBranches)) {
// srcBranch->tgtBranch 有冲突,先从 tgtBranch merge 到 srcBranch在 srcBranch 处理后再合并,参考 gitlab
StringBuilder commitMessage = new StringBuilder();
commitMessage.append("Merge branch '").append(targetBranch).append("' into '").append(sourceBranch).append("'\n\n");
commitMessage.append("# Conflicts:\n");
for (Map<Object, Object> map : conflictFileInfoInBranches) {
String path = T.MapUtil.getStr(map, "path");
commitMessage.append("# ").append(path).append("\n");
}
String parentId = JGitUtils.getBranchLatestCommit(repository, sourceBranch).getName();
Map<Object, Object> m = T.MapUtil.builder()
.put("sourceBranch", sourceBranch)
.put("targetBranch", targetBranch)
.put("commitId", parentId)
.put("commitMessage", commitMessage.toString())
.put("files", conflictFileInfoInBranches)
.build();
return m;
}
return T.MapUtil.builder()
.put("sourceBranch", sourceBranch)
.put("targetBranch", targetBranch)
.put("commitId", null)
.put("commitMessage", null)
.put("files", conflictFileInfoInBranches)
.build();
}
}
/**
* resolve mr conflicts
*
* @param mrId
* @param body
*/
@Override
public void resolveMrConflicts(String mrId, Map<String, Object> body) throws IOException {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
String author = loginUserEntity.getName();
String sourceBranch = entity.getSourceBranch();
String targetBranch = entity.getTargetBranch();
String commitMessage = T.MapUtil.getStr(body, "commitMessage");
List<Map<String, String>> files = T.MapUtil.get(body, "files", List.class, new ArrayList());
// 反过来合并
JGitUtils.mergeBranch(repository, targetBranch, sourceBranch, author, commitMessage, files);
} catch (GitAPIException e) {
log.error(e, "[resolveMrConflicts] [error] [id: {}]", mrId);
throw new RuntimeException(e);
}
}
/**
* merge operation
*
* @param mrId
* @param action merge|close
* @return
*/
@Override
public ApplicationMergeEntity mergeMr(String mrId, String action) {
ApplicationMergeEntity entity = this.getById(mrId);
T.VerifyUtil.is(entity).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
String workspaceId = entity.getWorkspaceId();
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
switch (action) {
case "merge": {
MergeRequestStatus mergeRequestStatus = MergeRequestStatus.getInstance(entity.getStatus());
if (!mergeRequestStatus.canMerge()) {
throw new ASWException(RCode.GIT_MERGE_NOT_SUPPORTED.setParam(mergeRequestStatus.toString()));
}
String srcBranch = entity.getSourceBranch();
String tgtBranch = entity.getTargetBranch();
StringBuilder commitMessage = new StringBuilder();
commitMessage.append("Merge branch '").append(srcBranch).append("' into '").append(tgtBranch).append("'\n\n");
commitMessage.append(entity.getTitle());
commitMessage.append("\n");
// 检查 tgtBranch 是否存在
if (T.BooleanUtil.negate(JGitUtils.isBranchExists(repository, tgtBranch))) {
throw new ASWException(RCode.GIT_MERGE_TARGET_BRANCH_NOT_EXIST.setParam(tgtBranch));
}
// merge
try {
SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
JGitUtils.mergeBranch(repository, srcBranch, tgtBranch, loginUserEntity.getName(), commitMessage.toString(), null);
String latestCommitId = JGitUtils.getBranchLatestCommit(repository, srcBranch).getName();
entity.setEndCommitId(latestCommitId);
entity.setStatus(MergeRequestStatus.MERGED.toString());
} catch (Exception e) {
log.error(e, "[mergeMr] [merge error] [workspaceId: {}] [srcBranch: {}] [tgtBranch: {}] [msg: {}]", workspaceId, srcBranch, tgtBranch, e.getMessage());
throw new ASWException(RCode.GIT_MERGE_FAILED.setParam(e.getMessage()));
}
// remove source branch
if (1 == entity.getRemoveSourceBranch()) {
try {
log.info("[mergeMr] [remove source branch] [workspaceId: {}] [branch: {}]", workspaceId, srcBranch);
branchService.deleteBranch(workspaceId, srcBranch);
} catch (Exception e) {
log.error(e, "[mergeMr] [remove source branch error] [workspaceId: {}] [branch: {}] [msg: {}]", workspaceId, srcBranch, e.getMessage());
}
}
// update
this.update(new LambdaUpdateWrapper<ApplicationMergeEntity>()
.eq(ApplicationMergeEntity::getId, mrId)
.set(ApplicationMergeEntity::getEndCommitId, entity.getEndCommitId())
.set(ApplicationMergeEntity::getStatus, entity.getStatus())
.set(ApplicationMergeEntity::getMessage, entity.getMessage())
);
return entity;
}
case "close": {
String updateStatus = StrUtil.equals(MergeRequestStatus.OPEN.toString(), entity.getStatus()) ? MergeRequestStatus.CLOSED.toString() : entity.getStatus();
entity.setStatus(updateStatus);
// 关闭请求,保留当前 src branch last commit = end_commit_id
String latestCommitId = JGitUtils.getBranchLatestCommit(repository, entity.getSourceBranch()).getName();
this.update(new LambdaUpdateWrapper<ApplicationMergeEntity>()
.eq(ApplicationMergeEntity::getId, mrId)
.set(ApplicationMergeEntity::getStatus, updateStatus)
.set(ApplicationMergeEntity::getEndCommitId, latestCommitId)
);
return entity;
}
default:
break;
}
return entity;
} catch (IOException e) {
log.error(e, "[mergeMr] [error] [id: {}] [action: {}]", mrId, action);
throw new RuntimeException(e);
}
}
public static enum MergeRequestStatus {
OPEN {
// 默认状态
public String toString() {
return "open";
}
public boolean canMerge() {
return true;
}
},
MERGED {
// 合并成功
public String toString() {
return "merged";
}
public boolean canMerge() {
return false;
}
},
CLOSED {
// 合并关闭
public String toString() {
return "closed";
}
public boolean canMerge() {
return false;
}
};
private MergeRequestStatus() {
}
public static MergeRequestStatus getInstance(String name) {
for (MergeRequestStatus v : values()) {
if (StrUtil.equalsIgnoreCase(v.name(), name)) {
return v;
}
}
return null;
}
public abstract boolean canMerge();
}
}

View File

@@ -0,0 +1,36 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationNoteDao;
import net.geedge.asw.module.app.entity.ApplicationNoteEntity;
import net.geedge.asw.module.app.service.IApplicationNoteService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ApplicationNoteServiceImpl extends ServiceImpl<ApplicationNoteDao, ApplicationNoteEntity> implements IApplicationNoteService {
@Override
@Transactional(rollbackFor = Exception.class)
public void saveNote(ApplicationNoteEntity note, String applicationId) {
// query last note
ApplicationNoteEntity noteLast = this.getOne(new LambdaQueryWrapper<ApplicationNoteEntity>()
.eq(ApplicationNoteEntity::getApplicationId, applicationId)
.orderByDesc(ApplicationNoteEntity::getOpVersion)
.last("limit 1"));
if (T.ObjectUtil.isNotEmpty(noteLast)){
note.setOpVersion(noteLast.getOpVersion() + 1);
}
//save note
note.setApplicationId(applicationId);
note.setCreateTimestamp(System.currentTimeMillis());
note.setCreateUserId(StpUtil.getLoginIdAsString());
this.save(note);
}
}

View File

@@ -0,0 +1,184 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.config.Query;
import net.geedge.asw.common.util.*;
import net.geedge.asw.module.app.dao.ApplicationReleaseDao;
import net.geedge.asw.module.app.entity.ApplicationReleaseEntity;
import net.geedge.asw.module.app.service.IApplicationReleaseService;
import net.geedge.asw.module.app.util.JGitUtils;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@Service
public class ApplicationReleaseServiceImpl extends ServiceImpl<ApplicationReleaseDao, ApplicationReleaseEntity> implements IApplicationReleaseService {
private final static Log log = Log.get();
@Value("${asw.resources.path:resources}")
private String aswResourcesPath;
@Autowired
private ISysUserService userService;
@Autowired
private IWorkspaceService workspaceService;
@Override
public ApplicationReleaseEntity queryInfo(String id) {
ApplicationReleaseEntity entity = this.getById(id);
T.VerifyUtil.is(entity).notNull(RCode.SYS_RECORD_NOT_FOUND);
SysUserEntity createUser = userService.getById(entity.getCreateUserId());
SysUserEntity updateUser = userService.getById(entity.getUpdateUserId());
createUser.setPwd(null);
updateUser.setPwd(null);
entity.setCreateUser(createUser);
entity.setUpdateUser(updateUser);
File gitDir = workspaceService.getGitDir(entity.getWorkspaceId());
try (Repository repository = JGitUtils.openRepository(gitDir);) {
Ref tagRef = repository.findRef(JGitUtils.getFullTagName(entity.getTagName()));
String tgtCommitId = tagRef.getTarget().getObjectId().getName();
RevCommit revCommit = JGitUtils.infoCommit(repository, tgtCommitId);
entity.setCommit(JGitUtils.buildAswCommitInfo(revCommit));
return entity;
} catch (IOException e) {
log.error(e, "[queryInfo] [id: {}]", id);
throw new RuntimeException(e);
}
}
@Override
public Page queryList(Map<String, Object> params) {
Page page = new Query(ApplicationReleaseEntity.class).getPage(params);
List<ApplicationReleaseEntity> entityList = this.getBaseMapper().queryList(page, params);
page.setRecords(entityList);
return page;
}
@Override
public ApplicationReleaseEntity saveRelease(String workspaceId, String name, String tagName, String description) {
ApplicationReleaseEntity checkTagInUse = this.getOne(new LambdaQueryWrapper<ApplicationReleaseEntity>()
.eq(ApplicationReleaseEntity::getWorkspaceId, workspaceId)
.eq(ApplicationReleaseEntity::getTagName, tagName)
);
if (null != checkTagInUse) {
throw new ASWException(RCode.GIT_TAG_ALREADY_IN_USE);
}
String uuid = T.StrUtil.uuid();
// release file
File releaseFile = FileResourceUtil.createFile(this.aswResourcesPath, workspaceId, Constants.FileTypeEnum.RELEASE.getType(), uuid, uuid + ".zip");
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);) {
Ref tagRef = repository.findRef(JGitUtils.getFullTagName(tagName));
T.VerifyUtil.is(tagRef).notNull(RCode.GIT_TAG_NOT_FOUND.setParam(tagName));
// archive
WorkspaceEntity workspace = workspaceService.getById(workspaceId);
String prefix = T.StrUtil.concat(true, workspace.getName(), "-", tagName, "/");
JGitUtils.archive(repository, tagName, releaseFile, prefix, "zip");
ApplicationReleaseEntity entity = new ApplicationReleaseEntity();
entity.setId(uuid);
entity.setName(name);
entity.setTagName(tagName);
entity.setPath(releaseFile.getAbsolutePath());
entity.setDescription(description);
entity.setWorkspaceId(workspaceId);
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateTimestamp(System.currentTimeMillis());
this.save(entity);
// build record info
SysUserEntity createUser = userService.getById(entity.getCreateUserId());
SysUserEntity updateUser = userService.getById(entity.getUpdateUserId());
createUser.setPwd(null);
updateUser.setPwd(null);
entity.setCreateUser(createUser);
entity.setUpdateUser(updateUser);
String tgtCommitId = tagRef.getTarget().getObjectId().getName();
RevCommit revCommit = JGitUtils.infoCommit(repository, tgtCommitId);
entity.setCommit(JGitUtils.buildAswCommitInfo(revCommit));
return entity;
} catch (IOException | GitAPIException e) {
T.FileUtil.del(releaseFile);
log.error(e, "[saveRelease] [error] [workspaceId: {}] [name: {}] [tagName: {}]", workspaceId, name, tagName);
throw new RuntimeException(e);
}
}
@Override
public ApplicationReleaseEntity updateRelease(String id, String name, String description) {
ApplicationReleaseEntity entity = this.getById(id);
T.VerifyUtil.is(entity).notNull(RCode.SYS_RECORD_NOT_FOUND);
LambdaUpdateWrapper<ApplicationReleaseEntity> wrapper = new LambdaUpdateWrapper<ApplicationReleaseEntity>()
.eq(ApplicationReleaseEntity::getId, id)
.set(ApplicationReleaseEntity::getName, name)
.set(ApplicationReleaseEntity::getDescription, description)
.set(ApplicationReleaseEntity::getUpdateUserId, StpUtil.getLoginIdAsString())
.set(ApplicationReleaseEntity::getUpdateTimestamp, System.currentTimeMillis());
// update
this.update(wrapper);
// return record info
return this.queryInfo(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeRelease(String id) {
ApplicationReleaseEntity entity = this.getById(id);
if (null != entity) {
// del file
T.FileUtil.del(entity.getPath());
// del record
this.removeById(id);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeRelease(String workspaceId, String tagName) {
ApplicationReleaseEntity entity = this.getOne(new LambdaQueryWrapper<ApplicationReleaseEntity>()
.eq(ApplicationReleaseEntity::getWorkspaceId, workspaceId)
.eq(ApplicationReleaseEntity::getTagName, tagName)
);
if (null != entity) {
// del file
T.FileUtil.del(entity.getPath());
// del record
this.removeById(entity.getId());
}
}
}

View File

@@ -1,250 +1,812 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.module.feign.client.KibanaClient;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationDao;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.entity.ApplicationLogEntity;
import net.geedge.asw.module.app.service.IApplicationLogService;
import net.geedge.asw.module.app.service.IApplicationService;
import net.geedge.asw.module.runner.entity.PcapEntity;
import net.geedge.asw.module.runner.service.IPcapService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.app.service.ITSGApplicationService;
import net.geedge.asw.module.app.util.JGitUtils;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.eclipse.jgit.util.io.DisabledOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class ApplicationServiceImpl extends ServiceImpl<ApplicationDao, ApplicationEntity> implements IApplicationService {
public class ApplicationServiceImpl implements IApplicationService {
private static final Log log = Log.get();
@Value("${kibana.url:127.0.0.1:5601}")
private String kibanaUrl;
@Value("${asw.application.template.meta.json}")
private String metaJsonTemplate;
@Value("${asw.application.template.signature.json}")
private String signatureJsonTemplate;
@Autowired
private IApplicationLogService applicationLogService;
private ISysUserService userService;
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IPcapService pcapService;
@Resource
private KibanaClient kibanaClient;
@Qualifier("tsg2402ApplicationService")
private ITSGApplicationService tsg2402ApplicationService;
@Override
public ApplicationEntity queryByApplicationAndLog(String id, String version) {
ApplicationEntity entity = this.baseMapper.queryByApplicationAndLog(id, version);
return entity;
public Map<Object, Object> infoApplication(String workspaceId, String branch, String name) {
Map<Object, Object> result = T.MapUtil.builder()
.put("branch", branch)
.build();
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
ObjectId branchRef = repository.resolve(branch);
result.put("commitId", branchRef.getName());
List<Map<Object, Object>> files = T.ListUtil.list(true);
try (TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository)) {
treeWalk.addTree(revWalk.parseTree(branchRef));
treeWalk.setFilter(PathFilter.create("applications/" + name + "/"));
treeWalk.setRecursive(true);
Map<String, String> appFilePathMapping = T.MapUtil.newHashMap(true);
while (treeWalk.next()) {
String pathString = treeWalk.getPathString();
Map<Object, Object> m = T.MapUtil.builder()
.put("path", pathString)
.build();
Map<Object, Object> fileContent = JGitUtils.getFileContent(repository, pathString, treeWalk.getObjectId(0));
m.putAll(fileContent);
files.add(m);
appFilePathMapping.put(pathString, pathString);
}
Map<String, RevCommit> lastCommitMapping = this.getLastCommitIdDataByPath(repository, branch, appFilePathMapping);
for (Map<Object, Object> m : files) {
String path = T.MapUtil.getStr(m, "path");
RevCommit revCommit = lastCommitMapping.get(path);
m.put("lastCommitId", revCommit != null ? revCommit.getName() : null);
}
}
result.put("files", files);
} catch (IOException e) {
log.error(e, "[infoApplication] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
throw new RuntimeException(e);
}
return result;
}
@Override
public Page queryList(Map<String, Object> params) {
Page page = T.PageUtil.getPage(params);
List<ApplicationEntity> packageList = this.getBaseMapper().queryList(page, params);
page.setRecords(packageList);
return page;
public List<Map<Object, Object>> listApplication(String workspaceId, String branch, String q) {
List<Map<Object, Object>> resultList = T.ListUtil.list(true);
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository);) {
ObjectId branchRef = repository.resolve(branch);
treeWalk.addTree(revWalk.parseTree(branchRef));
treeWalk.setFilter(PathFilter.create("applications/"));
treeWalk.setRecursive(true);
Map<String, String> appDirPathMapping = T.MapUtil.newHashMap(true);
while (treeWalk.next()) {
String filePath = treeWalk.getPathString();
String fileName = treeWalk.getNameString();
if (T.StrUtil.equals("meta.json", fileName)) {
// application_name 从目录中获取
String applicationName = T.PathUtil.getPathEle(Path.of(filePath), 1).toString();
// filter by name
if (T.StrUtil.isNotEmpty(q) && !T.StrUtil.containsIgnoreCase(applicationName, q)) {
continue;
}
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
String metaJsonStr = T.StrUtil.utf8Str(loader.getBytes());
metaJsonStr = T.StrUtil.emptyToDefault(metaJsonStr, T.StrUtil.EMPTY_JSON);
Map metaJsonMap = T.MapUtil.empty();
try {
metaJsonMap = T.JSONUtil.toBean(metaJsonStr, Map.class);
} catch (Exception e) {
log.error(e, "[listApplication] [meat.json format error] [application: {}]", applicationName);
}
Map<Object, Object> m = T.MapUtil.newHashMap(true);
m.putAll(metaJsonMap);
m.put("name", applicationName);
String appDirPath = treeWalk.getPathString().replaceAll(fileName, "");
appDirPathMapping.put(applicationName, appDirPath);
resultList.add(m);
}
}
Map<String, RevCommit> lastCommitMapping = this.getLastCommitIdDataByPath(repository, branch, appDirPathMapping);
for (Map<Object, Object> map : resultList) {
String applicationName = T.MapUtil.getStr(map, "name");
RevCommit lastCommit = T.MapUtil.get(lastCommitMapping, applicationName, RevCommit.class);
map.put("commit", JGitUtils.buildAswCommitInfo(lastCommit));
}
// 按照提交日期倒叙
resultList = resultList.stream().sorted(Comparator.comparing(map -> {
Map commit = T.MapUtil.get((Map) map, "commit", Map.class, new HashMap(2));
return (Long) T.MapUtil.getLong(commit, "createdAt", 0l);
}).reversed()).collect(Collectors.toList());
} catch (IOException e) {
log.error(e, "[listApplication] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
throw new RuntimeException(e);
}
return resultList;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApplicationEntity saveApplication(ApplicationEntity entity) {
ApplicationEntity one = this.getOne(new LambdaQueryWrapper<ApplicationEntity>()
.eq(ApplicationEntity::getWorkspaceId, entity.getWorkspaceId())
.eq(ApplicationEntity::getName, entity.getName()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.APP_DUPLICATE_RECORD).build();
public List<ApplicationEntity> listApplication(String workspaceId, String branch, String... names) {
List<ApplicationEntity> resultList = T.ListUtil.list(true);
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository)) {
ObjectId branchRef = repository.resolve(branch);
treeWalk.addTree(revWalk.parseTree(branchRef));
treeWalk.setFilter(PathFilter.create("applications/"));
treeWalk.setRecursive(true);
Map<String, String> signatureMapping = T.MapUtil.newHashMap();
while (treeWalk.next()) {
String filePath = treeWalk.getPathString();
String applicationName = T.PathUtil.getPathEle(Path.of(filePath), 1).toString();
if (T.BooleanUtil.and(
T.ObjectUtil.isNotEmpty(names),
!Arrays.asList(names).contains(applicationName))) {
continue;
}
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
switch (treeWalk.getNameString()) {
case "meta.json": {
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
String metaJsonStr = T.StrUtil.utf8Str(loader.getBytes());
metaJsonStr = T.StrUtil.emptyToDefault(metaJsonStr, T.StrUtil.EMPTY_JSON);
// save
this.save(entity);
return entity;
ApplicationEntity application = T.JSONUtil.toBean(metaJsonStr, ApplicationEntity.class);
application.setName(applicationName);
resultList.add(application);
break;
}
case "signature.json": {
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
signatureMapping.put(applicationName, T.StrUtil.utf8Str(loader.getBytes()));
break;
}
default:
break;
}
}
for (ApplicationEntity entity : resultList) {
String name = entity.getName();
String signature = signatureMapping.getOrDefault(name, "");
if (T.StrUtil.isNotEmpty(signature)) {
JSONObject jsonObject = T.JSONUtil.parseObj(signature);
entity.setSignature(jsonObject);
}
}
} catch (IOException e) {
log.error(e, "[listApplication] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
throw new RuntimeException(e);
}
return resultList;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApplicationEntity updateApplication(ApplicationEntity entity) {
ApplicationEntity one = this.getOne(new LambdaQueryWrapper<ApplicationEntity>()
.eq(ApplicationEntity::getWorkspaceId, entity.getWorkspaceId())
.eq(ApplicationEntity::getId, entity.getId()));
if (T.ObjectUtil.isNull(one)) {
throw ASWException.builder().rcode(RCode.APP_NOT_EXIST).build();
public void newApplication(String workspaceId, String branch, String name) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
Map<String, ObjectId> filePathAndBlobIdMap = T.MapUtil.newHashMap(true);
for (String filename : T.ListUtil.of("README.md", "meta.json", "signature.json")) {
String fileContent = T.StrUtil.EMPTY;
if ("meta.json".equals(filename)) {
JSONObject jsonObject = T.JSONUtil.parseObj(this.metaJsonTemplate);
jsonObject.set("id", T.StrUtil.uuid());
jsonObject.set("name", name);
jsonObject.set("longName", name);
fileContent = T.JSONUtil.parse(jsonObject).toJSONString(2);
}
if ("signature.json".equals(filename)) {
fileContent = this.signatureJsonTemplate;
}
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
entity.setOpVersion(one.getOpVersion() + 1);
// update
this.updateById(entity);
// save log
this.saveApplcationToLog(one);
return entity;
ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, fileContent.getBytes());
filePathAndBlobIdMap.put(JGitUtils.buildFilePath(name, filename), objectId);
}
private void saveApplcationToLog(ApplicationEntity one) {
ApplicationLogEntity applicationLogEntity = T.BeanUtil.toBean(one, ApplicationLogEntity.class);
applicationLogEntity.setUpdateTimestamp(System.currentTimeMillis());
applicationLogEntity.setUpdateUserId(StpUtil.getLoginIdAsString());
applicationLogEntity.setCreateTimestamp(System.currentTimeMillis());
applicationLogEntity.setCreateUserId(StpUtil.getLoginIdAsString());
applicationLogService.save(applicationLogEntity);
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository)) {
// branch tree
ObjectId resolveId = repository.resolve(branch);
if (null != resolveId) {
treeWalk.addTree(revWalk.parseTree(resolveId));
} else {
treeWalk.addTree(new CanonicalTreeParser());
}
treeWalk.setRecursive(true);
DirCache newTree = DirCache.newInCore();
DirCacheBuilder newTreeBuilder = newTree.builder();
while (treeWalk.next()) {
DirCacheEntry entry = JGitUtils.buildDirCacheEntry(treeWalk.getPathString(), treeWalk.getFileMode(0), treeWalk.getObjectId(0));
newTreeBuilder.add(entry);
}
// update new tree
for (Map.Entry<String, ObjectId> entry : filePathAndBlobIdMap.entrySet()) {
String filePath = entry.getKey();
ObjectId blobId = entry.getValue();
// add file ref
DirCacheEntry dirCacheEntry = JGitUtils.buildDirCacheEntry(filePath, FileMode.REGULAR_FILE, blobId);
newTreeBuilder.add(dirCacheEntry);
}
newTreeBuilder.finish();
ObjectId newTreeId = newTree.writeTree(inserter);
String message = String.format("feat: add %s application", name);
SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
// commit
JGitUtils.createCommit(repository, branch, newTreeId, message, personIdent);
}
} catch (IOException | ConcurrentRefUpdateException e) {
log.error(e, "[newApplication] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
throw new RuntimeException(e);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeApplication(List<String> ids) {
// remove
this.removeBatchByIds(ids);
applicationLogService.removeBatchByIds(ids);
public void renameApplication(String workspaceId, String branch, String oldName, String newName) {
log.info("[applicationName] [begin] [workspaceId: {}] [branch: {}] [oldName: {}] [newName: {}]", workspaceId, branch, oldName, newName);
if (T.StrUtil.equals(oldName, newName)) {
log.warn("[renameApplication] [newName has not changed]");
return;
}
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository)) {
ObjectId branchRef = repository.resolve(branch);
treeWalk.addTree(revWalk.parseTree(branchRef));
treeWalk.setRecursive(true);
DirCache newTree = DirCache.newInCore();
DirCacheBuilder newTreeBuilder = newTree.builder();
boolean isCommit = false;
String prefix = JGitUtils.buildFilePrefix(oldName);
while (treeWalk.next()) {
String pathString = treeWalk.getPathString();
// rename file
if (pathString.startsWith(prefix)) {
isCommit = true;
String filename = treeWalk.getNameString();
String savePath = JGitUtils.buildFilePath(newName, filename);
if (T.StrUtil.equals("meta.json", filename)) {
// 更新 meta.json name,longName 的值
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
String metaJsonStr = T.StrUtil.utf8Str(loader.getBytes());
JSONObject jsonObject = T.JSONUtil.parseObj(metaJsonStr);
jsonObject.set("name", newName);
jsonObject.set("longName", newName);
String content = T.JSONUtil.parse(jsonObject).toJSONString(2);
ObjectId blobId = JGitUtils.insertBlobFileToDatabase(repository, content.getBytes());
DirCacheEntry dirCacheEntry = JGitUtils.buildDirCacheEntry(savePath, treeWalk.getFileMode(0), blobId);
newTreeBuilder.add(dirCacheEntry);
} else {
DirCacheEntry dirCacheEntry = JGitUtils.buildDirCacheEntry(savePath, treeWalk.getFileMode(0), treeWalk.getObjectId(0));
newTreeBuilder.add(dirCacheEntry);
}
} else {
// other file
DirCacheEntry entry = JGitUtils.buildDirCacheEntry(pathString, treeWalk.getFileMode(0), treeWalk.getObjectId(0));
newTreeBuilder.add(entry);
}
}
newTreeBuilder.finish();
if (isCommit) {
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter()) {
ObjectId newTreeId = newTree.writeTree(inserter);
String message = String.format("chore: rename application %s to %s", oldName, newName);
SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
JGitUtils.createCommit(repository, branch, newTreeId, message, personIdent);
}
} else {
log.warn("[renameApplication] [old application not found] [name: {}]", oldName);
}
} catch (IOException | ConcurrentRefUpdateException e) {
log.error(e, "[renameApplication] [error] [workspaceId: {}] [branch: {}] [oldName: {}] [newName: {}]", workspaceId, branch, oldName, newName);
throw new RuntimeException(e);
}
}
@Override
public List<ApplicationEntity> queryLogList(String id) {
List<ApplicationEntity> packageList = this.getBaseMapper().queryLogList(id);
return packageList;
public void deleteApplication(String workspaceId, String branch, String name) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
ObjectInserter inserter = repository.getObjectDatabase().newInserter();
TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository);) {
ObjectId branchRef = repository.resolve(branch);
treeWalk.addTree(revWalk.parseTree(branchRef));
treeWalk.setRecursive(true);
DirCache newTree = DirCache.newInCore();
DirCacheBuilder newTreeBuilder = newTree.builder();
String appFilePrefixStr = JGitUtils.buildFilePrefix(name);
while (treeWalk.next()) {
String pathString = treeWalk.getPathString();
if (!pathString.startsWith(appFilePrefixStr)) {
DirCacheEntry entry = JGitUtils.buildDirCacheEntry(pathString, treeWalk.getFileMode(0), treeWalk.getObjectId(0));
newTreeBuilder.add(entry);
}
}
newTreeBuilder.finish();
ObjectId newTreeId = newTree.writeTree(inserter);
String message = String.format("refactor: remove %s application", name);
SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
// commit
JGitUtils.createCommit(repository, branch, newTreeId, message, personIdent);
} catch (IOException | ConcurrentRefUpdateException e) {
log.error(e, "[deleteApplication] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
throw new RuntimeException(e);
}
}
@Override
public List<ApplicationEntity> compare(String id, String oldVersion, String newVersion) {
Map<String, Object> params = Map.of("id", id, "versions", Arrays.asList(oldVersion, newVersion));
List<ApplicationEntity> packageList = this.getBaseMapper().compare(params);
return packageList;
public void updateApplication(String workspaceId, String branch, String lastCommitId, String message, List<Map<String, String>> updateContent) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
Git git = Git.open(repository.getDirectory());) {
// 分支最新 commitId
ObjectId branchRef = repository.resolve(branch);
// 如果不相等,检查 param.lastCommitId --- branch.HEAD 期间是否更新了本次修改文件
if (!T.StrUtil.equals(lastCommitId, branchRef.getName())) {
List<String> updateFilePath = updateContent.stream()
.filter(map -> {
String action = T.MapUtil.getStr(map, "action");
return T.StrUtil.equalsAny(action, "create", "update");
})
.map(map -> T.MapUtil.getStr(map, "path"))
.collect(Collectors.toList());
ObjectId currentCommitId = repository.resolve(lastCommitId);
try (RevWalk walk = new RevWalk(repository)) {
CanonicalTreeParser c1 = new CanonicalTreeParser();
c1.reset(repository.newObjectReader(), walk.parseCommit(currentCommitId).getTree());
CanonicalTreeParser c2 = new CanonicalTreeParser();
c2.reset(repository.newObjectReader(), walk.parseCommit(branchRef).getTree());
List<DiffEntry> diffs = git.diff()
.setOldTree(c1)
.setNewTree(c2)
.call();
List<String> affectFilePathList = diffs.stream()
.filter(entry -> T.StrUtil.equalsAnyIgnoreCase(entry.getChangeType().name(), DiffEntry.ChangeType.MODIFY.name(), DiffEntry.ChangeType.ADD.name()))
.map(DiffEntry::getNewPath)
.collect(Collectors.toList());
List<String> intersection = T.CollUtil.intersection(updateFilePath, affectFilePathList).stream().toList();
if (T.CollUtil.isEmpty(intersection)) {
// 在 param.lastCommitId 不是当前分支最新提交时,本次提交文件没有冲突,更新 commit=branch.HEAD
lastCommitId = branchRef.getName();
} else {
throw new ASWException(RCode.GIT_COMMIT_CONFLICT_ERROR);
}
}
}
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository)) {
// branch tree
treeWalk.addTree(revWalk.parseTree(branchRef));
treeWalk.setRecursive(true);
DirCache newTree = DirCache.newInCore();
DirCacheBuilder newTreeBuilder = newTree.builder();
List<String> updateFilePath = updateContent.stream().map(map -> T.MapUtil.getStr(map, "path")).collect(Collectors.toList());
while (treeWalk.next()) {
String pathString = treeWalk.getPathString();
// 先删
if (!updateFilePath.contains(pathString)) {
DirCacheEntry entry = JGitUtils.buildDirCacheEntry(pathString, treeWalk.getFileMode(0), treeWalk.getObjectId(0));
newTreeBuilder.add(entry);
}
}
// 后增
for (Map<String, String> map : updateContent) {
String action = T.MapUtil.getStr(map, "action");
if (T.StrUtil.equalsAnyIgnoreCase(action, "create", "update")) {
String path = T.MapUtil.getStr(map, "path");
DirCacheEntry dirCacheEntry = new DirCacheEntry(path);
dirCacheEntry.setFileMode(FileMode.REGULAR_FILE);
String content = T.MapUtil.getStr(map, "content");
String encoding = T.MapUtil.getStr(map, "encoding");
if ("base64".equals(encoding)) {
// binary
byte[] data = Base64.getDecoder().decode(content);
ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, data);
dirCacheEntry.setObjectId(objectId);
} else {
// text
ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, content.getBytes());
dirCacheEntry.setObjectId(objectId);
}
newTreeBuilder.add(dirCacheEntry);
}
}
newTreeBuilder.finish();
ObjectId newTreeId = newTree.writeTree(inserter);
SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
// commit
JGitUtils.createCommit(repository, branch, newTreeId, message, personIdent);
}
} catch (IOException | GitAPIException e) {
log.error(e, "[updateApplication] [error] [workspaceId: {}] [branch: {}] [lastCommitId: {}]", workspaceId, branch, lastCommitId);
throw new RuntimeException(e);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void restore(String id, String version) {
// save current to log
ApplicationEntity curApplication = this.getById(id);
this.saveApplcationToLog(curApplication);
// restore
ApplicationLogEntity oldApplication = applicationLogService.getOne(new LambdaQueryWrapper<ApplicationLogEntity>()
.eq(ApplicationLogEntity::getId, id)
.eq(ApplicationLogEntity::getOpVersion, version));
public List<Map<Object, Object>> listApplicationCommit(String workspaceId, String branch, String name, String file) {
List<Map<Object, Object>> resultList = T.ListUtil.list(true);
oldApplication.setUpdateTimestamp(System.currentTimeMillis());
oldApplication.setUpdateUserId(StpUtil.getLoginIdAsString());
oldApplication.setOpVersion(curApplication.getOpVersion() + 1);
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
Git git = Git.open(repository.getDirectory());) {
ApplicationEntity application = T.BeanUtil.toBean(oldApplication, ApplicationEntity.class);
this.updateById(application);
String filterPath = T.StrUtil.concat(true, "applications/", name);
if (T.StrUtil.isNotEmpty(file)) {
filterPath = T.StrUtil.concat(true, filterPath, "/", file);
}
ObjectId branchRef = git.getRepository().resolve(branch);
Iterable<RevCommit> iterable = git.log()
.add(branchRef)
.addPath(filterPath)
.call();
for (RevCommit commit : iterable) {
resultList.add(JGitUtils.buildAswCommitInfo(commit));
}
} catch (IOException | GitAPIException e) {
log.error(e, "[listApplicationCommit] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
throw new RuntimeException(e);
}
return resultList;
}
@Override
public Map<Object, Object> infoApplicationFileContent(String workspaceId, String branch, String name, String commitId, String file) {
// applications/qq/meta.json
String path = JGitUtils.buildFilePath(name, file);
Map<Object, Object> result = T.MapUtil.builder()
.put("path", path)
.build();
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
TreeWalk treeWalk = new TreeWalk(repository);
RevWalk revWalk = new RevWalk(repository);
) {
RevCommit revCommit = revWalk.parseCommit(ObjectId.fromString(commitId));
ObjectId treeId = revCommit.getTree().getId();
treeWalk.addTree(treeId);
treeWalk.setRecursive(true);
while (treeWalk.next()) {
if (T.StrUtil.equals(path, treeWalk.getPathString())) {
Map<Object, Object> fileContent = JGitUtils.getFileContent(repository, treeWalk.getPathString(), treeWalk.getObjectId(0));
result.putAll(fileContent);
}
}
} catch (IOException e) {
log.error(e, "[infoApplicationFileContent] [error] [workspaceId: {}] [branch: {}] [name: {}]", workspaceId, branch, name);
throw new RuntimeException(e);
}
return result;
}
/**
* 1. 根据 workspace_name 查询 index-pattern 是否存在
* 2. 不存在则创建索引
* 通过 path 获取 lastCommit
*
* 维护格式示例:
* {
* "type": "index-pattern",
* "id": "workspace_id",
* "attributes": {
* "title": "workspace-{workspace_name}-*"
* }
* }
* @param workspaceId
* @param pcapIds
* @param response
* @throws IOException
* @param repository
* @param branch
* @param pathMapping
* @return
*/
public Map<String, RevCommit> getLastCommitIdDataByPath(Repository repository, String branch, Map<String, String> pathMapping) {
Map<String, RevCommit> result = new HashMap<>();
try (Git git = new Git(repository);
DiffFormatter diffFormatter = new DiffFormatter(DisabledOutputStream.INSTANCE);
) {
diffFormatter.setRepository(repository);
// 指定分支获取 log
Iterable<RevCommit> commits = git.log()
.add(git.getRepository().resolve(branch))
.call();
for (RevCommit commit : commits) {
// 获取上次提交,用以对比差异
RevCommit parent = commit.getParentCount() > 0 ? commit.getParent(0) : null;
if (parent == null) {
continue;
}
// 计算当前提交与上次提交之间的差异
List<DiffEntry> diffs = diffFormatter.scan(parent.getTree(), commit.getTree());
for (DiffEntry diff : diffs) {
String newPath = diff.getNewPath();
String oldPath = diff.getOldPath();
// 检查是否匹配目标路径
for (Map.Entry<String, String> entry : pathMapping.entrySet()) {
String key = entry.getKey();
String path = entry.getValue();
if (T.BooleanUtil.and(
(newPath.startsWith(path) || oldPath.startsWith(path)),
!result.containsKey(key)
)) {
result.put(key, commit);
}
}
}
// 如果所有路径都找到对应的最后提交,提前结束循环
if (result.size() == pathMapping.size()) {
break;
}
}
} catch (IOException | GitAPIException e) {
log.error(e, "[getLastCommitIdDataByPath] [workspace: {}] [branch: {}]", repository.getDirectory().getPath(), branch);
}
return result;
}
public void saveOrUpdateBatch(String workspaceId, String branch, String commitMessage, List<ApplicationEntity> list) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
RevWalk revWalk = new RevWalk(repository)) {
ObjectId branchRef = repository.resolve(branch);
RevTree revTree = revWalk.parseTree(branchRef);
// 先查询,区分 新增APP、修改APP
List<ApplicationEntity> addAppList = T.ListUtil.list(true);
List<ApplicationEntity> updateAppList = T.ListUtil.list(true);
HashMap<String, JSONObject> appMetaContentInDb = T.MapUtil.newHashMap();
try (TreeWalk treeWalk = new TreeWalk(repository)) {
treeWalk.addTree(revTree);
treeWalk.setRecursive(true);
treeWalk.setFilter(PathFilter.create("applications/"));
HashSet<String> appNameListInDb = new HashSet<>();
while (treeWalk.next()) {
String appName = T.PathUtil.getPathEle(Path.of(treeWalk.getPathString()), 1).toString();
appNameListInDb.add(appName);
if (T.StrUtil.equals("meta.json", treeWalk.getNameString())) {
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
JSONObject jsonObject = T.JSONUtil.parseObj(T.StrUtil.utf8Str(loader.getBytes()));
appMetaContentInDb.put(appName, jsonObject);
}
}
list.parallelStream().forEach(entity -> {
if (appNameListInDb.contains(entity.getName())) {
updateAppList.add(entity);
} else {
addAppList.add(entity);
}
});
}
// 修改文件路径信息
List<String> updateFilePath = T.ListUtil.list(true);
updateAppList.parallelStream().forEach(entity -> {
updateFilePath.add(JGitUtils.buildFilePath(entity.getName(), "meta.json"));
updateFilePath.add(JGitUtils.buildFilePath(entity.getName(), "signature.json"));
});
// build tree
DirCache newTree = DirCache.newInCore();
DirCacheBuilder newTreeBuilder = newTree.builder();
try (TreeWalk treeWalk = new TreeWalk(repository)) {
treeWalk.addTree(revTree);
treeWalk.setRecursive(true);
while (treeWalk.next()) {
// 先删
String filePath = treeWalk.getPathString();
if (!updateFilePath.contains(filePath)) {
DirCacheEntry entry = new DirCacheEntry(filePath);
entry.setFileMode(treeWalk.getFileMode(0));
entry.setObjectId(treeWalk.getObjectId(0));
newTreeBuilder.add(entry);
}
}
// 新增APP
for (ApplicationEntity entity : addAppList) {
for (String fileName : T.ListUtil.of("README.md", "meta.json", "signature.json")) {
String fileContent = T.StrUtil.EMPTY;
if ("meta.json".equals(fileName)) {
JSONObject tempJSONObject = T.JSONUtil.parseObj(entity);
tempJSONObject.remove("signature");
fileContent = T.JSONUtil.parse(tempJSONObject).toJSONString(2);
}
if ("signature.json".equals(fileName)) {
String jsonStr = T.JSONUtil.toJsonStr(entity.getSignature());
JSONObject tempJSONObject = T.JSONUtil.parseObj(jsonStr);
fileContent = T.JSONUtil.parse(tempJSONObject).toJSONString(2);
}
// save
String filePath = JGitUtils.buildFilePath(entity.getName(), fileName);
DirCacheEntry dirCacheEntry = new DirCacheEntry(filePath);
dirCacheEntry.setFileMode(FileMode.REGULAR_FILE);
ObjectId blobId = JGitUtils.insertBlobFileToDatabase(repository, fileContent.getBytes());
dirCacheEntry.setObjectId(blobId);
newTreeBuilder.add(dirCacheEntry);
}
}
// 修改APP
for (ApplicationEntity entity : updateAppList) {
// meta.json
JSONObject jsonObject = T.JSONUtil.parseObj(entity);
jsonObject.remove("signature");
JSONObject metaInDb = appMetaContentInDb.get(entity.getName());
if (null != metaInDb) {
// 还原 asw 部分属性值,此部分 tsg app 不记录
jsonObject.set("id", metaInDb.getStr("id"));
jsonObject.set("developer", metaInDb.getStr("developer", ""));
jsonObject.set("website", metaInDb.getStr("website", ""));
jsonObject.set("packageName", metaInDb.getObj("packageName"));
}
String fileContent = T.JSONUtil.parse(jsonObject).toJSONString(2);
ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, fileContent.getBytes());
DirCacheEntry dirCacheEntry = JGitUtils.buildDirCacheEntry(JGitUtils.buildFilePath(entity.getName(), "meta.json"), FileMode.REGULAR_FILE, objectId);
newTreeBuilder.add(dirCacheEntry);
// signature.json
String jsonStr = T.JSONUtil.toJsonStr(entity.getSignature());
JSONObject jsonObject2 = T.JSONUtil.parseObj(jsonStr);
String fileContent2 = T.JSONUtil.parse(jsonObject2).toJSONString(2);
ObjectId objectId2 = JGitUtils.insertBlobFileToDatabase(repository, fileContent2.getBytes());
DirCacheEntry dirCacheEntry2 = JGitUtils.buildDirCacheEntry(JGitUtils.buildFilePath(entity.getName(), "signature.json"), FileMode.REGULAR_FILE, objectId2);
newTreeBuilder.add(dirCacheEntry2);
}
newTreeBuilder.finish();
RevCommit commitId = revWalk.parseCommit(branchRef);
SysUserEntity loginUserEntity = userService.getById(StpUtil.getLoginIdAsString());
PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUserEntity.getName());
boolean success = JGitUtils.commitIndex(repository, branch, newTree, commitId, null, commitMessage, personIdent);
log.info("[saveOrUpdateBatch] [workspaceId: {}] [branch: {}] [commitId: {}] [success: {}]", workspaceId, branch, commitId, success);
}
} catch (IOException | ConcurrentRefUpdateException e) {
log.error(e, "[saveOrUpdateBatch] [error] [workspaceId: {}] [branch: {}]", workspaceId, branch);
throw new RuntimeException(e);
}
}
@Override
public void redirectDiscoverPage(String workspaceId, String pcapIds, HttpServletResponse response) throws IOException {
// verify
WorkspaceEntity workspace = workspaceService.getById(workspaceId);
T.VerifyUtil.is(workspace).notNull(RCode.SYS_RECORD_NOT_FOUND);
List<String> pcapIdList = T.StrUtil.split(pcapIds, ",").stream().filter(s -> T.StrUtil.isNotEmpty(s)).collect(Collectors.toList());
List<PcapEntity> pcapList = pcapService.list(new LambdaQueryWrapper<PcapEntity>().in(PcapEntity::getId, pcapIdList));
T.VerifyUtil.is(pcapList).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
// index name
String indexName = String.format("workspace-%s-*", workspace.getName());
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
String token = tokenInfo.getTokenValue();
JSONObject index = kibanaClient.findIndexPattern(token, indexName);
JSONArray savedObjects = index.getJSONArray("saved_objects");
// check if index exists
boolean indexExists = savedObjects.stream()
.filter(obj -> {
JSONObject attributes = ((JSONObject) obj).getJSONObject("attributes");
if (T.ObjectUtil.isEmpty(attributes)) return false;
String title = attributes.getString("title");
return T.StrUtil.equals(indexName, title);
})
.findFirst()
.isPresent();
if (log.isDebugEnabled()) {
log.debug("[redirectDiscoverPage] [idnex-pattern: {}] [exists: {}]", indexName, indexExists);
public byte[] exportAppByFormat(List<ApplicationEntity> appList, String format) {
try {
switch (format) {
case "tsg2402": {
Map<Object, Object> m = tsg2402ApplicationService.aswToTsg2402(appList);
JSON json = new JSONObject(m, JSONConfig.create().setIgnoreNullValue(false).setKeyComparator(String::compareToIgnoreCase));
return T.StrUtil.bytes(json.toJSONString(0));
}
default:
break;
}
return new byte[]{};
} catch (Exception e) {
log.error(e, "[exportAppByFormat] [error] [format: {}] [application: {}]", format, T.JSONUtil.toJsonStr(appList));
throw new ASWException(RCode.ERROR);
}
}
// create index
if (T.BooleanUtil.negate(indexExists)) {
JSONObject attributes = new JSONObject();
attributes.put("title", indexName);
@Override
public List<ApplicationEntity> importAppByFormat(String workspaceId, String branch, String format, List<JSONObject> dataList) {
try {
switch (format) {
case "tsg2402": {
List<ApplicationEntity> records = tsg2402ApplicationService.tsg2402ToAsw(workspaceId, dataList);
JSONObject body = new JSONObject();
body.put("attributes", attributes);
kibanaClient.saveIndexPattern(token, workspaceId, body);
// commit
String commitMessage = "feat: import application data from TSG system (version 2402)";
this.saveOrUpdateBatch(workspaceId, branch, commitMessage, records);
return records;
}
default:
break;
}
return new ArrayList<>();
} catch (Exception e) {
log.error(e, "[importAppByFormat] [error] [workspaceId: {}] [format: {}]", workspaceId, format);
throw new ASWException(RCode.ERROR);
}
}
// build url
String baseUrl = UrlBuilder.ofHttp(kibanaUrl)
.addPath("/app/data-explorer/discover")
.addQuery("jwt", token)
.toString();
// build query param
String param1 = String.format("_a=(discover:(columns:!(_source),isDirty:!f,sort:!()),metadata:(indexPattern:'%s',view:discover))", workspaceId);
String param2 = "_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))";
String source = pcapList.stream()
.map(PcapEntity::getName)
.map(fileName -> "\"" + fileName + "\"")
.collect(Collectors.joining("|", "source: (", ")"));
String param3 = String.format("_q=(filters:!(),query:(language:lucene,query:'%s'))", source);
String query = String.format("?%s&%s&%s", param1, param2, param3);
String redirectUrl = baseUrl + "#" + query;
if(log.isDebugEnabled()){
log.debug("[redirectDiscoverPage] [url: {}]", redirectUrl);
}
// redirect
response.sendRedirect(redirectUrl);
}
}

View File

@@ -0,0 +1,85 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationSignatureDao;
import net.geedge.asw.module.app.entity.ApplicationSignatureEntity;
import net.geedge.asw.module.app.service.IApplicationSignatureService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Service
public class ApplicationSignatureServiceImpl extends ServiceImpl<ApplicationSignatureDao, ApplicationSignatureEntity> implements IApplicationSignatureService {
@Override
@Transactional(rollbackFor = Exception.class)
public void saveSignature(ApplicationSignatureEntity signature, String applicationId) {
// query last note
ApplicationSignatureEntity signatureLast = this.queryLastVersionSignatureByAppId(applicationId);
if (T.ObjectUtil.isNotEmpty(signatureLast)){
signature.setOpVersion(signatureLast.getOpVersion() + 1);
}
// save signature
signature.setApplicationId(applicationId);
signature.setCreateTimestamp(System.currentTimeMillis());
signature.setCreateUserId(StpUtil.getLoginIdAsString());
this.save(signature);
}
@Override
public List<ApplicationSignatureEntity> queryList(String applicationId) {
Map<Object, Object> params = T.MapUtil.builder().put("applicationId", applicationId).build();
List<ApplicationSignatureEntity> list = this.getBaseMapper().queryList(params);
return list;
}
@Override
public List<ApplicationSignatureEntity> compare(String applicationId, String oldVersion, String newVersion) {
List<String> versionList = Arrays.asList(oldVersion, newVersion);
Map<Object, Object> params = T.MapUtil.builder()
.put("applicationId", applicationId)
.put("versions", versionList)
.build();
List<ApplicationSignatureEntity> list = this.getBaseMapper().queryList(params);
return list;
}
@Override
public void restore(String applicationId, String version) {
ApplicationSignatureEntity signature = this.getOne(new LambdaQueryWrapper<ApplicationSignatureEntity>()
.eq(ApplicationSignatureEntity::getApplicationId, applicationId)
.eq(ApplicationSignatureEntity::getOpVersion, version));
ApplicationSignatureEntity lastSignature = this.queryLastVersionSignatureByAppId(applicationId);
if (T.ObjectUtil.isEmpty(signature)) {
throw ASWException.builder().rcode(RCode.APP_SIGNATURE_NOT_EXIST).build();
}
// restore
signature.setId(null);
signature.setOpVersion(lastSignature.getOpVersion() + 1);
this.save(signature);
}
@Override
public ApplicationSignatureEntity queryLastVersionSignatureByAppId(String applicationId) {
ApplicationSignatureEntity entity = this.getOne(new LambdaQueryWrapper<ApplicationSignatureEntity>()
.eq(ApplicationSignatureEntity::getApplicationId, applicationId)
.orderByDesc(ApplicationSignatureEntity::getOpVersion)
.last("limit 1"));
return entity;
}
}

View File

@@ -0,0 +1,156 @@
package net.geedge.asw.module.app.service.impl;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationMergeEntity;
import net.geedge.asw.module.app.service.IBranchService;
import net.geedge.asw.module.app.service.IApplicationMergeService;
import net.geedge.asw.module.app.service.impl.ApplicationMergeServiceImpl.MergeRequestStatus;
import net.geedge.asw.module.app.util.JGitUtils;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@Service
public class BranchServiceImpl implements IBranchService {
private final static Log log = Log.get();
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IApplicationMergeService applicationMergeService;
@Override
public List<Map<Object, Object>> listBranch(String workspaceId, String search) {
List<Map<Object, Object>> resultList = T.ListUtil.list(true);
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
Git git = Git.open(repository.getDirectory())) {
String fullBranch = repository.getFullBranch();
String defaultBranch = "main";
if (fullBranch != null && fullBranch.startsWith(JGitUtils.R_HEADS)) {
defaultBranch = fullBranch.substring(JGitUtils.R_HEADS.length());
}
// 默认行为,进查询本地分支
List<Ref> call = git.branchList().call();
RevWalk revCommits = new RevWalk(repository);
for (Ref ref : call) {
String branchName = ref.getName();
// 返回时去掉前缀
branchName = branchName.replaceAll(JGitUtils.R_HEADS, "");
if (T.StrUtil.isNotEmpty(search)) {
if (!T.StrUtil.contains(branchName, search)) {
continue;
}
}
Map<Object, Object> m = T.MapUtil.builder()
.put("name", branchName)
.put("default", T.StrUtil.equals(defaultBranch, branchName))
.build();
RevCommit commit = revCommits.parseCommit(ref.getObjectId());
m.put("commit", JGitUtils.buildAswCommitInfo(commit));
resultList.add(m);
}
revCommits.close();
revCommits.dispose();
} catch (GitAPIException | IOException e) {
log.error(e, "[listBranch] [error] [workspaceId: {}]", workspaceId);
throw new RuntimeException(e);
}
return resultList;
}
@Override
public Map<Object, Object> infoBranch(String workspaceId, String branchName) {
List<Map<Object, Object>> listBranch = this.listBranch(workspaceId, branchName);
// 分支不存在
if (T.CollUtil.isEmpty(listBranch)) {
throw new ASWException(RCode.SYS_RECORD_NOT_FOUND);
}
return T.CollUtil.getFirst(listBranch);
}
@Override
public Map<Object, Object> newBranch(String workspaceId, String name, String ref) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
Git git = Git.open(repository.getDirectory())) {
git.branchCreate()
.setName(name)
.setStartPoint(ref)
.call();
return this.infoBranch(workspaceId, name);
} catch (GitAPIException | IOException e) {
log.error(e, "[newBranch] [error] [workspaceId: {}] [branch: {}] [ref: {}]", workspaceId, name, ref);
throw new RuntimeException(e);
}
}
@Override
public void deleteBranch(String workspaceId, String branchName) {
log.info("[deleteBranch] [begin] [workspaceId: {}] [branch: {}]", workspaceId, branchName);
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
Git git = Git.open(repository.getDirectory())) {
git.branchDelete()
.setBranchNames(branchName)
.setForce(true)
.call();
// OPEN 状态mr 源分支被删除mr 记录直接删掉
applicationMergeService.remove(new LambdaQueryWrapper<ApplicationMergeEntity>()
.eq(ApplicationMergeEntity::getWorkspaceId, workspaceId)
.eq(ApplicationMergeEntity::getSourceBranch, branchName)
.eq(ApplicationMergeEntity::getStatus, MergeRequestStatus.OPEN.toString())
);
} catch (GitAPIException | IOException e) {
log.error(e, "[deleteBranch] [error] [workspaceId: {}] [branchName: {}]", workspaceId, branchName);
throw new RuntimeException(e);
}
}
@Override
public List<Map<Object, Object>> listBranchCommit(String workspaceId, String branch, String path) {
List<Map<Object, Object>> resultList = T.ListUtil.list(true);
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir)) {
List<RevCommit> branchCommitList = JGitUtils.getBranchCommitList(repository, branch, path);
branchCommitList.forEach(revCommit -> resultList.add(JGitUtils.buildAswCommitInfo(revCommit)));
} catch (GitAPIException | IOException e) {
log.error(e, "[listBranchCommit] [error] [workspaceId: {}] [branch: {}] [path: {}]", workspaceId, branch, path);
throw new RuntimeException(e);
}
return resultList;
}
}

View File

@@ -1,33 +1,74 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.common.config.Query;
import net.geedge.asw.common.util.*;
import net.geedge.asw.module.app.dao.PackageDao;
import net.geedge.asw.module.app.entity.PackageEntity;
import net.geedge.asw.module.app.service.IPackageService;
import net.geedge.asw.module.app.util.ApkInfo;
import net.geedge.asw.module.app.util.ApkUtil;
import net.geedge.asw.module.app.util.PkgConstant;
import net.geedge.asw.module.job.entity.JobEntity;
import net.geedge.asw.module.job.service.IJobService;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workbook.service.IWorkbookResourceService;
import net.geedge.asw.module.workbook.util.WorkbookConstant;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
@Service
public class PackageServiceImpl extends ServiceImpl<PackageDao, PackageEntity> implements IPackageService {
private final static Log log = Log.get();
@Autowired
private ISysUserService sysUserService;
@Autowired
private IWorkbookResourceService workbookResourceService;
@Autowired
private IJobService jobService;
@Value("${asw.resources.path:resources}")
private String resources;
@Override
public PackageEntity queryInfo(String id) {
PackageEntity entity = this.getById(id);
T.VerifyUtil.is(entity).notNull(RCode.SYS_RECORD_NOT_FOUND);
// user
SysUserEntity createUser = sysUserService.getById(entity.getCreateUserId());
SysUserEntity updateUser = sysUserService.getById(entity.getUpdateUserId());
createUser.setPwd(null);
updateUser.setPwd(null);
entity.setCreateUser(createUser);
entity.setUpdateUser(updateUser);
return entity;
}
@Override
public Page queryList(Map<String, Object> params) {
Page page = T.PageUtil.getPage(params);
Page page = new Query(PackageEntity.class).getPage(params);
List<PackageEntity> packageList = this.getBaseMapper().queryList(page, params);
page.setRecords(packageList);
return page;
@@ -35,56 +76,93 @@ public class PackageServiceImpl extends ServiceImpl<PackageDao, PackageEntity> i
@Override
@Transactional(rollbackFor = Exception.class)
public PackageEntity savePackage(PackageEntity entity) {
PackageEntity one = this.getOne(new LambdaQueryWrapper<PackageEntity>()
.eq(PackageEntity::getWorkspaceId, entity.getWorkspaceId())
.eq(PackageEntity::getName, entity.getName()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
public PackageEntity savePackage(String workspaceId, String description, Resource fileResource) {
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateTimestamp(System.currentTimeMillis());
String pkgId = T.StrUtil.uuid();
String filename = fileResource.getFilename();
String suffix = T.FileUtil.extName(filename);
suffix = T.StrUtil.emptyToDefault(suffix, "apk");
if (!Constants.ANDROID_PACKAGE_TYPE_LIST.contains(suffix)) {
throw new ASWException(RCode.PACKAGE_FILE_TYPE_ERROR);
}
String saveFileName = pkgId + "." + suffix;
File destination = FileResourceUtil.createFile(resources, workspaceId, Constants.FileTypeEnum.PACKAGE.getType(), pkgId, saveFileName);
PackageEntity entity = new PackageEntity();
ApkUtil apkUtil = new ApkUtil();
apkUtil.setAaptToolPath(Path.of(T.WebPathUtil.getRootPath(), "lib", "aapt").toString());
try {
FileUtils.copyInputStreamToFile(fileResource.getInputStream(), destination);
if (suffix.equals("apk")) {
// parse
ApkInfo apkInfo = apkUtil.parseApk(destination.getPath());
if (T.ObjectUtil.isNull(apkInfo)) {
throw new ASWException(RCode.PACKAGE_FILE_TYPE_ERROR);
}
entity.setName(apkInfo.getLabel());
entity.setVersion(apkInfo.getVersionName());
entity.setIdentifier(apkInfo.getPackageName());
} else {
ApkInfo apkInfo = apkUtil.parseXapk(destination.getPath());
if (T.ObjectUtil.isNull(apkInfo)) {
throw new ASWException(RCode.PACKAGE_FILE_TYPE_ERROR);
}
entity.setName(apkInfo.getLabel());
entity.setVersion(apkInfo.getVersionName());
entity.setIdentifier(apkInfo.getPackageName());
}
} catch (Exception e) {
log.error(e, "[savePackage] [save package error] [file: {}]", fileResource.getFilename());
FileUtil.del(destination);
throw new ASWException(RCode.PACKAGE_FILE_TYPE_ERROR);
}
entity.setId(pkgId);
entity.setDescription(T.StrUtil.emptyToDefault(description, ""));
entity.setPlatform(PkgConstant.Platform.ANDROID.getValue());
entity.setWorkspaceId(workspaceId);
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
// save
this.save(entity);
// workbook resource
workbookResourceService.saveResource(entity.getWorkbookId(), entity.getId(), WorkbookConstant.ResourceType.PACKAGE.getValue());
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public PackageEntity updatePackage(PackageEntity entity) {
PackageEntity one = this.getOne(new LambdaQueryWrapper<PackageEntity>()
.eq(PackageEntity::getWorkspaceId, entity.getWorkspaceId())
.eq(PackageEntity::getName, entity.getName())
.ne(PackageEntity::getId, entity.getId()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
// update
this.updateById(entity);
// workbook resource
workbookResourceService.saveResource(entity.getWorkbookId(), entity.getId(), WorkbookConstant.ResourceType.PACKAGE.getValue());
entity.setSize(destination.length());
entity.setPath(destination.getPath());
this.save(entity);
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removePackage(List<String> ids) {
List<JobEntity> jobList = jobService.list(new LambdaQueryWrapper<JobEntity>().in(JobEntity::getPackageId, ids));
if (T.CollUtil.isNotEmpty(jobList)) {
throw new ASWException(RCode.PACKAGE_CANNOT_DELETE);
}
for (String id : ids) {
PackageEntity entity = this.getById(id);
// remove file
T.FileUtil.del(entity.getPath());
// remove
this.removeBatchByIds(ids);
this.removeById(id);
}
// workbook resource
workbookResourceService.removeResource(ids, WorkbookConstant.ResourceType.PACKAGE.getValue());
}
@Override
public PackageEntity updatePackage(String workspaceId, String packageId, String name, String description) {
PackageEntity entity = this.getById(packageId);
if (T.StrUtil.isNotEmpty(name)){
entity.setName(name);
}
if (T.StrUtil.isNotEmpty(description)){
entity.setDescription(description);
}
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
this.updateById(entity);
return entity;
}
}

View File

@@ -1,13 +0,0 @@
package net.geedge.asw.module.app.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.module.app.dao.SignatureDao;
import net.geedge.asw.module.app.entity.SignatureEntity;
import net.geedge.asw.module.app.service.ISignatureService;
import org.springframework.stereotype.Service;
@Service
public class SignatureServiceImpl extends ServiceImpl<SignatureDao, SignatureEntity> implements ISignatureService {
}

View File

@@ -0,0 +1,813 @@
package net.geedge.asw.module.app.service.impl;
import cn.hutool.core.lang.Validator;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.service.ITSGApplicationService;
import net.geedge.asw.module.attribute.entity.AttributeEntity;
import net.geedge.asw.module.attribute.service.IAttributeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service("tsg2402ApplicationService")
public class TSG2402ApplicationServiceImpl implements ITSGApplicationService {
private static final Log log = Log.get();
@Autowired
private IAttributeService attributeService;
@Override
public Map<Object, Object> aswToTsg2402(List<ApplicationEntity> appList) {
List<Object> applications = this.buildTSG2402Applications(appList);
Map<Object, Object> signatures = this.buildTSG2402Signatures(appList);
Map<Object, Object> m = T.MapUtil.builder()
.put("applications", applications)
.putAll(signatures)
.build();
return m;
}
private List<Object> buildTSG2402Applications(List<ApplicationEntity> appList) {
List<Object> applications = T.ListUtil.list(true);
for (ApplicationEntity app : appList) {
// application
Map<Object, Object> application = T.MapUtil.builder()
.put("app_name", app.getName())
.put("app_longname", app.getName())
.put("description", app.getDescription())
.build();
// app_properties
Map properties = (Map) app.getProperties();
Map<Object, Object> app_properties = T.MapUtil.builder()
.put("parent_app_id", 0)
.put("parent_app_name", T.MapUtil.getStr(properties, "parentApp", ""))
.put("category", T.MapUtil.getStr(properties, "category", ""))
.put("subcategory", T.MapUtil.getStr(properties, "subcategory", ""))
.put("content", T.MapUtil.getStr(properties, "content", ""))
.put("risk", T.MapUtil.getStr(properties, "risk", "1"))
.put("deny_action", T.MapUtil.builder()
.put("method", "drop")
.put("after_n_packets", 0)
.put("send_icmp_unreachable", 0)
.put("send_tcp_reset", 0)
.build()
)
.put("continue_scanning", 0)
.put("tcp_timeout", 0)
.put("udp_timeout", 0)
.put("tcp_half_close", 0)
.put("tcp_time_wait", 0)
.build();
application.put("app_properties", app_properties);
// app_surrogates
JSONObject jsonObject = (JSONObject) app.getSignature();
JSONArray surrogates = jsonObject.getJSONArray("surrogates");
if (!surrogates.isEmpty()) {
List<Map> app_surrogates = T.ListUtil.list(true);
surrogates.forEach(obj -> {
List<Object> signature_sequence = T.ListUtil.list(true);
JSONArray signatureArr = ((JSONObject) obj).getJSONArray("signatures");
signatureArr.stream().map(o -> ((JSONObject) o).getStr("name")).forEach(tname -> {
signature_sequence.add(T.MapUtil.builder()
.put("signature", tname)
.put("exclude", 0)
.build()
);
});
app_surrogates.add(
T.MapUtil.builder()
.put("group_by", "session")
.put("time_window", 0)
.put("ordered_match", "no")
.put("signature_sequence", signature_sequence)
.build()
);
});
application.put("app_surrogates", app_surrogates);
}
applications.add(application);
}
return applications;
}
private Map<Object, Object> buildTSG2402Signatures(List<ApplicationEntity> appList) {
List<Object> signatures = T.ListUtil.list(true);
List<Object> sig_objects = T.ListUtil.list(true);
int sig_object_id = 10, signature_id = 0;
for (ApplicationEntity app : appList) {
JSONObject jsonObject = (JSONObject) app.getSignature();
JSONArray surrogates = jsonObject.getJSONArray("surrogates");
List<Object> signaturesForApp = surrogates.stream()
.map(obj -> ((JSONObject) obj).getJSONArray("signatures"))
.flatMap(Collection::stream)
.collect(Collectors.toList());
for (Object object : signaturesForApp) {
JSONObject surrogate = (JSONObject) object;
Map<Object, Object> m = T.MapUtil.builder()
.put("signature_id", signature_id++)
.put("signature_name", T.MapUtil.getStr(surrogate, "name"))
.put("signature_desc", T.MapUtil.getStr(surrogate, "description", ""))
.put("icon_color", "")
.build();
List<Object> and_conditions = T.ListUtil.list(true);
JSONArray conditions = surrogate.getJSONArray("conditions");
for (Object condition : conditions) {
JSONObject conditionJSONObj = (JSONObject) condition;
String attributeName = T.MapUtil.getStr(conditionJSONObj, "attributeName");
AttributeEntity attributeEntity = attributeService.queryAttribute(attributeName);
if (null == attributeEntity || T.StrUtil.isEmpty(attributeEntity.getObjectType())) continue;
Map<Object, Object> or_condition_obj = T.MapUtil.builder()
.put("lua_profile_id", 0)
.put("attribute_type", attributeEntity.getType())
.put("attribute_name", attributeName)
.put("protocol", attributeEntity.getProtocol())
.build();
List<Integer> source_object_ids = T.ListUtil.list(true);
// objects
JSONArray objects = conditionJSONObj.getJSONArray("objects");
for (Object tempObject : objects) {
JSONObject tempJsonObject = (JSONObject) tempObject;
// sig_objects
JSONArray items = tempJsonObject.getJSONArray("items");
String objectName = tempJsonObject.getStr("name");
String objectType = tempJsonObject.getStr("type");
String objectDescription = tempJsonObject.getStr("description");
if ("application".equalsIgnoreCase(objectType)) {
continue;
} else if ("boolean".equals(objectType)) {
items.stream()
.map(obj -> (JSONObject) obj)
.forEach(item -> {
String itemValue = T.MapUtil.getStr((JSONObject) item, "item");
if ("True".equalsIgnoreCase(itemValue)) {
source_object_ids.add(2);
} else if ("False".equalsIgnoreCase(itemValue)) {
source_object_ids.add(3);
}
});
} else if ("ip_protocol".equals(objectType)) {
items.stream()
.map(obj -> (JSONObject) obj)
.forEach(item -> {
String itemValue = T.MapUtil.getStr((JSONObject) item, "item");
if ("ICMP".equalsIgnoreCase(itemValue)) {
source_object_ids.add(5);
} else if ("TCP".equalsIgnoreCase(itemValue)) {
source_object_ids.add(6);
} else if ("UDP".equalsIgnoreCase(itemValue)) {
source_object_ids.add(7);
}
});
} else {
Map<Object, Object> sig_object = T.MapUtil.builder()
.put("id", sig_object_id)
.put("source_id", sig_object_id)
.put("name", objectName)
.put("source_name", objectName)
.put("type", objectType)
.put("sub_type", attributeEntity.getType())
.put("member_type", "item")
.put("uuid", T.IdUtil.fastSimpleUUID())
.put("statistics_option", "none")
.put("description", objectDescription)
.build();
Map<Object, Object> member = this.buildTSG2402SignaturesMember(objectType.toLowerCase(), items);
sig_object.put("member", member);
sig_objects.add(sig_object);
source_object_ids.add(sig_object_id);
sig_object_id++;
}
}
or_condition_obj.put("source_object_ids", source_object_ids);
Map<Object, Object> and_condition_item = T.MapUtil.builder()
.put("not_flag", T.MapUtil.getBool(conditionJSONObj, "negateOption", false) ? 1 : 0)
.put("or_conditions", T.ListUtil.of(or_condition_obj))
.build();
and_conditions.add(and_condition_item);
}
if (T.CollUtil.isNotEmpty(and_conditions)) {
m.put("and_conditions", and_conditions);
signatures.add(m);
}
}
}
sig_objects.add(T.JSONUtil.parseObj("""
{
"id": 2,
"type": "boolean",
"name": "True",
"vsys_id": 0,
"description": "True",
"source_id": 2,
"source_name": "True",
"member_type": "item",
"uuid": "c4ca4238a0b923820dcc509a6f75849b",
"statistics_option": "elaborate"
}
"""));
sig_objects.add(T.JSONUtil.parseObj("""
{
"id": 3,
"type": "boolean",
"name": "False",
"vsys_id": 0,
"description": "False",
"source_id": 3,
"source_name": "False",
"member_type": "item",
"uuid": "cfcd208495d565ef66e7dff9f98764da",
"statistics_option": "elaborate"
}
"""));
sig_objects.add(T.JSONUtil.parseObj("""
{
"id": 5,
"type": "ip_protocol",
"name": "ICMP",
"vsys_id": 0,
"description": "ICMP",
"source_id": 5,
"source_name": "ICMP",
"member_type": "item",
"uuid": "c4ca4238a0b923820dcc509a6f75849b",
"statistics_option": "elaborate"
}
"""));
sig_objects.add(T.JSONUtil.parseObj("""
{
"id": 6,
"type": "ip_protocol",
"name": "TCP",
"vsys_id": 0,
"description": "TCP",
"source_id": 6,
"source_name": "TCP",
"member_type": "item",
"uuid": "1679091c5a880faf6fb5e6087eb1b2dc",
"statistics_option": "elaborate"
}
"""));
sig_objects.add(T.JSONUtil.parseObj("""
{
"id": 7,
"type": "ip_protocol",
"name": "UDP",
"vsys_id": 0,
"description": "UDP",
"source_id": 7,
"source_name": "UDP",
"member_type": "item",
"uuid": "70efdf2ec9b086079795c442636b55fb",
"statistics_option": "elaborate"
}
"""));
Map<Object, Object> m = T.MapUtil.builder()
.put("signatures", signatures)
.put("sig_objects", sig_objects)
.build();
return m;
}
private Map<Object, Object> buildTSG2402SignaturesMember(String objectType, JSONArray itemArr) {
List<Object> list = T.ListUtil.list(true);
itemArr.stream()
.map(obj -> (JSONObject) obj)
.forEach(item -> {
switch (objectType) {
case "keywords":
case "http_signature": {
String str = item.getStr("item");
List<String> patternExprList = T.ListUtil.list(true);
patternExprList.add(str);
// 0 -> 无表达式1 -> 与表达式2 -> 正则表达式3、带偏移量的子串匹配
int expr_type = 0;
String exprType = item.getStr("exprType", "and");
if ("and".equalsIgnoreCase(exprType)) {
patternExprList = T.StrUtil.split(str, "&");
if (patternExprList.size() > 1) {
expr_type = 1;
}
} else if ("regex".equalsIgnoreCase(exprType)) {
expr_type = 2;
}
JSONArray patternArr = new JSONArray();
for (String expr : patternExprList) {
JSONObject pattern = new JSONObject();
pattern.put("keywords", expr);
Map<String, String> rangeVarMap = this.getRangeVarFromExpr(expr);
if (T.MapUtil.isNotEmpty(rangeVarMap)) {
expr_type = 3;
pattern.put("keywords", expr.replaceAll("^\\(.*?\\)", ""));
pattern.put("offset", T.MapUtil.getInt(rangeVarMap, "offset"));
pattern.put("depth", T.MapUtil.getInt(rangeVarMap, "depth"));
}
patternArr.add(pattern);
}
if ("keywords".equals(objectType)) {
Map<Object, Object> m = T.MapUtil.builder()
.put("string", T.MapUtil.builder()
.put("item_type", "keywords")
.put("expr_type", expr_type)
.put("is_hexbin", 0)
.put("patterns", patternArr)
.build()
).build();
list.add(m);
}
if ("http_signature".equals(objectType)) {
Map<Object, Object> m = T.MapUtil.builder()
.put("contextual_string", T.MapUtil.builder()
.put("expr_type", expr_type)
.put("is_hexbin", 0)
.put("context_name", item.getStr("district", "Set-Cookie"))
.put("patterns", patternArr)
.build()
)
.build();
list.add(m);
}
break;
}
case "url":
case "fqdn": {
Map<Object, Object> m = T.MapUtil.builder()
.put("string", T.MapUtil.builder()
.put("item_type", objectType)
.put("expr_type", 0)
.put("is_hexbin", 0)
.put("patterns", T.ListUtil.of(
new JSONObject().put("keywords", item.getStr("item"))
))
.build()
)
.build();
list.add(m);
break;
}
case "ip": {
String str = item.getStr("item");
String ip = str;
String port = "0-65535";
if (str.contains("#")) {
ip = str.split("#")[0];
port = str.split("#")[1];
}
Map<Object, Object> m = T.MapUtil.builder()
.put("ip", T.MapUtil.builder()
.put("addr_type", Validator.isIpv4(str) ? 4 : 6)
.put("port", port)
.put("ip_address", ip)
.build()
)
.build();
list.add(m);
break;
}
case "port": {
String port = item.getStr("item");
Map<Object, Object> m = T.MapUtil.builder()
.put("port", new JSONObject().put("port", port))
.build();
if (port.contains("-")) {
m.put("port", new JSONObject().put("port_range", port));
}
list.add(m);
break;
}
case "interval": {
String str = item.getStr("item");
String low_boundary = str, up_boundary = str;
if (str.contains("-")) {
low_boundary = item.getStr("item").split("-")[0];
up_boundary = item.getStr("item").split("-")[1];
}
Map<Object, Object> m = T.MapUtil.builder()
.put("interval", T.MapUtil.builder()
.put("low_boundary", low_boundary)
.put("up_boundary", up_boundary)
.build()
)
.build();
list.add(m);
break;
}
case "boolean":
case "ip_protocol":
case "application": {
break;
}
default:
break;
}
});
Map<Object, Object> member = T.MapUtil.builder()
.put("items", list)
.build();
return member;
}
/**
* 获取表达式中的 range 变量,示例 (nocase=off,offset=6,depth=13)expr_xxxxxxxxx
*/
private Map<String, String> getRangeVarFromExpr(String expr) {
try {
String regex = "^\\(([^)]+)\\)";
String str = T.ReUtil.get(regex, expr, 1);
if (T.StrUtil.isNotEmpty(str)) {
String[] pairs = str.split(",");
Map<String, String> map = new HashMap<>();
for (String pair : pairs) {
String[] keyValue = pair.split("=");
if (keyValue.length == 2) {
map.put(keyValue[0].trim(), keyValue[1].trim());
}
}
// 不包含 offsetdepth 算没有配置
if (!map.containsKey("offset") || !map.containsKey("depth")) {
return new HashMap<>();
}
return map;
}
} catch (Exception e) {
log.error(e, "[getRangeVarFromExpr] [expr: {}]", expr);
}
return new HashMap<>();
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@Override
public List<ApplicationEntity> tsg2402ToAsw(String workspaceId, List<JSONObject> dataList) {
List<ApplicationEntity> records = T.ListUtil.list(true);
for (JSONObject tsgAppSourceData : dataList) {
JSONArray all_application = tsgAppSourceData.getJSONArray("applications");
JSONArray all_signature = tsgAppSourceData.getJSONArray("signatures");
JSONArray all_sig_object = tsgAppSourceData.getJSONArray("sig_objects");
all_application.stream()
.map(obj -> (JSONObject) obj)
.forEach(application -> {
// application
String app_name = application.getStr("app_name");
String app_longname = application.getStr("app_longname");
String description = application.getStr("description");
JSONObject appProperties = application.getJSONObject("app_properties");
String category = T.MapUtil.getStr(appProperties, "category", "");
String subcategory = T.MapUtil.getStr(appProperties, "subcategory", "");
String content = T.MapUtil.getStr(appProperties, "content", "");
String parent_app_name = T.MapUtil.getStr(appProperties, "parent_app_name", "");
int risk = T.MapUtil.getInt(appProperties, "risk", 1);
Map<Object, Object> properties = T.MapUtil.builder()
.put("category", category)
.put("subcategory", subcategory)
.put("content", content)
.put("parentApp", parent_app_name)
.put("risk", risk)
.build();
// meta.json
ApplicationEntity entity = new ApplicationEntity();
entity.setName(app_name);
entity.setLongName(app_longname);
entity.setDescription(description);
entity.setProperties(properties);
// default value
entity.setId(T.StrUtil.uuid());
entity.setDeveloper("");
entity.setWebsite("");
entity.setPackageName(
T.MapUtil.builder()
.put("android", "")
.put("ios", "")
.build()
);
// surrogate - signature
Map<String, List<String>> surrAndSignListMap = T.MapUtil.newHashMap();
JSONArray app_surrogates = application.getJSONArray("app_surrogates");
if (T.ObjectUtil.isNotEmpty(app_surrogates)) {
for (int i = 0; i < app_surrogates.size(); i++) {
JSONObject surrogate = (JSONObject) app_surrogates.get(i);
List<String> signatureNameList = (List<String>) T.JSONUtil.getByPath(surrogate, "signature_sequence.signature");
surrAndSignListMap.put("surrogate_" + (i + 1), signatureNameList);
}
}
List<Object> insertSurrogateList = T.ListUtil.list(true);
for (Map.Entry<String, List<String>> entry : surrAndSignListMap.entrySet()) {
String surrogateName = entry.getKey();
List<String> signatureNameList = entry.getValue();
List<JSONObject> signatureListInApp = all_signature.stream()
.filter(obj -> {
String str = T.MapUtil.getStr((JSONObject) obj, "signature_name", "");
return signatureNameList.contains(str);
})
.map(obj -> (JSONObject) obj)
.collect(Collectors.toList());
if (T.CollUtil.isEmpty(signatureListInApp)) continue;
List<JSONObject> sigObjectList = all_sig_object.stream()
.map(obj -> (JSONObject) obj)
.collect(Collectors.toList());
Map<Object, Object> aswSrrogate = this.buildAswSurrogateFromTSG2402(surrogateName, signatureListInApp, sigObjectList);
insertSurrogateList.add(aswSrrogate);
}
Map<Object, Object> sm = T.MapUtil.builder()
.put("surrogates", insertSurrogateList)
.build();
// signature.json
entity.setSignature(T.JSONUtil.parseObj(sm));
// add records
records.add(entity);
});
}
return records;
}
private Map<Object, Object> buildAswSurrogateFromTSG2402(String surrogateName, List<JSONObject> signatureList, List<JSONObject> sigObjectList) {
// surrogate
Map<Object, Object> surrogate = T.MapUtil.builder()
.put("name", surrogateName)
.put("description", "")
.build();
// signatures
List<Object> signatures = T.ListUtil.list(true);
for (JSONObject jsonObject : signatureList) {
String signature_name = jsonObject.getStr("signature_name");
String signature_description = jsonObject.getStr("signature_desc");
Map<Object, Object> signMap = T.MapUtil.builder()
.put("name", signature_name)
.put("description", signature_description)
.build();
// conditions
List<Map<Object, Object>> conditionMapList = T.ListUtil.list(true);
JSONArray and_conditions = jsonObject.getJSONArray("and_conditions");
for (Object obj : and_conditions) {
JSONObject conditions = (JSONObject) obj;
// base field
Integer not_flag = conditions.getInt("not_flag", 0);
JSONObject or_condition = (JSONObject) T.JSONUtil.getByPath(conditions, "or_conditions[0]");
String attribute_name = or_condition.getStr("attribute_name", "");
Map<Object, Object> m = T.MapUtil.builder()
.put("attributeName", attribute_name)
.put("negateOption", not_flag == 1 ? true : false)
.put("description", "")
.build();
// items
List<Integer> source_object_ids = (List<Integer>) T.JSONUtil.getByPath(or_condition, "source_object_ids");
if (T.CollUtil.isEmpty(source_object_ids)) continue;
List<JSONObject> sourceObjectList = sigObjectList.stream()
.filter(entries -> {
Integer anInt = entries.getInt("id");
return source_object_ids.contains(anInt);
})
.collect(Collectors.toList());
List<Map<Object, Object>> objects = this.buildAswConditionObjectsFromTSG2402(sourceObjectList);
if (T.CollUtil.isEmpty(objects)) continue;
m.put("objects", objects);
conditionMapList.add(m);
}
signMap.put("conditions", conditionMapList);
signatures.add(signMap);
}
surrogate.put("signatures", signatures);
return surrogate;
}
private List<Map<Object, Object>> buildAswConditionObjectsFromTSG2402(List<JSONObject> sourceObjectList) {
List<Map<Object, Object>> objectList = T.ListUtil.list(true);
for (JSONObject jsonObject : sourceObjectList) {
String name = jsonObject.getStr("name");
String type = jsonObject.getStr("type");
String description = jsonObject.getStr("description");
JSONArray itemArr = (JSONArray) T.JSONUtil.getByPath(jsonObject, "member.items");
itemArr = T.CollUtil.defaultIfEmpty(itemArr, new JSONArray());
List<Map<Object, Object>> items = T.ListUtil.list(true);
switch (type) {
case "http_signature":
case "keywords": {
String exprTypeJsonPath = "keywords" .equals(type) ? "string.expr_type" : "contextual_string.expr_type";
String firstExprJsonPath = "keywords" .equals(type) ? "string.patterns[0].keywords" : "contextual_string.patterns[0].keywords";
String patternsJsonPath = "keywords" .equals(type) ? "string.patterns" : "contextual_string.patterns";
itemArr.stream()
.map(obj -> (JSONObject) obj)
.forEach(item -> {
// 0 -> 无表达式1 -> 与表达式2 -> 正则表达式3、带偏移量的子串匹配
Integer expr_type = (Integer) T.JSONUtil.getByPath(item, exprTypeJsonPath);
String tempType = "and";
String expr = (String) T.JSONUtil.getByPath(item, firstExprJsonPath);
switch (expr_type) {
case 0:
break;
case 1: {
JSONArray patterns = (JSONArray) T.JSONUtil.getByPath(item, patternsJsonPath);
expr = patterns.stream()
.map(obj -> ((JSONObject) obj).getStr("keywords"))
.collect(Collectors.joining("&"));
break;
}
case 2:
tempType = "regex";
break;
case 3: {
JSONArray patterns = (JSONArray) T.JSONUtil.getByPath(item, patternsJsonPath);
expr = patterns.stream()
.map(obj -> {
String keywords = ((JSONObject) obj).getStr("keywords");
String offset = ((JSONObject) obj).getStr("offset");
String depth = ((JSONObject) obj).getStr("depth");
return T.StrUtil.concat(true, "(offset=", offset, ",depth=", depth, ")", keywords);
})
.collect(Collectors.joining("&"));
break;
}
default:
break;
}
Map<Object, Object> m = T.MapUtil.builder()
.put("item", expr)
.put("exprType", tempType)
.put("description", "")
.build();
String context_name = (String) T.JSONUtil.getByPath(item, "contextual_string.context_name");
if (T.StrUtil.isNotEmpty(context_name)) {
m.put("district", context_name);
}
items.add(m);
});
break;
}
case "url":
case "fqdn": {
itemArr.stream()
.map(obj -> (JSONObject) obj)
.forEach(item -> {
String str = (String) T.JSONUtil.getByPath(item, "string.patterns[0].keywords");
items.add(
T.MapUtil.builder()
.put("item", str)
.put("description", "")
.build()
);
});
break;
}
case "ip": {
itemArr.stream()
.map(obj -> (JSONObject) obj)
.forEach(item -> {
String port = (String) T.JSONUtil.getByPath(item, "ip.port");
String ipAddress = (String) T.JSONUtil.getByPath(item, "ip.ip_address");
if (T.StrUtil.isEmpty(ipAddress)) {
ipAddress = (String) T.JSONUtil.getByPath(item, "ip.ip_cidr");
}
if (T.StrUtil.isEmpty(ipAddress)) {
ipAddress = (String) T.JSONUtil.getByPath(item, "ip.ip_range");
}
if (!"0-65535" .equalsIgnoreCase(port)) {
ipAddress = T.StrUtil.concat(true, ipAddress, "#", port);
}
items.add(
T.MapUtil.builder()
.put("item", ipAddress)
.put("description", "")
.build()
);
});
break;
}
case "port": {
itemArr.stream()
.map(obj -> (JSONObject) obj)
.forEach(item -> {
String port = (String) T.JSONUtil.getByPath(item, "port.port");
if (T.StrUtil.isEmpty(port)) {
port = (String) T.JSONUtil.getByPath(item, "port.port_range");
}
items.add(
T.MapUtil.builder()
.put("item", port)
.put("description", "")
.build()
);
});
break;
}
case "interval": {
itemArr.stream()
.map(obj -> (JSONObject) obj)
.forEach(item -> {
Object low_boundary = T.JSONUtil.getByPath(item, "interval.low_boundary");
Object up_boundary = T.JSONUtil.getByPath(item, "interval.up_boundary");
Map<Object, Object> m = T.MapUtil.builder()
.put("item", low_boundary + "-" + up_boundary)
.put("description", "")
.build();
items.add(m);
});
break;
}
case "boolean":
case "ip_protocol": {
Map<Object, Object> m = T.MapUtil.builder()
.put("item", jsonObject.getStr("name"))
.put("description", "")
.build();
items.add(m);
break;
}
case "application": {
break;
}
default:
break;
}
Map<Object, Object> m = T.MapUtil.builder()
.put("name", name)
.put("type", type)
.put("description", description)
.put("items", items)
.build();
objectList.add(m);
}
return objectList;
}
@Override
public Map<Object, Object> aswToTsg2410(List<ApplicationEntity> appList) {
// 不在这个类处理,在 TSG2410ApplicationServiceImpl 实现
return null;
}
@Override
public List<ApplicationEntity> tsg2410ToAsw(String workspaceId, List<JSONObject> dataList) {
// 不在这个类处理,在 TSG2410ApplicationServiceImpl 实现
return null;
}
}

View File

@@ -0,0 +1,194 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationReleaseEntity;
import net.geedge.asw.module.app.service.IApplicationReleaseService;
import net.geedge.asw.module.app.service.ITagService;
import net.geedge.asw.module.app.util.JGitUtils;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class TagServiceImpl implements ITagService {
private final static Log log = Log.get();
@Autowired
private ISysUserService userService;
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IApplicationReleaseService releaseService;
private String getShortTagName(String name) {
return name.startsWith(JGitUtils.R_TAGS) ? name.replaceAll(JGitUtils.R_TAGS, "") : name;
}
@Override
public Map<Object, Object> infoTag(String workspaceId, String name) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);) {
Map<Object, Object> infoTag = this.infoTag(repository, name);
return infoTag;
} catch (IOException e) {
log.error(e, "[infoTag] [error] [workspaceId: {}] [name: {}]", workspaceId, name);
throw new RuntimeException(e);
}
}
/**
* tag 详情
*
* @param repository
* @param name
* @return
* @throws IOException
*/
private Map<Object, Object> infoTag(Repository repository, String name) throws IOException {
Ref ref = repository.findRef(JGitUtils.getFullTagName(name));
T.VerifyUtil.is(ref).notNull(RCode.GIT_TAG_NOT_FOUND.setParam(name));
// tag
RevTag revTag = JGitUtils.infoTag(repository, ref.getObjectId());
String message = revTag.getFullMessage();
// commit
RevCommit revCommit = JGitUtils.infoCommit(repository, ref.getObjectId().getName());
Map<Object, Object> aswCommitInfo = JGitUtils.buildAswCommitInfo(revCommit);
// release
ApplicationReleaseEntity releaseEntity = releaseService.getOne(new LambdaQueryWrapper<ApplicationReleaseEntity>()
.eq(ApplicationReleaseEntity::getWorkspaceId, repository.getDirectory().getName())
.eq(ApplicationReleaseEntity::getTagName, this.getShortTagName(name))
);
Map<Object, Object> m = T.MapUtil.builder()
.put("name", this.getShortTagName(name))
.put("message", message)
.put("createdAt", revTag.getTaggerIdent().getWhen().getTime())
.put("commit", aswCommitInfo)
.put("release", releaseEntity)
.build();
return m;
}
@Override
public List<Map<Object, Object>> listTag(String workspaceId, String search) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
Git git = Git.open(repository.getDirectory())) {
List<Ref> refList = git.tagList().call();
if (T.StrUtil.isNotEmpty(search)) {
refList = refList.stream()
.filter(ref -> {
String name = ref.getName();
return T.StrUtil.containsIgnoreCase(this.getShortTagName(name), search);
})
.collect(Collectors.toList());
}
List<Map<Object, Object>> list = T.ListUtil.list(true);
for (Ref ref : refList) {
list.add(this.infoTag(repository, ref.getName()));
}
// 默认根据 createdAt 字段排序
list = list.stream()
.sorted(Comparator.comparing(map -> T.MapUtil.getLong((Map) map, "createdAt")).reversed())
.collect(Collectors.toList());
return list;
} catch (IOException | GitAPIException e) {
log.error(e, "[listTag] [error] [workspaceId: {}] [search: {}]", workspaceId, search);
throw new RuntimeException(e);
}
}
@Override
public Map<Object, Object> newTag(String workspaceId, String name, String branch, String message) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
Git git = Git.open(repository.getDirectory())) {
// check tag exists
List<Ref> tagListInDb = git.tagList().call();
Ref checkRefExists = tagListInDb.parallelStream()
.filter(ref -> T.StrUtil.equals(name, this.getShortTagName(ref.getName())))
.findFirst()
.orElse(null);
if (null != checkRefExists) {
throw new ASWException(RCode.GIT_TAG_ALREADY_EXISTS.setParam(name));
}
// check branch exists
if (T.BooleanUtil.negate(
JGitUtils.isBranchExists(repository, branch)
)) {
throw new ASWException(RCode.GIT_MERGE_TARGET_BRANCH_NOT_EXIST.setParam(branch));
}
SysUserEntity loginUser = userService.getById(StpUtil.getLoginIdAsString());
PersonIdent personIdent = JGitUtils.buildPersonIdent(loginUser.getName());
RevCommit branchLatestCommit = JGitUtils.getBranchLatestCommit(repository, branch);
// add tag
git.tag()
.setName(name)
.setMessage(message)
.setTagger(personIdent)
.setObjectId(branchLatestCommit)
.setAnnotated(true)
.setForceUpdate(false)
.call();
// return info
return this.infoTag(repository, name);
} catch (IOException | GitAPIException e) {
log.error(e, "[newTag] [error] [workspaceId: {}] [name: {}] [branch: {}]", workspaceId, name, branch);
throw new RuntimeException(e);
}
}
@Override
public void deleteTag(String workspaceId, String name) {
File gitDir = workspaceService.getGitDir(workspaceId);
try (Repository repository = JGitUtils.openRepository(gitDir);
Git git = Git.open(repository.getDirectory())) {
// del tag
git.tagDelete()
.setTags(name)
.call();
// del release
releaseService.removeRelease(workspaceId, name);
} catch (IOException | GitAPIException e) {
log.error(e, "[deleteTag] [error] [workspaceId: {}] [name: {}]", workspaceId, name);
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,180 @@
package net.geedge.asw.module.app.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ApkInfo {
public static final String APPLICATION_ICON_120 = "application-icon-120";
public static final String APPLICATION_ICON_160 = "application-icon-160";
public static final String APPLICATION_ICON_240 = "application-icon-240";
public static final String APPLICATION_ICON_320 = "application-icon-320";
// 所需设备属性
private List<String> features;
// 图标
private String icon;
// 各分辨率下图标路径
private Map<String, String> icons;
// 应用程序名
private String label;
// 入口Activity
private String launchableActivity;
// 支持的Android平台最低版本号
private String minSdkVersion;
// 主包名
private String packageName;
// 支持的SDK版本
private String sdkVersion;
// Apk文件大小字节
private long size;
// 目标SDK版本
private String targetSdkVersion;
// 所需权限
private List<String> usesPermissions;
// 内部版本号
private String versionCode;
// 外部版本号
private String versionName;
// xapk 包含的 apk
private List<String> splitApks;
public ApkInfo() {
this.features = new ArrayList<>();
this.icons = new HashMap<>();
this.usesPermissions = new ArrayList<>();
this.splitApks = new ArrayList<>();
}
public List<String> getFeatures() {
return features;
}
public void setFeatures(List<String> features) {
this.features = features;
}
public void addToFeatures(String feature) {
this.features.add(feature);
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Map<String, String> getIcons() {
return icons;
}
public void setIcons(Map<String, String> icons) {
this.icons = icons;
}
public void addToIcons(String key, String value) {
this.icons.put(key, value);
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getLaunchableActivity() {
return launchableActivity;
}
public void setLaunchableActivity(String launchableActivity) {
this.launchableActivity = launchableActivity;
}
public String getMinSdkVersion() {
return minSdkVersion;
}
public void setMinSdkVersion(String minSdkVersion) {
this.minSdkVersion = minSdkVersion;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getSdkVersion() {
return sdkVersion;
}
public void setSdkVersion(String sdkVersion) {
this.sdkVersion = sdkVersion;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public String getTargetSdkVersion() {
return targetSdkVersion;
}
public void setTargetSdkVersion(String targetSdkVersion) {
this.targetSdkVersion = targetSdkVersion;
}
public List<String> getUsesPermissions() {
return usesPermissions;
}
public void setUsesPermissions(List<String> usesPermissions) {
this.usesPermissions = usesPermissions;
}
public void addToUsesPermissions(String usesPermission) {
this.usesPermissions.add(usesPermission);
}
public String getVersionCode() {
return versionCode;
}
public void setVersionCode(String versionCode) {
this.versionCode = versionCode;
}
public String getVersionName() {
return versionName;
}
public void setVersionName(String versionName) {
this.versionName = versionName;
}
public List<String> getSplitApks() {
return splitApks;
}
public void setSplitApks(List<String> splitApks) {
this.splitApks = splitApks;
}
@Override
public String toString() {
return "ApkInfo [features=" + features + ", icon=" + icon + ", icons=" + icons + ", label=" + label + ", launchableActivity=" + launchableActivity + ", minSdkVersion=" + minSdkVersion + ", packageName=" + packageName + ", sdkVersion=" + sdkVersion + ", size=" + size + ", targetSdkVersion=" + targetSdkVersion + ", usesPermissions=" + usesPermissions + ", versionCode=" + versionCode + ", versionName=" + versionName + "]";
}
}

View File

@@ -0,0 +1,175 @@
package net.geedge.asw.module.app.util;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.T;
import java.io.*;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class ApkUtil {
private static final Log log = Log.get();
public static final String APPLICATION = "application:";
public static final String APPLICATION_ICON = "application-icon";
public static final String APPLICATION_LABEL = "application-label";
public static final String APPLICATION_LABEL_N = "application: label";
public static final String DENSITIES = "densities";
public static final String LAUNCHABLE_ACTIVITY = "launchable";
public static final String PACKAGE = "package";
public static final String SDK_VERSION = "sdkVersion";
public static final String SUPPORTS_ANY_DENSITY = "support-any-density";
public static final String SUPPORTS_SCREENS = "support-screens";
public static final String TARGET_SDK_VERSION = "targetSdkVersion";
public static final String VERSION_CODE = "versionCode";
public static final String VERSION_NAME = "versionName";
public static final String USES_FEATURE = "uses-feature";
public static final String USES_IMPLIED_FEATURE = "uses-implied-feature";
public static final String USES_PERMISSION = "uses-permission";
private static final String SPLIT_REGEX = "(: )|(=')|(' )|'";
private ProcessBuilder builder;
// aapt 所在目录
private String aaptToolPath = "aapt";
public ApkUtil() {
builder = new ProcessBuilder();
builder.redirectErrorStream(true);
}
public String getAaptToolPath() {
return aaptToolPath;
}
public void setAaptToolPath(String aaptToolPath) {
this.aaptToolPath = aaptToolPath;
}
public ApkInfo parseApk(String apkPath) {
String aaptTool = aaptToolPath;
Process process = null;
InputStream inputStream = null;
BufferedReader bufferedReader = null;
try {
process = builder.command(aaptTool, "d", "badging", apkPath).start();
inputStream = process.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
ApkInfo apkInfo = new ApkInfo();
apkInfo.setSize(new File(apkPath).length());
String temp = null;
while ((temp = bufferedReader.readLine()) != null) {
setApkInfoProperty(apkInfo, temp);
}
if (T.StrUtil.isBlank(apkInfo.getPackageName()) || T.StrUtil.isBlank(apkInfo.getVersionName())) {
return null;
}
return apkInfo;
} catch (IOException e) {
log.error(e, "[parseApk] [error] [path: {}]", apkPath);
return null;
} finally {
if (process != null) {
process.destroy();
}
T.IoUtil.close(inputStream);
T.IoUtil.close(bufferedReader);
}
}
public ApkInfo parseXapk(String xapkPath) {
InputStream inputStream = null;
BufferedReader reader = null;
ZipFile zipFile = null;
try {
zipFile = new ZipFile(T.FileUtil.file(xapkPath));
ZipEntry entry = zipFile.getEntry("manifest.json");
if (entry == null){
return null;
}
inputStream = zipFile.getInputStream(entry);
StringBuilder manifestJson = new StringBuilder();
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
manifestJson.append(line).append("\n");
}
Map manifest = T.JSONUtil.toBean(manifestJson.toString(), Map.class);
ApkInfo apkInfo = new ApkInfo();
this.setXapkInfoProperty(manifest, apkInfo);
String packageName = apkInfo.getPackageName();
List<String> splitApks = apkInfo.getSplitApks();
if (T.CollUtil.isNotEmpty(splitApks)){
Enumeration<? extends ZipEntry> apkEntries = zipFile.entries();
while (apkEntries.hasMoreElements()) {
ZipEntry apk = apkEntries.nextElement();
if (!apk.getName().endsWith(".apk")){
continue;
}
if (!T.StrUtil.equalsIgnoreCase(packageName + ".apk", apk.getName()) && !splitApks.contains(apk.getName())) {
return null;
}
}
}
return apkInfo;
} catch (Exception e) {
log.error(e, "[parseXapk] [error] [path: {}]", xapkPath);
return null;
} finally {
T.IoUtil.close(inputStream);
T.IoUtil.close(reader);
T.IoUtil.close(zipFile);
}
}
private void setXapkInfoProperty(Map manifest, ApkInfo apkInfo) {
apkInfo.setLabel(T.MapUtil.getStr(manifest, "name"));
apkInfo.setVersionName(T.MapUtil.getStr(manifest, "version_name"));
apkInfo.setPackageName(T.MapUtil.getStr(manifest, "package_name"));
List<Map> splitApkMapList = T.MapUtil.get(manifest, "split_apks", List.class);
List<String> splitApkNames = splitApkMapList.stream().map(x -> T.MapUtil.getStr(x, "file")).toList();
apkInfo.setSplitApks(splitApkNames);
}
private void setApkInfoProperty(ApkInfo apkInfo, String source) {
if (source.startsWith(APPLICATION)) {
String[] rs = source.split("( icon=')|'");
apkInfo.setIcon(rs[rs.length - 1]);
} else if (source.startsWith(APPLICATION_ICON)) {
apkInfo.addToIcons(getKeyBeforeColon(source), getPropertyInQuote(source));
} else if (source.startsWith(APPLICATION_LABEL)) {
apkInfo.setLabel(getPropertyInQuote(source));
} else if (source.startsWith(LAUNCHABLE_ACTIVITY)) {
apkInfo.setLaunchableActivity(getPropertyInQuote(source));
} else if (source.startsWith(PACKAGE)) {
String[] packageInfo = source.split(SPLIT_REGEX);
apkInfo.setPackageName(packageInfo[2]);
apkInfo.setVersionCode(packageInfo[4]);
apkInfo.setVersionName(packageInfo[6]);
} else if (source.startsWith(SDK_VERSION)) {
apkInfo.setSdkVersion(getPropertyInQuote(source));
} else if (source.startsWith(TARGET_SDK_VERSION)) {
apkInfo.setTargetSdkVersion(getPropertyInQuote(source));
} else if (source.startsWith(USES_PERMISSION)) {
apkInfo.addToUsesPermissions(getPropertyInQuote(source));
} else if (source.startsWith(USES_FEATURE)) {
apkInfo.addToFeatures(getPropertyInQuote(source));
}
}
private String getKeyBeforeColon(String source) {
return source.substring(0, source.indexOf(':'));
}
private String getPropertyInQuote(String source) {
int index = source.indexOf("'") + 1;
return source.substring(index, source.indexOf('\'', index));
}
}

View File

@@ -0,0 +1,897 @@
package net.geedge.asw.module.app.util;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import org.apache.commons.io.FilenameUtils;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.archive.ArchiveFormats;
import org.eclipse.jgit.diff.*;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.*;
import org.eclipse.jgit.merge.MergeFormatter;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.RecursiveMerger;
import org.eclipse.jgit.merge.ThreeWayMerger;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component
public class JGitUtils {
private final static Log log = Log.get();
/**
* 本地分支引用前缀
*/
public static final String R_HEADS = "refs/heads/";
public static final String R_TAGS = "refs/tags/";
/**
* 默认分支
*/
public static final String DEFAULT_BRANCH = "main";
private static String textExtensions;
@Autowired
public JGitUtils(Environment environment) {
textExtensions = environment.getProperty("file.extensions.text", "txt,csv,md,html,xml,json,log,bat,py,sh,ini,conf,yaml,yml,properties,toml,java,c,cpp,js,php,ts,go,rb,rtf,tex,rss,xhtml,sql");
}
/**
* 返回 git repository
*
* @param gitDir
* @return
* @throws IOException
*/
public static Repository openRepository(File gitDir) throws IOException {
FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repository = builder.setGitDir(gitDir)
.readEnvironment()
.findGitDir()
.build();
return repository;
}
/**
* 初始化仓库
*
* @param repoDir
* @param author
*/
public static void initRepository(File repoDir, String author) {
try (
Git git = Git.init()
.setBare(true)
.setDirectory(repoDir)
.setInitialBranch(DEFAULT_BRANCH)
.call();
Repository repository = git.getRepository();
ObjectInserter inserter = repository.getObjectDatabase().newInserter();
) {
DirCache dirCache = DirCache.newInCore();
DirCacheBuilder builder = dirCache.builder();
ObjectId objectId = JGitUtils.insertBlobFileToDatabase(repository, "".getBytes());
DirCacheEntry entry = JGitUtils.buildDirCacheEntry("README.md", FileMode.REGULAR_FILE, objectId);
builder.add(entry);
builder.finish();
// commit
String message = "Initial commit";
PersonIdent personIdent = new PersonIdent(author, "asw@geedgenetworks.com");
JGitUtils.createCommit(repository, DEFAULT_BRANCH, dirCache.writeTree(inserter), message, personIdent);
} catch (GitAPIException | IOException e) {
log.error(e, "[initRepository] [git init error]");
throw new RuntimeException(e);
}
}
/**
* 返回分支是否存在
*
* @param repository
* @param branch
* @return
* @throws IOException
*/
public static boolean isBranchExists(Repository repository, String branch) throws IOException {
Ref ref = repository.findRef(T.StrUtil.concat(true, R_HEADS, branch));
return null != ref;
}
/**
* 获取提交对象
*
* @param repository
* @param commitId
* @return
* @throws IOException
*/
public static RevCommit infoCommit(Repository repository, String commitId) throws IOException {
try (RevWalk revCommits = new RevWalk(repository);) {
RevCommit commit = revCommits.parseCommit(ObjectId.fromString(commitId));
return commit;
}
}
public static String getFullTagName(String name) {
return name.startsWith(R_TAGS) ? name : T.StrUtil.concat(true, R_TAGS, name);
}
/**
* 获取tag对象
*
* @param repository
* @param objectId
* @return
* @throws IOException
*/
public static RevTag infoTag(Repository repository, ObjectId objectId) throws IOException {
try (RevWalk revWalk = new RevWalk(repository)) {
RevTag revTag = revWalk.parseTag(objectId);
return revTag;
}
}
/**
* 返回分支最新提交
*
* @param repository
* @param branch
* @return
*/
public static RevCommit getBranchLatestCommit(Repository repository, String branch) throws IOException {
RevWalk revWalk = new RevWalk(repository);
RevCommit commit = revWalk.parseCommit(repository.resolve(branch));
return commit;
}
/**
* 获取分支提交记录
*
* @param repository
* @param branch
* @param path
* @return
* @throws GitAPIException
* @throws IOException
*/
public static List<RevCommit> getBranchCommitList(Repository repository, String branch, String path) throws GitAPIException, IOException {
List<RevCommit> resultList = T.ListUtil.list(true);
Git git = Git.open(repository.getDirectory());
ObjectId branchRef = git.getRepository().resolve(branch);
LogCommand command = git.log().add(branchRef);
if (T.StrUtil.isNotEmpty(path)) {
command.addPath(path);
}
Iterable<RevCommit> iterable = command.call();
for (RevCommit commit : iterable) {
resultList.add(commit);
}
return resultList;
}
/**
* 获取父提交
*
* @param repository
* @param branch
* @param commitId
* @return
* @throws IOException
*/
public static RevCommit getParentCommit(Repository repository, String branch, String commitId) throws IOException {
try (RevWalk revWalk = new RevWalk(repository)) {
Ref branchRef = repository.findRef(branch);
revWalk.markStart(revWalk.parseCommit(branchRef.getObjectId()));
RevCommit parentCommit = null;
Iterator<RevCommit> iterator = revWalk.iterator();
while (iterator.hasNext()) {
RevCommit currentCommit = iterator.next();
if (currentCommit.getId().getName().equals(commitId)) {
if (currentCommit.getParentCount() > 0) {
parentCommit = currentCommit.getParent(0);
}
break;
}
}
if (null == parentCommit) {
throw new ASWException(RCode.GIT_PARENT_COMMITID_NOT_FOUND);
}
return parentCommit;
}
}
/**
* Returns the merge base of two commits or null if there is no common ancestry.
* 返回两个提交的合并基础,如果没有共同祖先,则返回 null。
*
* @param repository
* @param commitIdA
* @param commitIdB
* @return the commit id of the merge base or null if there is no common base
*/
public static RevCommit getMergeBaseCommit(Repository repository, ObjectId commitIdA, ObjectId commitIdB) {
try (RevWalk rw = new RevWalk(repository)) {
RevCommit a = rw.lookupCommit(commitIdA);
RevCommit b = rw.lookupCommit(commitIdB);
rw.setRevFilter(RevFilter.MERGE_BASE);
rw.markStart(a);
rw.markStart(b);
RevCommit mergeBase = rw.next();
if (mergeBase == null) {
return null;
}
return mergeBase;
} catch (Exception e) {
log.error(e, "Failed to determine merge base");
throw new RuntimeException(e);
}
}
/**
* commit range
* 沿着 parent(0) 的主路径提交列表,忽略所有分支路径的提交
*
* @param repository
* @param newCommitId
* @param oldCommitId
* @return
* @throws IOException
*/
public static List<RevCommit> listCommitRange(Repository repository, String newCommitId, String oldCommitId) throws IOException {
log.info("[listCommitRange] [begin] [repository: {}] [newCommitId: {}] [oldCommitId: {}]", repository, newCommitId, oldCommitId);
List<RevCommit> commitList = T.ListUtil.list(true);
try (RevWalk revWalk = new RevWalk(repository)) {
RevCommit revCommit = revWalk.parseCommit(repository.resolve(newCommitId));
Set<ObjectId> visitedCommits = new HashSet<>();
while (revCommit != null && !visitedCommits.contains(revCommit.getId())) {
if (oldCommitId != null && revCommit.getId().getName().equals(oldCommitId)) {
break;
}
commitList.add(revCommit);
visitedCommits.add(revCommit.getId());
// 沿着 parent(0) 前进
if (revCommit.getParentCount() > 0) {
revCommit = revWalk.parseCommit(revCommit.getParent(0));
} else {
revCommit = null;
}
}
} finally {
log.info("[listCommitRange] [finshed] [repository: {}] [commits size: {}]", repository, commitList.size());
}
return commitList;
}
/**
* 文件前缀
*
* @param applicationName
* @return
*/
public static String buildFilePrefix(String applicationName) {
return T.StrUtil.concat(true, "applications/", applicationName, "/");
}
/**
* git file save path
*
* @param applicationName
* @param fileName
* @return
*/
public static String buildFilePath(String applicationName, String fileName) {
return T.StrUtil.concat(true, buildFilePrefix(applicationName), fileName);
}
/**
* 构建 DirCacheEntry
*
* @param path
* @param mode
* @param objectId
* @return
*/
public static DirCacheEntry buildDirCacheEntry(String path, FileMode mode, ObjectId objectId) {
DirCacheEntry dirCacheEntry = new DirCacheEntry(path);
dirCacheEntry.setFileMode(mode);
dirCacheEntry.setObjectId(objectId);
return dirCacheEntry;
}
public static PersonIdent buildPersonIdent(String name) {
return buildPersonIdent(name, "asw@geedgenetworks.com");
}
public static PersonIdent buildPersonIdent(String name, String email) {
return new PersonIdent(name, email);
}
/**
* 将 file object 添加到 objectDatabases 并返回 ObjectId
*
* @param repository
* @param data
* @return
* @throws IOException
*/
public static ObjectId insertBlobFileToDatabase(Repository repository, byte[] data) throws IOException {
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter()) {
ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, data);
inserter.flush();
return blobId;
}
}
/**
* commit
*
* @param repository
* @param branch
* @param treeId
* @param message
* @param personIdent
* @throws IOException
* @throws ConcurrentRefUpdateException
*/
public static void createCommit(Repository repository, String branch, ObjectId treeId, String message, PersonIdent personIdent) throws IOException, ConcurrentRefUpdateException {
try (ObjectInserter inserter = repository.getObjectDatabase().newInserter();
RevWalk revWalk = new RevWalk(repository);) {
CommitBuilder builder = new CommitBuilder();
builder.setTreeId(treeId);
builder.setMessage(message);
ObjectId branchRef = repository.resolve(branch);
if (null != branchRef) {
RevCommit parentId = revWalk.parseCommit(branchRef);
builder.setParentId(parentId);
}
builder.setAuthor(personIdent);
builder.setCommitter(personIdent);
// 插入新的提交对象
ObjectId commitId = inserter.insert(builder);
inserter.flush();
// 更新 branch 指向新的提交
RefUpdate ru = null;
RefUpdate.Result rc = null;
if (null != branchRef) {
ru = repository.updateRef(T.StrUtil.concat(true, R_HEADS, branch));
ru.setNewObjectId(commitId);
rc = ru.update();
} else {
ru = repository.updateRef(Constants.HEAD);
ru.setNewObjectId(commitId);
rc = ru.update();
}
switch (rc) {
case NEW:
case FORCED:
case FAST_FORWARD:
break;
case REJECTED:
case LOCK_FAILURE:
throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
default:
throw new JGitInternalException(MessageFormat.format(
JGitText.get().updatingRefFailed, branch, commitId.toString(),
rc));
}
}
}
/**
* commit index
*
* @param repository
* @param branch
* @param index
* @param parentId1
* @param parentId2
* @param message
* @param personIdent
* @return
* @throws IOException
* @throws ConcurrentRefUpdateException
*/
public static boolean commitIndex(Repository repository, String branch, DirCache index, ObjectId parentId1, ObjectId parentId2, String message, PersonIdent personIdent) throws IOException, ConcurrentRefUpdateException {
boolean success = false;
try (ObjectInserter odi = repository.newObjectInserter()) {
// new index
ObjectId indexTreeId = index.writeTree(odi);
// PersonIdent
CommitBuilder commit = new CommitBuilder();
commit.setAuthor(personIdent);
commit.setCommitter(personIdent);
if (null != parentId1 && null == parentId2) {
commit.setParentId(parentId1);
}
if (null != parentId1 && null != parentId2) {
commit.setParentIds(parentId1, parentId2);
}
commit.setTreeId(indexTreeId);
commit.setMessage(message);
// insert the commit into the repository
ObjectId commitId = odi.insert(commit);
odi.flush();
RefUpdate ru = repository.updateRef(T.StrUtil.concat(true, R_HEADS, branch));
ru.setNewObjectId(commitId);
RefUpdate.Result rc = ru.update();
switch (rc) {
case NEW:
case FORCED:
case FAST_FORWARD:
success = true;
break;
case REJECTED:
case LOCK_FAILURE:
throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD, ru.getRef(), rc);
default:
throw new JGitInternalException(MessageFormat.format(
JGitText.get().updatingRefFailed, branch, commitId.toString(),
rc));
}
}
return success;
}
/**
* 获取 commitIdA -> commitIdB 文件差异
*
* @param repository
* @param newCommitId
* @param oldCommitId
* @return
* @throws IOException
*/
public static List<Map<Object, Object>> getDiffFileListInCommits(Repository repository, String newCommitId, String oldCommitId) throws IOException {
log.info("[getDiffFileListInCommits] [begin] [repository: {}] [newCommitId: {}] [oldCommitId: {}]", repository, newCommitId, oldCommitId);
try (RevWalk revWalk = new RevWalk(repository);
DiffFormatter diffFormatter = new DiffFormatter(null);
) {
RevCommit oldCommit = revWalk.parseCommit(repository.resolve(oldCommitId));
RevCommit newCommit = revWalk.parseCommit(repository.resolve(newCommitId));
// oldTree
CanonicalTreeParser oldTree = new CanonicalTreeParser();
oldTree.reset(repository.newObjectReader(), oldCommit.getTree());
// newTree
CanonicalTreeParser newTree = new CanonicalTreeParser();
newTree.reset(repository.newObjectReader(), newCommit.getTree());
// diff
List<Map<Object, Object>> files = T.ListUtil.list(true);
diffFormatter.setRepository(repository);
diffFormatter.setDiffComparator(RawTextComparator.DEFAULT);
diffFormatter.setDetectRenames(true);
List<DiffEntry> diffs = diffFormatter.scan(oldTree, newTree);
for (DiffEntry diff : diffs) {
int addedLines = 0;
int deletedLines = 0;
EditList edits = diffFormatter.toFileHeader(diff).toEditList();
for (Edit edit : edits) {
switch (edit.getType()) {
case INSERT:
addedLines += edit.getLengthB();
break;
case DELETE:
deletedLines += edit.getLengthA();
break;
case REPLACE:
addedLines += edit.getLengthB();
deletedLines += edit.getLengthA();
break;
default:
break;
}
}
String oldPath = diff.getOldPath(), newPath = diff.getNewPath(), encoding = null, oldContent = null, newContent = null;
switch (diff.getChangeType()) {
case COPY:
case ADD: {
Map<Object, Object> fileContent = getFileContent(repository, newPath, diff.getNewId().toObjectId());
encoding = T.MapUtil.getStr(fileContent, "encoding", "");
newContent = T.MapUtil.getStr(fileContent, "content", "");
break;
}
case DELETE: {
Map<Object, Object> fileContent = getFileContent(repository, oldPath, diff.getOldId().toObjectId());
encoding = T.MapUtil.getStr(fileContent, "encoding", "");
oldContent = T.MapUtil.getStr(fileContent, "content", "");
break;
}
case MODIFY: {
Map<Object, Object> fileContent = getFileContent(repository, oldPath, diff.getOldId().toObjectId());
oldContent = T.MapUtil.getStr(fileContent, "content", "");
Map<Object, Object> fileContent1 = getFileContent(repository, newPath, diff.getNewId().toObjectId());
encoding = T.MapUtil.getStr(fileContent1, "encoding", "");
newContent = T.MapUtil.getStr(fileContent1, "content", "");
break;
}
case RENAME: {
if (0 != addedLines | 0 != deletedLines) {
Map<Object, Object> fileContent = getFileContent(repository, oldPath, diff.getOldId().toObjectId());
oldContent = T.MapUtil.getStr(fileContent, "content", "");
Map<Object, Object> fileContent1 = getFileContent(repository, newPath, diff.getNewId().toObjectId());
encoding = T.MapUtil.getStr(fileContent1, "encoding", "");
newContent = T.MapUtil.getStr(fileContent1, "content", "");
}
break;
}
default:
break;
}
files.add(
T.MapUtil.builder()
.put("oldPath", oldPath)
.put("newPath", newPath)
.put("addedLines", addedLines)
.put("removedLines", deletedLines)
.put("encoding", encoding)
.put("oldContent", oldContent)
.put("newContent", newContent)
.put("action", diff.getChangeType().name().toLowerCase())
.build()
);
}
return files;
}
}
/**
* 获取 sourceBranch,targetBranch 冲突文件路径
*
* @param repository
* @param srcBranch
* @param tgtBranch
* @return
* @throws IOException
*/
public static List<String> getConflictFilePathInBranches(Repository repository, String srcBranch, String tgtBranch) throws IOException {
log.info("[getConflictFileListInBranches] [begin] [repository: {}] [srcBranch: {}] [tgtBranch: {}]", repository, srcBranch, tgtBranch);
try (RevWalk revWalk = new RevWalk(repository)) {
RevCommit commitA = revWalk.parseCommit(repository.resolve(srcBranch));
RevCommit commitB = revWalk.parseCommit(repository.resolve(tgtBranch));
// Find the merge base
RevCommit mergeBaseCommit = JGitUtils.getMergeBaseCommit(repository, commitA, commitB);
String mergeBaseId = mergeBaseCommit.getName();
log.info("[getConflictFileListInBranches] [mergeBase: {}]", mergeBaseId);
RevCommit mergeBase = revWalk.parseCommit(repository.resolve(mergeBaseId));
ThreeWayMerger threeWayMerger = (ThreeWayMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
threeWayMerger.setBase(mergeBase);
boolean isOk = threeWayMerger.merge(commitA, commitB);
log.info("[getConflictFileListInBranches] [merge result] [isOk: {}]", isOk);
if (!isOk) {
List<String> unmergedPaths = ((RecursiveMerger) threeWayMerger).getUnmergedPaths();
return unmergedPaths;
}
return T.ListUtil.empty();
}
}
/**
* 获取 sourceBranch,targetBranch 冲突文件详情
*
* @param repository
* @param srcBranch
* @param tgtBranch
* @return
* @throws IOException
*/
public static List<Map<Object, Object>> getConflictFileInfoInBranches(Repository repository, String srcBranch, String tgtBranch) throws IOException {
log.info("[getConflictFileInfoInBranches] [begin] [repository: {}] [srcBranch: {}] [tgtBranch: {}]", repository, srcBranch, tgtBranch);
try (RevWalk revWalk = new RevWalk(repository);) {
RevCommit commitA = revWalk.parseCommit(repository.resolve(srcBranch));
RevCommit commitB = revWalk.parseCommit(repository.resolve(tgtBranch));
// Find the merge base
String mergeBaseId = getMergeBaseCommit(repository, commitA, commitB).getName();
log.info("[getConflictFileInfoInBranches] [mergeBase: {}]", mergeBaseId);
RevCommit mergeBase = revWalk.parseCommit(repository.resolve(mergeBaseId));
ThreeWayMerger threeWayMerger = (ThreeWayMerger) MergeStrategy.RECURSIVE.newMerger(repository, true);
threeWayMerger.setBase(mergeBase);
boolean isOk = threeWayMerger.merge(commitA, commitB);
log.info("[getConflictFileInfoInBranches] [merge result] [isOk: {}]", isOk);
if (!isOk) {
List<Map<Object, Object>> files = T.ListUtil.list(true);
Map<String, org.eclipse.jgit.merge.MergeResult<? extends Sequence>> mergeResults = ((RecursiveMerger) threeWayMerger).getMergeResults();
for (Map.Entry<String, org.eclipse.jgit.merge.MergeResult<? extends Sequence>> entry : mergeResults.entrySet()) {
String unmergedPath = entry.getKey();
// 暂不支持处理二进制文件冲突
if (isBinary(T.FileNameUtil.getName(unmergedPath))) {
throw new ASWException(RCode.GIT_BINARY_CONFLICT_ERROR);
}
org.eclipse.jgit.merge.MergeResult<? extends Sequence> mergeResult = entry.getValue();
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
MergeFormatter formatter = new MergeFormatter();
String oursName = unmergedPath;
String theirsName = unmergedPath;
formatter.formatMerge(outputStream, mergeResult, mergeBaseId, oursName, theirsName, StandardCharsets.UTF_8);
files.add(
T.MapUtil.builder()
.put("path", unmergedPath)
.put("content", outputStream.toString(StandardCharsets.UTF_8.name()))
.put("conflict", true)
.build()
);
}
}
return files;
}
return T.ListUtil.empty();
}
}
/**
* 分支合并
*
* @param centralRepository
* @param srcBranch
* @param tgtBranch
* @param author
* @param message
* @param resolveConflictFileContent
* @throws RuntimeException
* @throws GitAPIException
* @throws IOException
*/
public static void mergeBranch(Repository centralRepository, String srcBranch, String tgtBranch, String author, String message, List<Map<String, String>> resolveConflictFileContent) throws RuntimeException, GitAPIException, IOException {
log.info("[mergeBranch] [begin] [repository: {}] [srcBranch: {}] [tgtBranch: {}]", centralRepository, srcBranch, tgtBranch);
// prepare a new folder for the cloned repository
File localPath = T.FileUtil.file(net.geedge.asw.common.util.Constants.TEMP_PATH, T.StrUtil.uuid());
T.FileUtil.del(localPath);
T.FileUtil.mkdir(localPath);
// bare repository
File repoDir = centralRepository.getDirectory();
// clone
try (Git git = Git.cloneRepository()
.setBare(false)
.setURI(repoDir.getAbsolutePath())
.setDirectory(localPath)
.setCredentialsProvider(null)
.call();
Repository repository = git.getRepository();) {
StoredConfig config = repository.getConfig();
config.setString("user", null, "name", author);
config.setString("user", null, "email", "asw@geedgenetworks.com");
config.save();
// git fetch
git.fetch().call();
// checout
git.checkout()
.setCreateBranch(T.StrUtil.equals(DEFAULT_BRANCH, tgtBranch) ? false : true)
.setName(tgtBranch)
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
.setStartPoint("origin/" + tgtBranch)
.call();
// merge
MergeResult mergeResult = git.merge()
.setCommit(true)
.setMessage(message)
.setStrategy(MergeStrategy.RECURSIVE)
.setFastForward(MergeCommand.FastForwardMode.NO_FF)
.include(repository.findRef("origin/" + srcBranch))
.call();
MergeResult.MergeStatus mergeStatus = mergeResult.getMergeStatus();
log.info("[mergeBranch] [merge status: {}]", mergeStatus);
if (!mergeStatus.isSuccessful()) {
// 解决冲突
if (mergeStatus == MergeResult.MergeStatus.CONFLICTING && T.CollUtil.isNotEmpty(resolveConflictFileContent)) {
Map<String, int[][]> conflicts = mergeResult.getConflicts();
for (Map.Entry<String, int[][]> entry : conflicts.entrySet()) {
String conflictFilePath = entry.getKey();
Map<String, String> map = resolveConflictFileContent.stream()
.filter(m -> T.StrUtil.equals(conflictFilePath, T.MapUtil.getStr(m, "path")))
.findFirst()
.orElse(null);
if (null != map) {
Path filePath = Paths.get(localPath.getAbsolutePath(), conflictFilePath);
Files.write(filePath, T.MapUtil.getStr(map, "content").getBytes(StandardCharsets.UTF_8));
git.add().addFilepattern(conflictFilePath).call();
}
}
git.commit().setMessage(message).call();
} else {
// 其他类型的合并错误,抛出异常
String errorMessage = String.format("Merge failed: %s, Conflicts: %s", mergeStatus, mergeResult.getConflicts());
throw new RuntimeException(errorMessage);
}
}
// push
Iterable<PushResult> pushResultIterable = git.push()
.setRemote("origin")
.add(tgtBranch)
.call();
for (PushResult pushResult : pushResultIterable) {
for (RemoteRefUpdate update : pushResult.getRemoteUpdates()) {
if (update.getStatus() != RemoteRefUpdate.Status.OK && update.getStatus() != RemoteRefUpdate.Status.UP_TO_DATE) {
log.error("[mergeBranch] [push error] [remote: {}] [status: {}]", update.getRemoteName(), update.getStatus());
String errorMessage = "Push failed: " + update.getStatus();
throw new RuntimeException(errorMessage);
}
}
}
} finally {
T.FileUtil.del(localPath);
}
}
/**
* 归档
*
* @param repository
* @param tagName
* @param file
* @param prefix
* @param format
* @throws IOException
* @throws GitAPIException
*/
public static void archive(Repository repository, String tagName, File file, String prefix, String format) throws IOException, GitAPIException {
ArchiveFormats.registerAll();
T.FileUtil.mkdir(T.FileUtil.getParent(file, 1));
try (OutputStream out = new FileOutputStream(file)) {
try (Git git = new Git(repository)) {
git.archive()
.setTree(repository.resolve(tagName))
.setFormat(format)
.setPrefix(prefix)
.setOutputStream(out)
.call();
}
} finally {
ArchiveFormats.unregisterAll();
}
}
/**
* build asw commit info
*
* @param commit
* @return
*/
public static Map<Object, Object> buildAswCommitInfo(RevCommit commit) {
if (null == commit) {
return T.MapUtil.newHashMap();
}
Map<Object, Object> m = new LinkedHashMap<>();
m.put("id", commit.getName());
m.put("shortId", T.StrUtil.subPre(commit.getName(), 8));
m.put("createdAt", TimeUnit.SECONDS.toMillis(commit.getCommitTime()));
m.put("title", commit.getShortMessage());
m.put("message", commit.getFullMessage());
List<String> parentIds = Arrays.stream(commit.getParents()).map(RevCommit::getName).collect(Collectors.toList());
m.put("parentIds", parentIds);
PersonIdent authorIdent = commit.getAuthorIdent();
m.put("authorName", authorIdent.getName());
m.put("authorEmail", authorIdent.getEmailAddress());
m.put("authoredDate", authorIdent.getWhen().getTime());
PersonIdent committerIdent = commit.getCommitterIdent();
m.put("committerName", committerIdent.getName());
m.put("committerEmail", committerIdent.getEmailAddress());
m.put("committedDate", committerIdent.getWhen().getTime());
return m;
}
/**
* is binary file
*
* @param filename
* @return
*/
public static boolean isBinary(String filename) {
String extension = FilenameUtils.getExtension(filename);
List<String> split = T.StrUtil.split(textExtensions, ",");
return !split.contains(extension.toLowerCase());
}
/**
* 根据 path,objectId 读取文件内容
* 响应 Map,key=encoding,content
*
* @param repository
* @param path
* @param objectId
* @return
* @throws IOException
*/
public static Map<Object, Object> getFileContent(Repository repository, String path, ObjectId objectId) throws IOException {
String encoding = null, content = null;
ObjectLoader loader = repository.open(objectId);
if (isBinary(T.FileNameUtil.getName(path))) {
encoding = "base64";
content = Base64.getEncoder().encodeToString(loader.getBytes());
} else {
content = T.StrUtil.utf8Str(loader.getBytes());
}
return T.MapUtil.builder()
.put("encoding", encoding)
.put("content", content)
.build();
}
}

View File

@@ -0,0 +1,37 @@
package net.geedge.asw.module.app.util;
import net.geedge.asw.common.util.T;
import java.io.File;
public class PkgConstant {
/**
* android packages file dir
*/
public static File APK_FILES_DIR = T.FileUtil.file(T.WebPathUtil.getRootPath(), "apk_files");
/**
* support platform
*/
public enum Platform {
ANDROID("android"),
IOS("ios"),
WINDOWS("windows"),
LINUX("linux");
private String value;
Platform(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}

View File

@@ -0,0 +1,26 @@
package net.geedge.asw.module.attribute.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.R;
import net.geedge.asw.module.attribute.service.IAttributeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/attribute")
public class AttributeController {
@Autowired
private IAttributeService attributeService;
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
Page page = attributeService.queryList(params);
return R.ok(page);
}
}

View File

@@ -0,0 +1,15 @@
package net.geedge.asw.module.attribute.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.attribute.entity.AttributeEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
public interface AttributeDao extends BaseMapper<AttributeEntity> {
List<AttributeEntity> queryList(@Param("params") Map<String, Object> params);
}

View File

@@ -0,0 +1,43 @@
package net.geedge.asw.module.attribute.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
@Data
@TableName("attribute_dict")
public class AttributeEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String type;
private String protocol;
private String layer;
private String stage;
private String objectType;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
}

View File

@@ -0,0 +1,13 @@
package net.geedge.asw.module.attribute.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.attribute.entity.AttributeEntity;
import java.util.Map;
public interface IAttributeService extends IService<AttributeEntity> {
Page queryList(Map<String, Object> params);
AttributeEntity queryAttribute(String name);
}

View File

@@ -0,0 +1,36 @@
package net.geedge.asw.module.attribute.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.config.Query;
import net.geedge.asw.module.attribute.dao.AttributeDao;
import net.geedge.asw.module.attribute.entity.AttributeEntity;
import net.geedge.asw.module.attribute.service.IAttributeService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class AttributeServiceImpl extends ServiceImpl<AttributeDao, AttributeEntity> implements IAttributeService {
@Override
public Page queryList(Map<String, Object> params) {
Page page = new Query(AttributeEntity.class).getPage(params);
List<AttributeEntity> attributeList = this.getBaseMapper().queryList(params);
page.setRecords(attributeList);
return page;
}
@Override
public AttributeEntity queryAttribute(String name) {
AttributeEntity one = this.getOne(new LambdaQueryWrapper<AttributeEntity>()
.eq(AttributeEntity::getName, name)
.last("limit 1")
);
return one;
}
}

View File

@@ -0,0 +1,222 @@
package net.geedge.asw.module.environment.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.*;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import net.geedge.asw.module.environment.util.EnvironmentUtil;
import net.geedge.asw.module.job.entity.PcapEntity;
import net.geedge.asw.module.job.service.IPcapService;
import net.geedge.asw.module.job.util.JobConstant;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
@RestController
@RequestMapping("/api/v1/env")
public class EnvironmentController {
private static final Log log = Log.get();
@Autowired
private IEnvironmentService environmentService;
@Autowired
private IEnvironmentSessionService environmentSessionService;
@Autowired
private ISysUserService userService;
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IPcapService pcapService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
EnvironmentEntity entity = environmentService.queryInfo(id);
return R.ok().putData("record", entity);
}
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull()
.and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
Page page = environmentService.queryList(params);
return R.ok(page);
}
@GetMapping("/mgt")
public R queryList(@RequestParam Map<String, Object> params) {
Page page = environmentService.findEnvironmentByCurrentUserId(params);
return R.ok().putData(page);
}
@PostMapping("/mgt")
public R save(@RequestBody EnvironmentEntity entity) {
EnvironmentEntity env = environmentService.saveEnv(entity);
return R.ok().putData("record", env.getId());
}
@PutMapping("/mgt")
public R update(@RequestBody EnvironmentEntity entity) {
EnvironmentEntity env = environmentService.updateEnv(entity);
return R.ok().putData("record", env.getId());
}
@DeleteMapping("/mgt")
public R delete(String ids) {
T.VerifyUtil.is(ids).notEmpty();
environmentService.removeEnv(T.ListUtil.of(ids.split(",")));
return R.ok();
}
@PostMapping("/test")
public R testConnect(@RequestBody EnvironmentEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getParam()).notEmpty(RCode.PARAM_CANNOT_EMPTY);
JSONObject jsonObject = entity.getParamJSONObject();
String url = jsonObject.getStr("url");
String token = jsonObject.getStr("token");
if (T.StrUtil.hasEmpty(url, token)) {
return R.error(RCode.PARAM_CANNOT_EMPTY);
}
try {
HttpRequest request = T.HttpUtil.createGet(String.format("%s/api/v1/env/status", url));
request.header("Authorization", token);
HttpResponse response = request.execute();
log.info("[testConnect] [status: {}]", response.getStatus());
if (response.getStatus() == 401) {
return R.error(401, "Unauthorized");
}
if (response.isOk()) {
return R.ok();
}
} catch (Exception e) {
log.error(e);
return R.error(RCode.ERROR);
}
return R.error(RCode.ERROR);
}
@RequestMapping(value = "/{envId}/session/{sessionId}/**", method ={ RequestMethod.GET, RequestMethod.POST, RequestMethod.DELETE}, headers = "Upgrade!=websocket")
public void agentEvn(@PathVariable("envId") String envId, @PathVariable("sessionId") String sessionId, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
EnvironmentSessionEntity session = environmentSessionService.getOne(new LambdaQueryWrapper<EnvironmentSessionEntity>().eq(EnvironmentSessionEntity::getId, sessionId).eq(EnvironmentSessionEntity::getStatus, 1));
if (T.ObjectUtil.isNull(session)){
throw new ASWException(RCode.ENVIRONMENT_SESSION_NOT_EXIST);
}
EnvironmentEntity environment = environmentService.getById(session.getEnvId());
if (T.ObjectUtil.isNull(environment)) {
throw new ASWException(RCode.ENVIRONMENT_NOT_EXIST);
}
EnvironmentUtil.getForObject(environment, request, response, sessionId);
}
@GetMapping("/mySession")
public R mySession(@RequestParam Map params){
Page page = environmentService.mySession(params);
return R.ok(page);
}
@PostMapping("/{envId}/session")
public R saveSession(@PathVariable("envId") String envId, @RequestParam String workspaceId){
EnvironmentSessionEntity session = environmentSessionService.saveSession(envId, workspaceId);
return R.ok().putData("record", session.getId());
}
@GetMapping("/{envId}/session/{sessionId}")
public R querySession(@PathVariable("envId") String envId, @PathVariable("sessionId") String sessionId, @RequestParam String workspaceId){
EnvironmentSessionEntity session = environmentSessionService.getOne(new LambdaQueryWrapper<EnvironmentSessionEntity>().eq(EnvironmentSessionEntity::getId, sessionId).eq(EnvironmentSessionEntity::getWorkspaceId, workspaceId));
session.setEnv(environmentService.getById(envId));
session.setWorkspace(workspaceService.getById(workspaceId));
session.setUser(userService.getById(session.getUserId()));
return R.ok().putData("record", session);
}
@DeleteMapping("/{envId}/session/{sessionId}")
@Transactional
public R removeSession(@PathVariable("envId") String envId, @PathVariable("sessionId") String sessionId, @RequestParam String workspaceId) {
environmentService.removeSession(sessionId);
return R.ok();
}
@DeleteMapping("/{envId}/session/{sessionId}/pcap/{pcapId}")
public R stopTcpdump(@PathVariable("envId") String envId,
@PathVariable("sessionId") String sessionId,
@PathVariable("pcapId") String pcapId,
@RequestParam Map param) throws IOException, ServletException {
EnvironmentSessionEntity session = environmentSessionService.getOne(new LambdaQueryWrapper<EnvironmentSessionEntity>().eq(EnvironmentSessionEntity::getId, sessionId).eq(EnvironmentSessionEntity::getStatus, 1));
if (T.ObjectUtil.isNull(session)){
throw new ASWException(RCode.ENVIRONMENT_SESSION_NOT_EXIST);
}
EnvironmentEntity environment = environmentService.getById(envId);
if (T.ObjectUtil.isNull(environment)) {
throw new ASWException(RCode.ENVIRONMENT_NOT_EXIST);
}
// build query param
Map params = T.MapUtil.builder().put("id", pcapId).put("returnFile", T.MapUtil.getBool(param, "savePcap")).build();
ResponseEntity<byte[]> responseEntity = EnvironmentUtil.stopTcpdump(environment, params);
if (T.MapUtil.getBool(param, "savePcap")){
// save pcap to workspace
WorkspaceEntity workspace = workspaceService.getById(session.getWorkspaceId());
String pcapName = T.StrUtil.emptyToDefault(T.MapUtil.getStr(param,"pcapName"), pcapId);
File destination = T.FileUtil.file(T.WebPathUtil.getRootPath(), workspace.getId(), T.StrUtil.concat(true,pcapName, ".pcap"));
if (destination.exists()){
String formatTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
destination = T.FileUtil.file(T.WebPathUtil.getRootPath(), workspace.getId(), T.StrUtil.concat(true, pcapName, "-", formatTime, ".pcap"));
}
// create empty file
destination = FileUtil.touch(destination);
if (ArrayUtil.isNotEmpty(responseEntity.getBody())){
FileOutputStream fos = new FileOutputStream(destination);
T.IoUtil.write(fos,true, responseEntity.getBody());
}
log.info("save pcap to path:{}", destination.getAbsolutePath());
// save entity
PcapEntity entity = new PcapEntity();
entity.setId(pcapId);
entity.setName(destination.getName());
entity.setSize(destination.length());
entity.setStatus(JobConstant.PcapStatus.UPLOADED.getValue());
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setWorkspaceId(workspace.getId());
entity.setPath(destination.getPath());
entity.setMd5(destination.length() == 0 ? Constants.EMPTY_FILE_MD5 : T.DigestUtil.md5Hex(destination));
pcapService.save(entity);
}
return R.ok();
}
}

View File

@@ -0,0 +1,17 @@
package net.geedge.asw.module.environment.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface EnvironmentDao extends BaseMapper<EnvironmentEntity> {
List<EnvironmentEntity> queryList(Page page, Map<String, Object> params);
List<EnvironmentEntity> mySession(Page page, Map params);
}

View File

@@ -0,0 +1,14 @@
package net.geedge.asw.module.environment.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface EnvironmentSessionDao extends BaseMapper<EnvironmentSessionEntity> {
List<EnvironmentSessionEntity> queryListByUsed();
}

View File

@@ -0,0 +1,9 @@
package net.geedge.asw.module.environment.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.environment.entity.EnvironmentWorkspaceEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EnvironmentWorkspaceDao extends BaseMapper<EnvironmentWorkspaceEntity> {
}

View File

@@ -0,0 +1,66 @@
package net.geedge.asw.module.environment.entity;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import java.util.List;
@Data
@TableName("environment")
public class EnvironmentEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String location;
private String platform;
private Object param;
private String description;
private Integer status;
private Long lastHealthCheck;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
@TableField(exist = false)
private String workspaceId;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
@TableField(exist = false)
private JSONObject useUser;
@TableField(exist = false)
private List<WorkspaceEntity> workspaces;
@TableField(exist = false)
private EnvironmentSessionEntity session;
@TableField(exist = false)
private List<String> workspaceIds;
@JsonIgnore
public String getParamStr() {
return null == this.param ? "{}" : T.JSONUtil.toJsonStr(this.param);
}
@JsonIgnore
public JSONObject getParamJSONObject() {
return null == this.param ? new JSONObject() : T.JSONUtil.parseObj(this.getParamStr());
}
}

View File

@@ -0,0 +1,36 @@
package net.geedge.asw.module.environment.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
@Data
@TableName("environment_session")
public class EnvironmentSessionEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String envId;
private String userId;
private Integer status;
private String jobId;
private Long startTimestamp;
private Long endTimestamp;
private String workspaceId;
@TableField(exist = false)
private EnvironmentEntity env;
@TableField(exist = false)
private WorkspaceEntity workspace;
@TableField(exist = false)
private SysUserEntity user;
}

View File

@@ -0,0 +1,19 @@
package net.geedge.asw.module.environment.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("environment_workspace")
public class EnvironmentWorkspaceEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String envId;
private String workspaceId;
private Long createTimestamp;
private String createUserId;
}

View File

@@ -0,0 +1,124 @@
package net.geedge.asw.module.environment.job;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import org.apache.commons.lang3.time.StopWatch;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@DisallowConcurrentExecution
public class JobEnvironmentStatusChecker extends QuartzJobBean {
private static final Log log = Log.get();
@Autowired
private IEnvironmentService envService;
@Autowired
private IEnvironmentSessionService envSessionService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
Thread.currentThread().setName("JobEnvironmentStatusChecker");
log.info("[JobEnvironmentStatusChecker] [begin]");
StopWatch sw = new StopWatch();
sw.start();
try {
this.environmentStatusChecker();
} catch (Exception e) {
log.error(e, "[JobEnvironmentStatusChecker] [error]");
} finally {
sw.stop();
}
log.info("[JobEnvironmentStatusChecker] [finshed] [Run Time: {}]", sw.toString());
}
/**
* environment status checker
* <p>
* 1. update entity status、lastHealthCheck
* 2. close the offline env session
*/
@Transactional(rollbackFor = Exception.class)
public void environmentStatusChecker() {
List<EnvironmentEntity> list = envService.list();
for (EnvironmentEntity entity : list) {
Thread.ofVirtual().start(() -> {
String result = null;
try {
JSONObject paramJSONObject = entity.getParamJSONObject();
String url = paramJSONObject.getStr("url");
String token = paramJSONObject.getStr("token");
HttpRequest request = T.HttpUtil.createGet(String.format("%s/api/v1/env/status", url));
request.header("Authorization", token);
HttpResponse response = request.execute();
log.info("[environmentStatusChecker] [env: {}] [status: {}]", entity.getId(), response.getStatus());
if (response.isOk()) {
result = response.body();
}
} catch (RuntimeException e) {
log.error(e, "[environmentStatusChecker] [request api error] [env: {}]", entity.getId());
}
if (log.isDebugEnabled()) {
log.debug("[environmentStatusChecker] [env: {}] [result: {}]", entity.getId(), result);
}
entity.setStatus(0);
entity.setLastHealthCheck(System.currentTimeMillis());
if (T.StrUtil.isNotEmpty(result)) {
try {
JSONObject jsonObject = T.JSONUtil.parseObj(result);
if (T.ObjectUtil.equal(RCode.SUCCESS.getCode(), jsonObject.getInt("code"))) {
JSONObject data = jsonObject.getJSONObject("data");
String status = data.getStr("status");
if (T.StrUtil.equals("online", status)) {
entity.setStatus(1);
}
}
} catch (Exception e) {
log.error(e, "[environmentStatusChecker] [parse result error] [env: {}]", entity.getId());
}
}
// update entity status、lastHealthCheck
envService.update(new LambdaUpdateWrapper<EnvironmentEntity>()
.set(EnvironmentEntity::getStatus, entity.getStatus())
.set(EnvironmentEntity::getLastHealthCheck, entity.getLastHealthCheck())
.eq(EnvironmentEntity::getId, entity.getId())
);
// close the offline env session
if (0 == entity.getStatus()) {
envSessionService.update(new LambdaUpdateWrapper<EnvironmentSessionEntity>()
.set(EnvironmentSessionEntity::getStatus, 2)
.set(EnvironmentSessionEntity::getEndTimestamp, System.currentTimeMillis())
.eq(EnvironmentSessionEntity::getStatus, 1)
.eq(EnvironmentSessionEntity::getEnvId, entity.getId())
);
}
});
}
}
}

View File

@@ -0,0 +1,27 @@
package net.geedge.asw.module.environment.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import java.util.List;
import java.util.Map;
public interface IEnvironmentService extends IService<EnvironmentEntity>{
EnvironmentEntity queryInfo(String id);
Page queryList(Map<String, Object> params);
Page findEnvironmentByCurrentUserId(Map<String, Object> params);
void removeEnv(List<String> ids);
Page mySession(Map params);
EnvironmentEntity saveEnv(EnvironmentEntity entity);
EnvironmentEntity updateEnv(EnvironmentEntity entity);
void removeSession(String sessionId);
}

View File

@@ -0,0 +1,13 @@
package net.geedge.asw.module.environment.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import java.util.List;
public interface IEnvironmentSessionService extends IService<EnvironmentSessionEntity>{
EnvironmentSessionEntity saveSession(String envId, String workspaceId);
List<EnvironmentSessionEntity> queryListByUsed();
}

View File

@@ -0,0 +1,7 @@
package net.geedge.asw.module.environment.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.environment.entity.EnvironmentWorkspaceEntity;
public interface IEnvironmentWorkspaceService extends IService<EnvironmentWorkspaceEntity> {
}

View File

@@ -0,0 +1,232 @@
package net.geedge.asw.module.environment.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.config.Query;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.attribute.entity.AttributeEntity;
import net.geedge.asw.module.environment.dao.EnvironmentDao;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import net.geedge.asw.module.environment.entity.EnvironmentWorkspaceEntity;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import net.geedge.asw.module.environment.service.IEnvironmentWorkspaceService;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
public class EnvironmentServiceImpl extends ServiceImpl<EnvironmentDao, EnvironmentEntity> implements IEnvironmentService {
private static final Log log = Log.get();
@Autowired
private ISysUserService sysUserService;
@Autowired
private IEnvironmentSessionService environmentSessionService;
@Autowired
private IEnvironmentWorkspaceService environmentWorkspaceService;
@Autowired
private IWorkspaceService workspaceService;
@Override
public EnvironmentEntity queryInfo(String id) {
EnvironmentEntity environment = this.getById(id);
T.VerifyUtil.is(environment).notNull(RCode.SYS_RECORD_NOT_FOUND);
// param
environment.setParam(environment.getParamJSONObject());
// user
SysUserEntity createUser = sysUserService.getById(environment.getCreateUserId());
SysUserEntity updateUser = sysUserService.getById(environment.getUpdateUserId());
createUser.setPwd(null);
updateUser.setPwd(null);
environment.setCreateUser(createUser);
environment.setUpdateUser(updateUser);
// workspaces
List<EnvironmentWorkspaceEntity> environmentWorkspaceList = environmentWorkspaceService.list(new LambdaQueryWrapper<EnvironmentWorkspaceEntity>().eq(EnvironmentWorkspaceEntity::getEnvId, id));
if (T.CollUtil.isNotEmpty(environmentWorkspaceList)) {
List<String> workspaceIds = environmentWorkspaceList.stream().map(x -> x.getWorkspaceId()).toList();
List<WorkspaceEntity> workspaceList = workspaceService.list(new LambdaQueryWrapper<WorkspaceEntity>().in(WorkspaceEntity::getId, workspaceIds));
environment.setWorkspaces(workspaceList);
}
// session
EnvironmentSessionEntity deviceSession = environmentSessionService.getOne(new LambdaQueryWrapper<EnvironmentSessionEntity>()
.eq(EnvironmentSessionEntity::getEnvId, environment.getId())
.eq(EnvironmentSessionEntity::getStatus, 1));
if (null != deviceSession) {
SysUserEntity useUser = sysUserService.getById(deviceSession.getUserId());
useUser.setPwd(null);
WorkspaceEntity workspace = workspaceService.getById(deviceSession.getWorkspaceId());
deviceSession.setUser(useUser);
deviceSession.setWorkspace(workspace);
environment.setSession(deviceSession);
environment.setStatus(environment.getStatus() == 1 ? 2 : environment.getStatus());
}
return environment;
}
@Override
public Page queryList(Map<String, Object> params) {
Page page = new Query(EnvironmentEntity.class).getPage(params);
List<EnvironmentEntity> packageList = this.getBaseMapper().queryList(page, params);
List<EnvironmentSessionEntity> sessionEntityList = environmentSessionService.queryListByUsed();
List<String> envIdList = sessionEntityList.stream().map(x -> x.getEnvId()).toList();
Map<String, EnvironmentSessionEntity> sessionByEnvId = sessionEntityList.stream().collect(Collectors.toMap(EnvironmentSessionEntity::getEnvId, Function.identity()));
for (EnvironmentEntity entity : packageList) {
entity.setParam(entity.getParamJSONObject());
entity.setStatus(envIdList.contains(entity.getId()) ? 2 : entity.getStatus());
entity.setSession(sessionByEnvId.get(entity.getId()));
}
page.setRecords(packageList);
return page;
}
@Override
public Page findEnvironmentByCurrentUserId(Map<String, Object> params) {
params.put("currentUserId", StpUtil.getLoginIdAsString());
Page page = this.queryList(params);
return page;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeEnv(List<String> ids) {
// remove
this.remove(new LambdaQueryWrapper<EnvironmentEntity>().in(EnvironmentEntity::getId, ids).eq(EnvironmentEntity::getCreateUserId, StpUtil.getLoginIdAsString()));
// session
environmentSessionService.remove(new LambdaQueryWrapper<EnvironmentSessionEntity>().in(EnvironmentSessionEntity::getEnvId, ids));
//device workspace
environmentWorkspaceService.remove(new LambdaQueryWrapper<EnvironmentWorkspaceEntity>().in(EnvironmentWorkspaceEntity::getEnvId, ids));
}
@Override
public Page mySession(Map params) {
String currentUserId = StpUtil.getLoginIdAsString();
params.put("currentUserId", currentUserId);
Page page = new Query(EnvironmentEntity.class).getPage(params);
List<EnvironmentSessionEntity> sessionEntityList = environmentSessionService.queryListByUsed();
List<EnvironmentEntity> packageList = this.getBaseMapper().mySession(page, params);
List<String> envIdList = sessionEntityList.stream().map(x -> x.getEnvId()).toList();
Map<String, EnvironmentSessionEntity> sessionByEnvId = sessionEntityList.stream().collect(Collectors.toMap(EnvironmentSessionEntity::getEnvId, Function.identity()));
for (EnvironmentEntity entity : packageList) {
entity.setParam(entity.getParamJSONObject());
entity.setStatus(envIdList.contains(entity.getId()) ? 2 : entity.getStatus());
entity.setSession(sessionByEnvId.get(entity.getId()));
}
page.setRecords(packageList);
return page;
}
@Override
@Transactional(rollbackFor = Exception.class)
public EnvironmentEntity saveEnv(EnvironmentEntity entity) {
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setParam(entity.getParamStr());
this.save(entity);
// save env workspace
if (T.CollUtil.isNotEmpty(entity.getWorkspaceIds())){
List<EnvironmentWorkspaceEntity> list = T.ListUtil.list(false);
for (String workspaceId : entity.getWorkspaceIds()) {
EnvironmentWorkspaceEntity environmentWorkspace = new EnvironmentWorkspaceEntity();
environmentWorkspace.setEnvId(entity.getId());
environmentWorkspace.setWorkspaceId(workspaceId);
environmentWorkspace.setCreateTimestamp(System.currentTimeMillis());
environmentWorkspace.setCreateUserId(StpUtil.getLoginIdAsString());
list.add(environmentWorkspace);
}
environmentWorkspaceService.saveBatch(list);
}
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public EnvironmentEntity updateEnv(EnvironmentEntity entity) {
EnvironmentEntity environment = this.getOne(new LambdaQueryWrapper<EnvironmentEntity>().eq(EnvironmentEntity::getId, entity.getId()).eq(EnvironmentEntity::getCreateUserId, StpUtil.getLoginIdAsString()));
if (T.ObjectUtil.isNull(environment)) {
throw new ASWException(RCode.ENVIRONMENT_NOT_EXIST);
}
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setParam(entity.getParamStr());
this.updateById(entity);
environmentWorkspaceService.remove(new LambdaQueryWrapper<EnvironmentWorkspaceEntity>().eq(EnvironmentWorkspaceEntity::getEnvId, entity.getId()));
// save env workspace
if (T.CollUtil.isNotEmpty(entity.getWorkspaceIds())){
List<EnvironmentWorkspaceEntity> list = T.ListUtil.list(false);
for (String workspaceId : entity.getWorkspaceIds()) {
EnvironmentWorkspaceEntity environmentWorkspace = new EnvironmentWorkspaceEntity();
environmentWorkspace.setEnvId(entity.getId());
environmentWorkspace.setWorkspaceId(workspaceId);
environmentWorkspace.setCreateTimestamp(System.currentTimeMillis());
environmentWorkspace.setCreateUserId(StpUtil.getLoginIdAsString());
list.add(environmentWorkspace);
}
environmentWorkspaceService.saveBatch(list);
}
return entity;
}
@Override
public void removeSession(String sessionId) {
EnvironmentSessionEntity session = environmentSessionService.getById(sessionId);
WebSocketSession novncSession = Constants.ENV_NOVNC_WEBSOCKET_SESSION.get(sessionId);
WebSocketSession terminalSession = Constants.ENV_TERMINAL_WEBSOCKET_SESSION.get(sessionId);
// 根据 session 找到 novncSession&terminalSession ,更新状态,设置结束时间
session.setEndTimestamp(System.currentTimeMillis());
session.setStatus(2);
environmentSessionService.updateById(session);
try {
if (T.ObjectUtil.isNotEmpty(novncSession)) {
Constants.ENV_NOVNC_WEBSOCKET_SESSION.remove(sessionId);
novncSession.close(CloseStatus.NORMAL.withReason("Administrator disconnected."));
}
if (T.ObjectUtil.isNotEmpty(terminalSession)) {
Constants.ENV_TERMINAL_WEBSOCKET_SESSION.remove(sessionId);
terminalSession.close(CloseStatus.NORMAL.withReason("Administrator disconnected."));
}
} catch (IOException e) {
log.error(e, "RemoveSession send exit prompt error sessionId: {}", sessionId);
}
}
}

View File

@@ -0,0 +1,95 @@
package net.geedge.asw.module.environment.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.environment.dao.EnvironmentSessionDao;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import net.geedge.asw.module.environment.util.EnvironmentUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class EnvironmentSessionServiceImpl extends ServiceImpl<EnvironmentSessionDao, EnvironmentSessionEntity> implements IEnvironmentSessionService {
private static final Log log = Log.get();
@Autowired
private IEnvironmentService environmentService;
@Override
public EnvironmentSessionEntity saveSession(String envId, String workspaceId) {
List<EnvironmentSessionEntity> sessionEntityList = this.list(new LambdaQueryWrapper<EnvironmentSessionEntity>()
.eq(EnvironmentSessionEntity::getEnvId, envId)
.eq(EnvironmentSessionEntity::getWorkspaceId, workspaceId)
.eq(EnvironmentSessionEntity::getStatus, 1));
if (T.CollectionUtil.isNotEmpty(sessionEntityList)) {
throw new ASWException(RCode.ENVIRONMENT_USED);
}
boolean isFree = this.checkEnvironmentStatus(envId);
if (!isFree) {
throw new ASWException(RCode.ENVIRONMENT_STATUS_ERROR);
}
EnvironmentSessionEntity session = new EnvironmentSessionEntity();
session.setEnvId(envId);
session.setWorkspaceId(workspaceId);
session.setStatus(1);
session.setStartTimestamp(System.currentTimeMillis());
session.setUserId(StpUtil.getLoginIdAsString());
this.save(session);
return session;
}
private boolean checkEnvironmentStatus(String envId) {
boolean isFree = true;
EnvironmentEntity environment = environmentService.getById(envId);
if (T.ObjectUtil.isNull(environment)) {
throw new ASWException(RCode.ENVIRONMENT_NOT_EXIST);
}
if (environment.getStatus() != 1){
isFree = false;
}
String resultJsonStr = T.StrUtil.EMPTY_JSON;
try {
resultJsonStr = EnvironmentUtil.requestGet(environment, Constants.ENV_API_STATUS_PATH, null, String.class);
}catch (Exception e){
log.error(e, "CheckEnvironmentStatus. request environment status api error environment: {}]", T.JSONUtil.toJsonStr(environment));
isFree = false;
}
log.info("CheckEnvironmentStatus. environment status api result: {}", resultJsonStr);
Map resultObj = T.JSONUtil.toBean(resultJsonStr, Map.class);
if (T.BooleanUtil.or(
T.MapUtil.isEmpty(resultObj),
T.ObjectUtil.notEqual(RCode.SUCCESS.getCode(), resultObj.get("code")))) {
isFree = false;
} else {
Map data = T.MapUtil.get(resultObj, "data", Map.class);
String status = T.MapUtil.getStr(data, "status");
if (!T.StrUtil.equalsIgnoreCase(status, "online")){
isFree = false;
}
}
return isFree;
}
@Override
public List<EnvironmentSessionEntity> queryListByUsed() {
List<EnvironmentSessionEntity> sessionEntityList = this.getBaseMapper().queryListByUsed();
return sessionEntityList;
}
}

View File

@@ -0,0 +1,11 @@
package net.geedge.asw.module.environment.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.module.environment.dao.EnvironmentWorkspaceDao;
import net.geedge.asw.module.environment.entity.EnvironmentWorkspaceEntity;
import net.geedge.asw.module.environment.service.IEnvironmentWorkspaceService;
import org.springframework.stereotype.Service;
@Service
public class EnvironmentWorkspaceServiceImpl extends ServiceImpl<EnvironmentWorkspaceDao, EnvironmentWorkspaceEntity> implements IEnvironmentWorkspaceService {
}

View File

@@ -0,0 +1,243 @@
package net.geedge.asw.module.environment.util;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.net.url.UrlPath;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.Header;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
@Configuration
@SuppressWarnings("all")
public class EnvironmentUtil {
private static Log log = Log.get();
private static RestTemplate restTemplate;
public static <T> T requestGet(EnvironmentEntity environment, String path, String queryString, Class<T> responseType) {
return request(environment, HttpMethod.GET, path, queryString, null, responseType);
}
public static <T> T request(EnvironmentEntity environment, HttpMethod method, String path, String queryString, Object body,
Class<T> responseType) {
JSONObject jsonObject = environment.getParamJSONObject();
String url = jsonObject.getStr("url");
String token = jsonObject.getStr("token");
String urlString = UrlBuilder.of(url)
.setPath(UrlPath.of(path, Charset.forName("UTF-8")))
.setQuery(UrlQuery.of(queryString, Charset.forName("UTF-8"), false, true))
.setCharset(StandardCharsets.UTF_8).toString();
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION,token);
HttpEntity httpEntity = body == null ? new HttpEntity(headers) : new HttpEntity(body, headers);
// 发送 请求
return request(urlString, method, token, body, responseType);
}
public static <T> T request(String url, HttpMethod method, String token, Object body, Class<T> responseType) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, token);
HttpEntity httpEntity = body == null ? new HttpEntity(headers) : new HttpEntity(body, headers);
// 发送 请求
ResponseEntity<T> exchange = null;
try {
exchange = restTemplate.exchange(new URI(url), method, httpEntity, responseType);
} catch (URISyntaxException e) {
log.error(e);
}
return exchange.getBody();
}
public static <T> T requestGet(String url, String token, Class<T> responseType) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, token);
HttpEntity httpEntity = new HttpEntity(headers);
// 发送 请求
ResponseEntity<T> exchange = restTemplate.exchange(url, HttpMethod.GET, httpEntity, responseType);
return exchange.getBody();
}
/**
* agent stop tcpdump
* @param environment
* @param params
* @return
* @throws IOException
* @throws ServletException
*/
public static ResponseEntity<byte[]> stopTcpdump(EnvironmentEntity environment, Map params) throws IOException, ServletException {
JSONObject jsonObject = environment.getParamJSONObject();
String url = jsonObject.getStr("url");
String token = jsonObject.getStr("token");
String urlStr = UrlBuilder.of(url)
.setPath(UrlPath.of(Constants.ENV_API_TCPDUMP_PATH, Charset.forName("UTF-8")))
.setQuery(UrlQuery.of(params))
.setCharset(StandardCharsets.UTF_8).toString();
// token
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, token);
HttpEntity httpEntity = new HttpEntity(headers);
ResponseEntity<byte[]> responseEntity = null;
try {
responseEntity = restTemplate.exchange(new URI(urlStr), HttpMethod.DELETE, httpEntity, byte[].class);
} catch (Exception e) {
log.error(e, "stop tcpdump request error. url:{}", urlStr);
String message = e.getMessage();
if (ObjectUtil.isNotNull(e.getCause())) {
message = e.getCause().getMessage();
}
throw new ASWException(message, HttpStatus.INTERNAL_SERVER_ERROR.value());
}
int statusCode = responseEntity.getStatusCodeValue();
log.info("stop tcpdump request url:{}, responseStatus:{}", urlStr, statusCode);
return responseEntity;
}
/**
* env api agent
* @param device
* @param request
* @param response
* @param sessionId
* @throws IOException
* @throws ServletException
*/
public static void getForObject(EnvironmentEntity device, HttpServletRequest request, HttpServletResponse response, String sessionId) throws IOException, ServletException {
// path
String[] paths = request.getServletPath().split(sessionId);
String path = Arrays.asList(paths).getLast();
path = path.startsWith("/") ? (String.format("%s%s", Constants.ENV_API_PREFIX, path))
: (String.format("%s/%s", Constants.ENV_API_PREFIX, path));
// host port token
JSONObject jsonObject = device.getParamJSONObject();
String url = jsonObject.getStr("url");
String token = jsonObject.getStr("token");
// query param
String queryString = request.getQueryString();
queryString = StrUtil.isNotBlank(queryString) ? queryString : "";
queryString = URLUtil.decode(queryString);
String urlStr = UrlBuilder.of(url)
.setPath(UrlPath.of(path, Charset.forName("UTF-8")))
.setQuery(UrlQuery.of(queryString, Charset.forName("UTF-8"), false, true))
.setCharset(StandardCharsets.UTF_8).toString();
// token
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String hn = headerNames.nextElement();
if (Constants.AUTH_TOKEN_CODE.equalsIgnoreCase(hn)) {
continue;
}
headers.add(hn, request.getHeader(hn));
}
headers.add(HttpHeaders.AUTHORIZATION, token);
// body
byte[] body = T.IoUtil.readBytes(request.getInputStream());
HttpEntity httpEntity = new HttpEntity(body, headers);
// from-data
if (request.getContentType() != null &&
request.getContentType().startsWith("multipart")) {
// 获取表单中的文件和参数
Collection<Part> parts = request.getParts();
// from 表单文件
MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
for (Part part : parts) {
String partName = part.getName();
if (part.getSubmittedFileName() != null) {
ByteArrayResource resource = new ByteArrayResource(part.getInputStream().readAllBytes()) {
@Override
public String getFilename() {
return part.getSubmittedFileName();
}
};
form.add(partName, resource);
} else {
form.add(partName, part.getInputStream().readAllBytes());
}
httpEntity = new HttpEntity(form, headers);
}
}
ResponseEntity<byte[]> responseEntity = null;
try {
responseEntity = restTemplate.exchange(new URI(urlStr), HttpMethod.valueOf(request.getMethod()), httpEntity, byte[].class);
} catch (Exception e) {
log.error(e, "env request error. url:{}", urlStr);
String message = e.getMessage();
if (ObjectUtil.isNotNull(e.getCause())) {
message = e.getCause().getMessage();
}
throw new ASWException(message, HttpStatus.INTERNAL_SERVER_ERROR.value());
}
log.info("env request url:{}, responseStatus:{}", urlStr, responseEntity.getStatusCode());
writeResponseWithHeaders(response, responseEntity);
}
public static void writeResponseWithHeaders(HttpServletResponse response, ResponseEntity<byte[]> responseEntity) throws IOException {
HttpHeaders httpHeaders = responseEntity.getHeaders();
int statusCode = responseEntity.getStatusCodeValue();
byte[] responseBody = responseEntity.getBody();
response.reset();
response.setStatus(statusCode);
Set<Map.Entry<String, List<String>>> entrySet = httpHeaders.entrySet();
// 设置 cors 响应头
Constants.CORS_HEADER.forEach((k, v) -> {
response.setHeader(k, v);
});
for (Map.Entry<String, List<String>> en : entrySet) {
String name = en.getKey();
List<String> value = en.getValue();
if (en.getKey().equalsIgnoreCase(Header.CONTENT_LENGTH.getValue())) {
continue;
}
if (en.getKey().equalsIgnoreCase(Header.TRANSFER_ENCODING.getValue())) {
continue;
}
response.setHeader(name, T.StrUtil.join(",", value.toArray()));
}
response.setContentLength(T.ArrayUtil.length(responseBody));
response.getOutputStream().write(responseBody);
response.flushBuffer();
}
@Autowired
public void setRestTemplate(RestTemplate restTemplate) {
EnvironmentUtil.restTemplate = restTemplate;
}
}

View File

@@ -4,9 +4,7 @@ import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.log.Log;
import feign.Feign;
import feign.form.FormEncoder;
import net.geedge.asw.module.feign.client.GeoipClient;
import net.geedge.asw.module.feign.client.KibanaClient;
import net.geedge.asw.module.feign.client.ZeekClient;
import net.geedge.asw.module.feign.client.*;
import net.geedge.asw.module.feign.support.Fastjson2Decoder;
import net.geedge.asw.module.feign.support.Fastjson2Encoder;
import net.geedge.asw.module.feign.support.Http2Client;
@@ -28,6 +26,12 @@ public class FeignClientConfiguration {
@Value("${kibana.url:127.0.0.1:5601}")
private String kibanaUrl;
@Value("${webShark.url:127.0.0.1:8085}")
private String websharkurl;
@Value("${pcapComment.url:127.0.0.1:5000}")
private String pcapCommentUrl;
@Bean("zeekClient")
public ZeekClient zeekClient() {
String url = UrlBuilder.ofHttp(zeekUrl).toString();
@@ -61,4 +65,35 @@ public class FeignClientConfiguration {
.target(KibanaClient.class, url);
}
@Bean("webSharkClient")
public WebSharkClient webSharkClient() {
String url = UrlBuilder.ofHttp(websharkurl).toString();
log.info("[webSharkClient] [url: {}]", url);
return Feign.builder()
.encoder(new FormEncoder())
.client(new Http2Client())
.target(WebSharkClient.class, url);
}
@Bean("pcapCommentClient")
public PcapCommentClient pcapCommentClient() {
String url = UrlBuilder.ofHttp(pcapCommentUrl).toString();
log.info("[pcapCommentClient] [url: {}]", url);
return Feign.builder()
.encoder(new FormEncoder())
.client(new Http2Client())
.target(PcapCommentClient.class, url);
}
@Bean("dashboardClient")
public DashboardClient dashboardClient() {
String url = UrlBuilder.ofHttp(kibanaUrl).toString();
log.info("[kibanaClient] [url: {}]", url);
return Feign.builder()
.encoder(new FormEncoder())
.decoder(new Fastjson2Decoder())
.client(new Http2Client())
.target(DashboardClient.class, url);
}
}

View File

@@ -0,0 +1,22 @@
package net.geedge.asw.module.feign.client;
import com.alibaba.fastjson2.JSONObject;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import java.io.File;
@FeignClient(name = "dashboardClient")
@Headers("Authorization: Bearer {token}")
public interface DashboardClient {
@Headers({
"Content-Type: multipart/form-data",
"osd-xsrf: true",
"kbn-xsrf: true"
})
@RequestLine("POST /api/saved_objects/_import?createNewCopies={createNewCopies}")
JSONObject importDashboard(@Param("token") String token, @Param("file") File file, @Param("createNewCopies") boolean createNewCopies);
}

View File

@@ -10,8 +10,8 @@ import org.springframework.cloud.openfeign.FeignClient;
@Headers("Authorization: Bearer {token}")
public interface KibanaClient {
@RequestLine("GET /api/saved_objects/_find?fields=title&per_page=10000&type=index-pattern&search_fields=title&search={name}")
JSONObject findIndexPattern(@Param("token") String token, @Param("name") String name);
@RequestLine("GET /api/saved_objects/_find?fields=title&per_page=10000&type={type}&search_fields=title&search={name}")
JSONObject findIndexPattern(@Param("token") String token, @Param("type") String type , @Param("name") String name);
@Headers({
"Content-Type: application/json",
@@ -20,4 +20,11 @@ public interface KibanaClient {
@RequestLine("POST /api/saved_objects/index-pattern/{id}")
JSONObject saveIndexPattern(@Param("token") String token, @Param("id") String id, JSONObject body);
@Headers({
"Content-Type: application/json",
"osd-xsrf: true"
})
@RequestLine("DELETE /api/saved_objects/index-pattern/{id}?force={force}")
JSONObject deleteIndexPattern(@Param("token") String token, @Param("id") String id , @Param("force") boolean force);
}

View File

@@ -0,0 +1,18 @@
package net.geedge.asw.module.feign.client;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import feign.Response;
import org.springframework.cloud.openfeign.FeignClient;
import java.io.File;
@FeignClient(name = "pcapCommentClient")
public interface PcapCommentClient {
@RequestLine("POST /api/v1/pcap/comment")
@Headers("Content-Type: multipart/form-data")
Response addCommon(@Param("file") File file, @Param("url") String url, @Param("id") String pcapId);
}

View File

@@ -0,0 +1,18 @@
package net.geedge.asw.module.feign.client;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import feign.Response;
import org.springframework.cloud.openfeign.FeignClient;
import java.io.File;
import java.util.Map;
@FeignClient(name = "webSharkClient")
public interface WebSharkClient {
@RequestLine("POST /webshark/upload")
@Headers("Content-Type: multipart/form-data")
Response upload(@Param("fileKey") File file);
}

View File

@@ -0,0 +1,208 @@
package net.geedge.asw.module.job.controller;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.*;
import net.geedge.asw.module.environment.entity.EnvironmentEntity;
import net.geedge.asw.module.environment.entity.EnvironmentSessionEntity;
import net.geedge.asw.module.environment.service.IEnvironmentService;
import net.geedge.asw.module.environment.service.IEnvironmentSessionService;
import net.geedge.asw.module.job.entity.JobCfgEntity;
import net.geedge.asw.module.job.entity.JobEntity;
import net.geedge.asw.module.job.service.IJobCfgService;
import net.geedge.asw.module.job.util.JobQueueManager;
import net.geedge.asw.module.job.service.IJobService;
import net.geedge.asw.module.job.util.JobConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api/v1/workspace")
public class JobController {
private static final Log log = Log.get();
@Autowired
private IJobService jobService;
@Autowired
private IEnvironmentService environmentService;
@Autowired
private IEnvironmentSessionService sessionService;
@Autowired
private JobQueueManager jobQueueManager;
@Autowired
private IJobCfgService jobCfgService;
@GetMapping("/{workspaceId}/job/{id}")
public R detail(@PathVariable("workspaceId") String workspaceId,
@PathVariable("id") String id) {
JobEntity jobEntity = jobService.queryInfo(id);
return R.ok().putData("record", jobEntity);
}
@GetMapping("/{workspaceId}/job")
public R list(@PathVariable("workspaceId") String workspaceId,
@RequestParam Map<String, Object> params) {
params.put("workspaceId", workspaceId);
Page page = jobService.queryList(params);
return R.ok(page);
}
@PostMapping("/{workspaceId}/job")
public R add(@PathVariable("workspaceId") String workspaceId, String jobCfgId) {
T.VerifyUtil.is(jobCfgId).notNull(RCode.ID_CANNOT_EMPTY);
JobEntity jobEntity = jobService.saveJob(workspaceId, jobCfgId);
return R.ok().putData("record", T.MapUtil.of("id", jobEntity.getId()));
}
@DeleteMapping("/{workspaceId}/job")
public R delete(@PathVariable("workspaceId") String workspaceId,
@RequestParam String ids) {
T.VerifyUtil.is(ids).notEmpty();
List<String> idList = Arrays.asList(ids.split(","));
jobService.removeJob(idList);
return R.ok();
}
@PutMapping("/{workspaceId}/job/cancel")
public R cancel(@PathVariable("workspaceId") String workspaceId,
@RequestParam String ids) {
T.VerifyUtil.is(ids).notEmpty();
List<String> idList = Arrays.asList(ids.split(","));
for (String id : idList) {
cancelJob(id);
}
return R.ok();
}
private void cancelJob(String id) {
JobEntity job = jobService.getById(id);
EnvironmentEntity environment = environmentService.getById(job.getEnvId());
log.info("[cancelJob] [jobId: {}]", id);
JSONObject paramJSONObject = environment.getParamJSONObject();
String url = paramJSONObject.getStr("url");
String token = paramJSONObject.getStr("token");
HttpRequest requestStatus = T.HttpUtil.createGet(String.format("%s/api/v1/env/playbook/%s", url, id));
requestStatus.header("Authorization", token);
if (job.getStatus().contains(JobConstant.JobStatus.RUNNING.getValue())){
while (true){
HttpResponse response = requestStatus.execute();
if (response.isOk()){
break;
}
T.ThreadUtil.sleep(3, TimeUnit.SECONDS);
}
HttpRequest request = T.HttpUtil.createRequest(Method.DELETE, String.format("%s/api/v1/env/playbook/%s", url, id));
request.header("Authorization", token);
HttpResponse response = request.execute();
log.info("[cancelJob] [request env stop playbook] [status: {}]", response.body());
}
if (job.getStatus().contains(JobConstant.JobStatus.PENDING.getValue())){
jobQueueManager.removeJob(job);
}
Thread runningThread = Constants.RUNNING_JOB_THREAD.get(id);
if (runningThread != null) {
runningThread.interrupt();
}
Thread resultThread = Constants.RESULT_JOB_THREAD.get(id);
if (resultThread != null) {
resultThread.interrupt();
}
EnvironmentSessionEntity session = sessionService.getOne(new LambdaQueryWrapper<EnvironmentSessionEntity>()
.eq(EnvironmentSessionEntity::getJobId, id)
.eq(EnvironmentSessionEntity::getStatus, 1));
if (T.ObjectUtil.isNotEmpty(session)) {
environmentService.removeSession(session.getId());
}
// update state
jobService.update(new LambdaUpdateWrapper<JobEntity>()
.eq(JobEntity::getId, id)
.set(JobEntity::getStatus, "cancel")
);
}
@GetMapping("/{workspaceId}/job/{id}/log")
public R getJobLog(@PathVariable("workspaceId") String workspaceId,
@PathVariable("id") String id,
@RequestParam Integer offset) {
offset = offset == null ? 0 : offset;
Map result = jobService.queryJobLog(id, offset);
return R.ok().putData("record", result);
}
@GetMapping("/{workspaceId}/job/cfg/{id}")
public R jobCfgInfo(@PathVariable("workspaceId") String workspaceId,
@PathVariable("id") String id) {
JobCfgEntity entity = jobCfgService.info(id);
return R.ok().putData("record", entity);
}
@GetMapping("/{workspaceId}/job/cfg")
public R jobCfgList(@PathVariable("workspaceId") String workspaceId,
@RequestParam Map<String, Object> params) {
Page page = jobCfgService.queryList(params);
return R.ok(page);
}
@PostMapping("/{workspaceId}/job/cfg")
public R saveCfg(@PathVariable("workspaceId") String workspaceId,
@RequestBody JobCfgEntity cfg) {
VerifyUtil.is(cfg).notNull()
.and(cfg.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
.and(cfg.getPlaybookId()).notEmpty(RCode.PLAYBOOK_ID_CANNOT_EMPTY)
.and(cfg.getPackageId()).notEmpty(RCode.PACKAGE_ID_CANNOT_EMPTY)
.and(cfg.getType()).notEmpty(RCode.JOB_CFG_TYPE_CANNOT_EMPTY)
.and(cfg.getStatus()).notEmpty(RCode.JOB_CFG_STATUS_CANNOT_EMPTY);
JobCfgEntity jobCfgEntity = jobCfgService.saveJobCfg(workspaceId, cfg);
return R.ok().putData("record", T.MapUtil.of("id", jobCfgEntity.getId()));
}
@PutMapping("/{workspaceId}/job/cfg")
public R updateCfg(@PathVariable("workspaceId") String workspaceId,
@RequestBody JobCfgEntity cfg) {
VerifyUtil.is(cfg).notNull()
.and(cfg.getId()).notEmpty(RCode.ID_CANNOT_EMPTY)
.and(cfg.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
.and(cfg.getPlaybookId()).notEmpty(RCode.PLAYBOOK_ID_CANNOT_EMPTY)
.and(cfg.getPackageId()).notEmpty(RCode.PACKAGE_ID_CANNOT_EMPTY)
.and(cfg.getType()).notEmpty(RCode.JOB_CFG_TYPE_CANNOT_EMPTY)
.and(cfg.getStatus()).notEmpty(RCode.JOB_CFG_STATUS_CANNOT_EMPTY);
JobCfgEntity jobCfgEntity = jobCfgService.updateCfg(workspaceId, cfg);
return R.ok().putData("record", T.MapUtil.of("id", jobCfgEntity.getId()));
}
@DeleteMapping("/{workspaceId}/job/cfg")
public R deleteJobCfg(@PathVariable("workspaceId") String workspaceId,
String ids) {
VerifyUtil.is(ids).notEmpty();
List<String> idList = Arrays.asList(ids.split(","));
jobCfgService.removeBatchByIds(idList);
return R.ok();
}
}

View File

@@ -0,0 +1,203 @@
package net.geedge.asw.module.job.controller;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import feign.Response;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.config.SpringContextUtils;
import net.geedge.asw.common.util.*;
import net.geedge.asw.module.feign.client.WebSharkClient;
import net.geedge.asw.module.job.entity.PcapEntity;
import net.geedge.asw.module.job.service.IPcapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/v1/workspace")
public class PcapController {
private static final Log log = Log.get();
@Autowired
private IPcapService pcapService;
@Value("${webShark.url:127.0.0.1:8085}")
private String websharkurl;
@GetMapping("/{workspaceId}/pcap/{id}")
public R detail(@PathVariable("id") String id) {
PcapEntity pcapEntity = pcapService.queryInfo(id);
return R.ok().putData("record", pcapEntity);
}
@GetMapping("/{workspaceId}/pcap")
public R list(@PathVariable("workspaceId") String workspaceId, @RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull();
params.put("workspaceId", workspaceId);
Page page = pcapService.queryList(params);
return R.ok(page);
}
@PostMapping("/{workspaceId}/pcap")
@Transactional(rollbackFor = Exception.class)
public R add(@RequestParam(value = "files", required = true) List<MultipartFile> fileList,
@RequestParam(value = "descriptions", required = false) List<String> descriptionList,
@PathVariable("workspaceId") String workspaceId) throws IOException {
T.VerifyUtil.is(workspaceId).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
List<Object> recordList = T.ListUtil.list(true);
for (int i = 0; i < fileList.size(); i++) {
MultipartFile file = fileList.get(i);
String description = T.StrUtil.emptyToDefault(T.CollUtil.get(descriptionList, i), "");
PcapEntity pcapEntity = pcapService.savePcap(file.getResource(), description, workspaceId);
recordList.add(
T.MapUtil.builder()
.put("id", pcapEntity.getId())
.build()
);
}
return R.ok().putData("records", recordList);
}
@PutMapping("/{workspaceId}/pcap")
@Transactional(rollbackFor = Exception.class)
public R update(@PathVariable("workspaceId") String workspaceId, @RequestBody List<Map<String, String>> body) {
List<Object> recordList = T.ListUtil.list(true);
for (Map<String, String> map : body) {
String id = T.MapUtil.getStr(map, "id", "");
if (T.StrUtil.isEmpty(id)) {
continue;
}
String description = T.MapUtil.getStr(map, "description", "");
pcapService.update(new LambdaUpdateWrapper<PcapEntity>()
.eq(PcapEntity::getId, id)
.set(PcapEntity::getDescription, description)
);
recordList.add(
T.MapUtil.builder()
.put("id", id)
.build()
);
}
return R.ok().putData("records", recordList);
}
@DeleteMapping("/{workspaceId}/pcap")
public R delete(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
pcapService.deletePcap(ids);
return R.ok();
}
@PutMapping("/{workspaceId}/pcap/parse2session")
public R parse2session(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
pcapService.parse2session(ids);
// records
List<PcapEntity> entityList = pcapService.list(new LambdaQueryWrapper<PcapEntity>().in(PcapEntity::getId, ids));
List<Map<String, String>> records = entityList.stream()
.map(entity ->
Map.of(
"id", entity.getId(),
"name", entity.getName(),
"status", entity.getStatus()
)
)
.collect(Collectors.toList());
return R.ok().putData("records", records);
}
@GetMapping("/{workspaceId}/pcap/download")
public void download(HttpServletResponse response, String ids) throws IOException {
T.VerifyUtil.is(ids).notEmpty();
List<String> pcapIdList = Arrays.asList(ids.split(","));
List<PcapEntity> pcapList = pcapService.listByIds(pcapIdList);
if (T.CollectionUtil.isNotEmpty(pcapList) && pcapList.size() == 1) {
PcapEntity first = pcapList.getFirst();
File pcapFile = T.FileUtil.file(first.getPath());
ResponseUtil.downloadFile(response, MediaType.APPLICATION_OCTET_STREAM_VALUE, first.getName(), T.FileUtil.readBytes(pcapFile));
}
if (pcapList.size() > 1) {
File zipFile = T.FileUtil.file(T.StrUtil.concat(true, Constants.TEMP_PATH, "/", "pcap-", DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN) + ".zip"));
List<File> fileList = pcapList.stream().map(x -> T.FileUtil.file(x.getPath())).toList();
T.ZipUtil.zip(zipFile, false, fileList.toArray(new File[0]));
ResponseUtil.downloadFile(response, zipFile.getName(), T.FileUtil.readBytes(zipFile));
T.FileUtil.del(zipFile);
}
}
@GetMapping("/{workspaceId}/pcap/{id}/webshark")
public R webshark(@PathVariable("id") String id) {
T.VerifyUtil.is(id).notEmpty();
HashMap<Object, Object> result = T.MapUtil.newHashMap();
PcapEntity pcap = pcapService.getById(id);
Map summary = T.JSONUtil.toBean(pcap.getSummary(), Map.class);
File pcapFile = FileUtil.file(T.MapUtil.getStr(summary, "commentPath"));
pcapFile = FileUtil.exist(pcapFile) ? pcapFile : T.FileUtil.file(pcap.getPath());
String uploadFileName = T.StrUtil.concat(true, id, ".", T.FileUtil.getSuffix(pcapFile));
File newFile = FileUtil.copy(pcapFile, FileUtil.file(Constants.TEMP_PATH, uploadFileName), false);
try {
WebSharkClient webSharkClient = (WebSharkClient) SpringContextUtils.getBean("webSharkClient");
Response obj = webSharkClient.upload(newFile);
if (T.ObjectUtil.isNotEmpty(obj) && HttpStatus.resolve(obj.status()).is2xxSuccessful()){
String baseUrl = UrlBuilder.ofHttp(websharkurl)
.addPath("/webshark")
.toString();
result.put("fileName", uploadFileName);
result.put("url", baseUrl);
}
}catch (Exception e){
log.error(e, "webshark upload pcap error, id: {}", pcap.getId());
throw new ASWException(RCode.PCAP_UPLOAD_WEB_SHARK_ERROR);
}finally {
FileUtil.del(newFile);
}
return R.ok(result);
}
@PutMapping("/{workspaceId}/pcap/unparse2session")
public R unparse2session(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
pcapService.unparse2session(ids);
return R.ok();
}
@GetMapping("/{workspaceId}/pcap/explore")
public R explore(@PathVariable("workspaceId") String workspaceId, @RequestParam String pcapIds, @RequestParam(required = false) String protocol, @RequestParam(required = false) String streamId) {
String discoverUrl = pcapService.generateKibanaDiscoverUrl(workspaceId, pcapIds, protocol, streamId);
return R.ok().putData("url", discoverUrl);
}
@GetMapping("/{workspaceId}/pcap/dashboard")
public R dashboard(@PathVariable("workspaceId") String workspaceId, @RequestParam String pcapIds) {
String dashboardUrl = pcapService.generateKibanaDashboardUrl(workspaceId, pcapIds);
return R.ok().putData("url", dashboardUrl);
}
}

View File

@@ -0,0 +1,75 @@
package net.geedge.asw.module.job.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.ResponseUtil;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.job.entity.PlaybookEntity;
import net.geedge.asw.module.job.service.IPlaybookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/workspace")
public class PlaybookController {
@Autowired
private IPlaybookService playbookService;
@GetMapping("/{workspaceId}/playbook/{id}")
public R detail(@PathVariable("workspaceId") String workspaceId, @PathVariable("id") String id) {
PlaybookEntity playbook = playbookService.detail(workspaceId, id);
return R.ok().putData("record", playbook);
}
@GetMapping("/{workspaceId}/playbook")
public R list(@PathVariable("workspaceId") String workspaceId, @RequestParam Map params) {
Page page = playbookService.queryList(workspaceId, params);
return R.ok(page);
}
@PostMapping("/{workspaceId}/playbook")
public R save(@PathVariable("workspaceId") String workspaceId,
@RequestParam("file") MultipartFile file,
@RequestParam("name") String name,
@RequestParam("type") String type,
@RequestParam(value = "description", required = false) String description) {
PlaybookEntity playbook = playbookService.savePlaybook(workspaceId, file, name, type, description);
return R.ok().put("record", playbook);
}
@PutMapping("/{workspaceId}/playbook/{playbookId}")
public R update(@PathVariable("workspaceId") String workspaceId,
@PathVariable("playbookId") String playbookId,
@RequestParam("name") String name,
@RequestParam(value = "description", required = false) String description) {
PlaybookEntity playbook = playbookService.updatePlaybook(workspaceId, playbookId, name, description);
return R.ok().putData("record", playbook);
}
@DeleteMapping("/{workspaceId}/playbook")
public R delete(@PathVariable("workspaceId") String workspaceId,
@RequestParam("ids") String ids) {
playbookService.delete(workspaceId, ids);
return R.ok();
}
@GetMapping("/{workspaceId}/playbook/{id}/download")
public void download(@PathVariable("workspaceId") String workspaceId,
@PathVariable("id") String id, HttpServletResponse response) throws IOException {
PlaybookEntity entity = playbookService.getById(id);
T.VerifyUtil.is(entity).notNull(RCode.SYS_RECORD_NOT_FOUND);
File playbookFile = T.FileUtil.file(entity.getPath());
ResponseUtil.downloadFile(response, MediaType.APPLICATION_OCTET_STREAM_VALUE, playbookFile.getName(), T.FileUtil.readBytes(playbookFile));
}
}

Some files were not shown because too many files have changed in this diff Show More