Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5162b99e3e | ||
|
|
263436405c | ||
|
|
2296ca46a4 | ||
|
|
ffcb3baf44 | ||
|
|
501755231a | ||
|
|
f4ddeb3e6c | ||
|
|
40afc03203 | ||
|
|
7c479b1720 | ||
|
|
6c6f69ae24 | ||
|
|
d1cb056adf | ||
|
|
14dc8e0407 | ||
|
|
528cfc90f2 | ||
|
|
327bb72659 | ||
|
|
c6b248ab6f | ||
|
|
0ece84bdf1 | ||
|
|
61fa7a6e32 | ||
|
|
21e7d29077 | ||
|
|
fbf4299a61 | ||
|
|
ad42be8fe8 | ||
|
|
d9c8923b6d | ||
|
|
aa913bce8d | ||
|
|
3e3af89939 | ||
|
|
783baaf8c8 | ||
|
|
a53565e06b | ||
|
|
061495eb13 | ||
|
|
6514363847 | ||
|
|
c3da882950 | ||
|
|
6deebedc75 | ||
|
|
2012f20a09 | ||
|
|
2377c50187 | ||
|
|
91b22297a2 | ||
|
|
5bbf46d141 | ||
|
|
3c7a0947ee | ||
|
|
83c64dddfb | ||
|
|
5d86c330fe | ||
|
|
6a13d3cb22 | ||
|
|
3a8addb4d2 | ||
|
|
90705781f2 | ||
|
|
d8818c3ec2 | ||
|
|
91fecad956 | ||
|
|
9635aa34a7 | ||
|
|
a2f7ab7f9c | ||
|
|
55d0dfd27d | ||
|
|
eb18142926 | ||
|
|
0649c4e3de | ||
|
|
f648e18557 | ||
|
|
9d3b46e9b3 | ||
|
|
14dfe457bf | ||
|
|
9651354317 | ||
|
|
8929aa2f74 | ||
|
|
ea98592012 | ||
|
|
b0be444fba | ||
|
|
b3daacbd8f | ||
|
|
37fc271741 | ||
|
|
bd76eb255d | ||
|
|
0810de23ea | ||
|
|
9f2e4582cc | ||
|
|
8e52631b46 | ||
|
|
7cd885a050 | ||
|
|
3045512320 | ||
|
|
55c2199c3f | ||
|
|
963cd0b227 | ||
|
|
1d5c579e64 | ||
|
|
773d95207f | ||
|
|
2103698417 | ||
|
|
25082b9ea1 | ||
|
|
cb20d56a7b | ||
|
|
d58d46ca8c | ||
|
|
29e6db75ca | ||
|
|
86abf4f634 | ||
|
|
876e763fcc | ||
|
|
32d60a4d43 | ||
|
|
6ffc5d27b5 | ||
|
|
ac412b0dde | ||
|
|
4bdb61870a | ||
|
|
24ad1f64df | ||
|
|
6774376418 | ||
|
|
364120c143 | ||
|
|
cb59390fed | ||
|
|
69bc5b859f | ||
|
|
c420747c6f | ||
|
|
1881086e98 | ||
|
|
eb8d618c2a | ||
|
|
8aa8cda677 | ||
|
|
e67ee60361 | ||
|
|
29fcdd311e | ||
|
|
bb243cf195 | ||
|
|
670e40f6f0 | ||
|
|
feb379f85f | ||
|
|
a8de5ab713 | ||
|
|
4b0c91f106 | ||
|
|
8d46e1d44d | ||
|
|
b3c045aa46 | ||
|
|
ca4ec20e49 | ||
|
|
8db2986dfc | ||
|
|
3156ae1dff | ||
|
|
697d29c6f2 | ||
|
|
bf4b1f9702 | ||
|
|
d2703661c8 | ||
|
|
df78fc5977 | ||
|
|
5e03810295 | ||
|
|
6b44e193f5 | ||
|
|
919e8383bf | ||
|
|
0b7faa038a | ||
|
|
3c75aedcec | ||
|
|
393286d309 | ||
|
|
8501e900c7 | ||
|
|
c53042a4b5 | ||
|
|
c661d34a79 | ||
|
|
a3476596dd | ||
|
|
8a4176bc9e | ||
|
|
a0a7240191 | ||
|
|
2b52697cdc | ||
|
|
63d82b5a19 | ||
|
|
af23e56f03 | ||
|
|
cecd71c42a | ||
|
|
3ed9df8788 | ||
|
|
7a89858790 | ||
|
|
a548413358 | ||
|
|
c859225908 | ||
|
|
b097836ec8 | ||
|
|
9e0cfef4da | ||
|
|
a76a7d4150 | ||
|
|
2920504023 | ||
|
|
c32b590bb4 | ||
|
|
612d7f1166 | ||
|
|
b7c4fbcab8 | ||
|
|
a3f84e799c | ||
|
|
5ab3f168d8 | ||
|
|
402e183d2f | ||
|
|
d3e5e08f6c | ||
|
|
e214fbde5c | ||
|
|
2f7b4bd5ea | ||
|
|
8bba423ff3 | ||
|
|
0d0eadc3bf | ||
|
|
ab430d8934 | ||
|
|
edcc2cf0c8 | ||
|
|
2913ddb2e0 | ||
|
|
7d35f839df | ||
|
|
ef9c34f7d9 | ||
|
|
f1a4e049ca | ||
|
|
8ee1e6acf1 | ||
|
|
d0eddf1e15 | ||
|
|
b42645b0ce |
25
.env.example
25
.env.example
@@ -1,11 +1,20 @@
|
||||
VOLUME_BASE=/data/orion-visor-space/docker-volumes
|
||||
|
||||
DEMO_MODE=false
|
||||
|
||||
SERVICE_PORT=1081
|
||||
SPRING_PROFILES_ACTIVE=prod
|
||||
|
||||
DEMO_MODE=false
|
||||
|
||||
API_CORS=true
|
||||
API_HOST=0.0.0.0
|
||||
# API_URL=http://127.0.0.1:9700/orion-visor/api
|
||||
API_IP_HEADERS=X-Forwarded-For,X-Real-IP
|
||||
API_EXPOSE_TOKEN=pmqeHOyZaumHm0Wt
|
||||
SECRET_KEY=uQeacXV8b3isvKLK
|
||||
|
||||
NGINX_SERVICE_HOST=service
|
||||
NGINX_SERVICE_PORT=9200
|
||||
|
||||
MYSQL_HOST=mysql
|
||||
MYSQL_PORT=3306
|
||||
MYSQL_DATABASE=orion_visor
|
||||
@@ -20,4 +29,16 @@ REDIS_DATA_VERSION=1
|
||||
|
||||
GUACD_HOST=guacd
|
||||
GUACD_PORT=4822
|
||||
GUACD_SSH_PORT=22
|
||||
GUACD_SSH_USERNAME=guacd
|
||||
GUACD_SSH_PASSWORD=guacd
|
||||
GUACD_DRIVE_PATH=/drive
|
||||
|
||||
INFLUXDB_ENABLED=true
|
||||
INFLUXDB_HOST=influxdb
|
||||
INFLUXDB_PORT=8086
|
||||
INFLUXDB_ORG=orion-visor
|
||||
INFLUXDB_BUCKET=metrics
|
||||
INFLUXDB_TOKEN=Data@123456
|
||||
INFLUXDB_ADMIN_USERNAME=admin
|
||||
INFLUXDB_ADMIN_PASSWORD=Data@123456
|
||||
|
||||
10
.gitee/ISSUE_TEMPLATE.zh-CN.md
Normal file
10
.gitee/ISSUE_TEMPLATE.zh-CN.md
Normal file
@@ -0,0 +1,10 @@
|
||||
### *当前使用版本 (必填)
|
||||
|
||||
### 问题描述
|
||||
|
||||
### 该问题是如何引起的
|
||||
|
||||
### 重现步骤
|
||||
|
||||
### 报错信息
|
||||
|
||||
8
.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
Normal file
8
.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md
Normal file
@@ -0,0 +1,8 @@
|
||||
### 修改描述
|
||||
|
||||
### 关联的 Issue
|
||||
|
||||
### 测试用例
|
||||
|
||||
### 修复效果的截屏
|
||||
|
||||
52
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
52
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: 错误报告
|
||||
description: File a bug report.
|
||||
title: "[错误报告]: "
|
||||
labels: [ "" ]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: confirm
|
||||
attributes:
|
||||
label: 确认
|
||||
description: 在提交 issue 之前, 请确认你已经阅读并确认以下内容
|
||||
options:
|
||||
- label: 我使用的是最新版本 [最新版](https://github.com/dromara/orion-visor/releases)
|
||||
required: true
|
||||
- label: 我使用官方文档进行部署 [安装文档](https://visor.orionsec.cn/quickstart/docker.html)
|
||||
required: true
|
||||
- label: 我已检查了 [常见问题](https://visor.orionsec.cn/support/faq.html) 并没有找到解决方法
|
||||
required: true
|
||||
- label: 我已搜索 [issue](https://github.com/dromara/orion-visor/issues) 并没有找到相关问题
|
||||
required: true
|
||||
- label: 我已 star 了此项目, 否则可能会被自动关闭
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: 当前程序版本
|
||||
description: 遇到问题时程序所在的版本号
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: install
|
||||
attributes:
|
||||
label: 安装方式
|
||||
options:
|
||||
- Docker
|
||||
- 普通安装
|
||||
- 其他
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: 问题描述
|
||||
description: 请详细描述你碰到的问题
|
||||
placeholder: "问题描述"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: 详细日志
|
||||
description: 问题出现时的程序日志
|
||||
render: bash
|
||||
5
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 官网
|
||||
url: https://visor.orionsec.cn/
|
||||
about: document.
|
||||
35
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
35
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: 功能改进
|
||||
description: 提出新功能建议 (请提交到需求收集帖)
|
||||
title: "[功能建议]: "
|
||||
labels: [ "" ]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
所有功能建议请统一提交到需求收集帖: 🔗 [#83 需求收集](https://github.com/dromara/orion-visor/issues/83)
|
||||
|
||||
在提交前请确认:
|
||||
- ✅ 使用的是[最新版本](https://github.com/dromara/orion-visor/releases)
|
||||
- ✅ 已搜索[已有 issue](https://github.com/dromara/orion-visor/issues) 和 需求收集帖避免重复
|
||||
- ✅ 定制化需求请联系作者
|
||||
|
||||
---
|
||||
|
||||
### 如何提交高质量建议?
|
||||
1. **功能描述**: 你希望增加什么?
|
||||
2. **使用场景**: 你在什么情况下需要它?
|
||||
4. **参考实现**: 开源项目中的类似功能(**禁止引用商业闭源软件**)
|
||||
- type: textarea
|
||||
id: feature
|
||||
attributes:
|
||||
label: 功能改进
|
||||
description: 请详细描述需要改进或者添加的功能。
|
||||
placeholder: "功能改进"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: references
|
||||
attributes:
|
||||
label: 参考资料
|
||||
description: 可以列举一些参考资料, 但是不要引用同类但商业化软件的任何内容。
|
||||
placeholder: "参考资料"
|
||||
@@ -41,6 +41,10 @@ jobs:
|
||||
pnpm install
|
||||
pnpm build
|
||||
|
||||
- name: 📦️ Download instance-agent
|
||||
working-directory: ./docker/service
|
||||
run: wget https://github.com/lijiahangmax/orion-visor-agent/releases/latest/download/instance-agent-release.tar.gz -O instance-agent-release.tar.gz
|
||||
|
||||
- name: 📁 Prepare build context
|
||||
run: |
|
||||
cp -r ./sql ./docker/mysql/sql
|
||||
@@ -62,7 +66,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
service: [ adminer, guacd, mysql, redis, service, ui ]
|
||||
service: [ adminer, guacd, mysql, redis, influxdb, service, ui ]
|
||||
|
||||
env:
|
||||
GITHUB_REGISTRY: ghcr.io
|
||||
@@ -117,11 +121,15 @@ jobs:
|
||||
type=semver,pattern={{major}}
|
||||
|
||||
- name: 🛠️ Build and push Docker image for orion-visor-${{ matrix.service }}
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./docker
|
||||
file: ./docker/${{ matrix.service }}/Dockerfile
|
||||
push: true
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
platforms: linux/amd64,linux/arm64
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
tags: |
|
||||
${{ env.DOCKERHUB_NAMESPACE }}/orion-visor-${{ matrix.service }}:${{ steps.meta.outputs.version }}
|
||||
${{ env.DOCKERHUB_NAMESPACE }}/orion-visor-${{ matrix.service }}:latest
|
||||
@@ -129,5 +137,3 @@ jobs:
|
||||
${{ env.GITHUB_REGISTRY }}/${{ github.repository_owner }}/orion-visor-${{ matrix.service }}:latest
|
||||
${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/orion-visor-${{ matrix.service }}:${{ steps.meta.outputs.version }}
|
||||
${{ env.ALIYUN_REGISTRY }}/${{ env.ALIYUN_NAMESPACE }}/orion-visor-${{ matrix.service }}:latest
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
9
.github/workflows/e2e.yaml
vendored
9
.github/workflows/e2e.yaml
vendored
@@ -1,9 +1,10 @@
|
||||
name: E2E
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
# pull_request:
|
||||
# branches:
|
||||
# - main
|
||||
|
||||
concurrency:
|
||||
group: ${{github.workflow}} - ${{github.ref}}
|
||||
@@ -18,4 +19,4 @@ jobs:
|
||||
run: |
|
||||
sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
|
||||
sudo chmod u+x /usr/local/bin/docker-compose
|
||||
docker compose -f docker-compose-testing.yml up --build testing --exit-code-from testing --remove-orphans
|
||||
docker compose -f docker-compose-testing.yaml up --build testing --exit-code-from testing --remove-orphans
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,7 +16,8 @@ target/
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
**/.idea/*
|
||||
!**/.idea/icon.png
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
BIN
.idea/icon.png
generated
Normal file
BIN
.idea/icon.png
generated
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
2
NOTICE
2
NOTICE
@@ -5,5 +5,5 @@
|
||||
1. 禁止修改或删除 LICENSE 文件。
|
||||
2. 禁止修改或删除源码头部的版权声明。
|
||||
3. 本项目可免费商业使用,商业使用请保留项目源码、出处、描述文件和作者声明等。
|
||||
4. 分发源码时候,请注明软件出处 https://visor.dromara.org/
|
||||
4. 分发源码时候,请注明软件出处 https://visor.orionsec.cn/
|
||||
5. 不可二次开发或参与同类竞品的开发。
|
||||
|
||||
33
README.md
33
README.md
@@ -52,6 +52,7 @@
|
||||
* **文件管理**:支持远程主机 SFTP 大文件的批量上传、下载和在线编辑等操作。
|
||||
* **批量操作**:支持批量执行主机命令、多主机文件分发等功能。
|
||||
* **计划任务**:支持配置 cron 表达式,定时执行主机命令。
|
||||
* **系统监控**:支持对主机 CPU、内存、磁盘、网络等系统指标的监控和告警。
|
||||
* **安全可靠**:动态配置权限,记录用户操作日志,提供简单的审计功能。
|
||||
|
||||
## 演示环境
|
||||
@@ -62,9 +63,6 @@
|
||||
这对我很重要! [github](https://github.com/dromara/orion-visor) [gitee](https://gitee.com/dromara/orion-visor) [gitcode](https://gitcode.com/dromara/orion-visor)
|
||||
* 🌈 如果本项目对你有帮助请帮忙推广一下 让更多的人知道此项目!
|
||||
* 🎭 演示环境部分功能不可用, 完整功能请本地部署!
|
||||
* 📛 演示环境请不要随便删除数据!
|
||||
* 📧 如果演示环境不可用请联系我!
|
||||
* 📨 **作者随缘寻java高级/资深内推 望京/5号/10号线 有坑位的联系我哦** 微信: `ljh1553488`
|
||||
|
||||
## 快速开始
|
||||
|
||||
@@ -79,18 +77,19 @@ docker compose up -d
|
||||
|
||||
## 项目文档
|
||||
|
||||
* [文档地址](https://visor.dromara.org/)
|
||||
* [安装文档](https://visor.dromara.org/quickstart/docker.html)
|
||||
* [更新日志](https://visor.dromara.org/update/change-log.html)
|
||||
* [操作手册](https://visor.dromara.org/operator/asset.html)
|
||||
* [常见问题](https://visor.dromara.org/support/faq.html)
|
||||
* [文档地址](https://visor.orionsec.cn/)
|
||||
* [安装文档](https://visor.orionsec.cn/quickstart/docker.html)
|
||||
* [更新日志](https://visor.orionsec.cn/update/change-log.html)
|
||||
* [操作手册](https://visor.orionsec.cn/operator/asset.html)
|
||||
* [常见问题](https://visor.orionsec.cn/support/faq.html)
|
||||
|
||||
## 技术栈
|
||||
|
||||
* SpringBoot 2.7.17
|
||||
* SpringBoot 2.7+
|
||||
* Mysql 8.0+
|
||||
* Redis 6.0+
|
||||
* Vue3 3.5+
|
||||
* InfluxDB 2.7+
|
||||
* Vue 3.5+
|
||||
* Arco Design 2.56+
|
||||
|
||||
## 主要功能预览
|
||||
@@ -111,6 +110,13 @@ docker compose up -d
|
||||
|
||||

|
||||
|
||||
#### 主机监控
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### 批量执行
|
||||
|
||||

|
||||
@@ -130,7 +136,8 @@ docker compose up -d
|
||||
|
||||
## 关于我
|
||||
|
||||
本人专注于使用 Java 和 Vue 进行全栈开发, 并在系统自动化运维方面拥有丰富开发的经验。如果您在这些领域有需求或遇到痛点, 请随时联系我, 并备注“合作”。
|
||||
本人专注于使用 Java 和 Vue 进行全栈开发, 并在系统自动化运维方面拥有丰富开发的经验, 并提供企业级的解决方案。如果您在这些领域有需求或遇到痛点, 请随时联系我,
|
||||
并备注“合作”。
|
||||
|
||||
## 联系我
|
||||
|
||||
@@ -156,6 +163,10 @@ QQ群: 755242157
|
||||
|
||||
本项目遵循 [Apache-2.0](https://github.com/dromara/orion-visor/blob/main/LICENSE) 开源许可证。
|
||||
|
||||
## 贡献者
|
||||
|
||||
[](https://github.com/dromara/orion-visor, "Contributors")
|
||||
|
||||
## Gitee 最有价值的开源项目 GVP
|
||||
|
||||

|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-service:latest
|
||||
privileged: true
|
||||
ports:
|
||||
- 9200:9200
|
||||
- "9200:9200"
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: prod
|
||||
MYSQL_HOST: mysql
|
||||
@@ -19,6 +19,10 @@ services:
|
||||
DEMO_MODE: false
|
||||
volumes:
|
||||
- /data/orion-visor-space/docker-volumes/service/root-orion:/root/orion
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
|
||||
interval: 15s
|
||||
@@ -37,7 +41,7 @@ services:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:latest
|
||||
privileged: true
|
||||
ports:
|
||||
- 3307:3306
|
||||
- "3307:3306"
|
||||
environment:
|
||||
MYSQL_DATABASE: orion_visor
|
||||
MYSQL_USER: orion
|
||||
@@ -59,7 +63,7 @@ services:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:latest
|
||||
privileged: true
|
||||
ports:
|
||||
- 6380:6379
|
||||
- "6380:6379"
|
||||
environment:
|
||||
REDIS_PASSWORD: Data@123456
|
||||
volumes:
|
||||
@@ -87,4 +91,4 @@ services:
|
||||
|
||||
networks:
|
||||
orion-visor-net:
|
||||
driver: bridge
|
||||
driver: bridge
|
||||
@@ -1,6 +1,6 @@
|
||||
version: '3.3'
|
||||
|
||||
# latest = 2.4.2
|
||||
# latest = 2.5.6
|
||||
|
||||
# 支持以下源
|
||||
# lijiahangmax/*
|
||||
@@ -12,6 +12,9 @@ services:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-ui:latest
|
||||
ports:
|
||||
- ${SERVICE_PORT:-1081}:80
|
||||
environment:
|
||||
NGINX_SERVICE_HOST: ${NGINX_SERVICE_HOST:-service}
|
||||
NGINX_SERVICE_PORT: ${NGINX_SERVICE_PORT:-9200}
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
service:
|
||||
@@ -23,7 +26,7 @@ services:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-service:latest
|
||||
privileged: true
|
||||
ports:
|
||||
- 9200:9200
|
||||
- "9200:9200"
|
||||
environment:
|
||||
SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod}
|
||||
MYSQL_HOST: ${MYSQL_HOST:-mysql}
|
||||
@@ -35,13 +38,27 @@ services:
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-Data@123456}
|
||||
REDIS_DATABASE: ${REDIS_DATABASE:-0}
|
||||
REDIS_DATA_VERSION: ${REDIS_DATA_VERSION:-1}
|
||||
INFLUXDB_ENABLED: ${INFLUXDB_ENABLED:-true}
|
||||
INFLUXDB_HOST: ${INFLUXDB_HOST:-influxdb}
|
||||
INFLUXDB_PORT: ${INFLUXDB_PORT:-8086}
|
||||
INFLUXDB_ORG: ${INFLUXDB_ORG:-orion-visor}
|
||||
INFLUXDB_BUCKET: ${INFLUXDB_BUCKET:-metrics}
|
||||
INFLUXDB_TOKEN: ${INFLUXDB_TOKEN:-Data@123456}
|
||||
GUACD_HOST: ${GUACD_HOST:-guacd}
|
||||
GUACD_PORT: ${GUACD_PORT:-4822}
|
||||
GUACD_DRIVE_PATH: ${GUACD_DRIVE_PATH:-/drive}
|
||||
SECRET_KEY: ${SECRET_KEY:-uQeacXV8b3isvKLK}
|
||||
API_EXPOSE_TOKEN: ${API_EXPOSE_TOKEN:-pmqeHOyZaumHm0Wt}
|
||||
API_IP_HEADERS: ${API_IP_HEADERS:-X-Forwarded-For,X-Real-IP}
|
||||
API_HOST: ${API_HOST:-0.0.0.0}
|
||||
API_CORS: ${API_CORS:-true}
|
||||
DEMO_MODE: ${DEMO_MODE:-false}
|
||||
volumes:
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/service/root-orion:/root/orion
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
|
||||
@@ -54,6 +71,8 @@ services:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
influxdb:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- orion-visor-net
|
||||
|
||||
@@ -61,7 +80,7 @@ services:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-mysql:latest
|
||||
privileged: true
|
||||
ports:
|
||||
- 3307:3306
|
||||
- "3307:3306"
|
||||
environment:
|
||||
MYSQL_DATABASE: ${MYSQL_DATABASE:-orion_visor}
|
||||
MYSQL_USER: ${MYSQL_USER:-orion}
|
||||
@@ -84,7 +103,7 @@ services:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-redis:latest
|
||||
privileged: true
|
||||
ports:
|
||||
- 6380:6379
|
||||
- "6380:6379"
|
||||
environment:
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-Data@123456}
|
||||
volumes:
|
||||
@@ -100,10 +119,35 @@ services:
|
||||
networks:
|
||||
- orion-visor-net
|
||||
|
||||
influxdb:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-influxdb:latest
|
||||
privileged: true
|
||||
ports:
|
||||
- "8086:8086"
|
||||
environment:
|
||||
DOCKER_INFLUXDB_INIT_MODE: setup
|
||||
DOCKER_INFLUXDB_INIT_USERNAME: ${INFLUXDB_ADMIN_USERNAME:-admin}
|
||||
DOCKER_INFLUXDB_INIT_PASSWORD: ${INFLUXDB_ADMIN_PASSWORD:-Data@123456}
|
||||
DOCKER_INFLUXDB_INIT_ADMIN_TOKEN: ${INFLUXDB_TOKEN:-Data@123456}
|
||||
DOCKER_INFLUXDB_INIT_ORG: ${INFLUXDB_ORG:-orion-visor}
|
||||
DOCKER_INFLUXDB_INIT_BUCKET: ${INFLUXDB_BUCKET:-metrics}
|
||||
volumes:
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/influxdb/data:/var/lib/influxdb2
|
||||
- ${VOLUME_BASE:-/data/orion-visor-space/docker-volumes}/influxdb/config:/etc/influxdb2
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/8086" ]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
networks:
|
||||
- orion-visor-net
|
||||
|
||||
guacd:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-guacd:latest
|
||||
ports:
|
||||
- 4822:4822
|
||||
- "4822:4822"
|
||||
environment:
|
||||
GUACD_LOG_LEVEL: info
|
||||
GUACD_LOG_FILE: /var/log/guacd.log
|
||||
@@ -125,7 +169,7 @@ services:
|
||||
adminer:
|
||||
image: registry.cn-hangzhou.aliyuncs.com/orionsec/orion-visor-adminer:latest
|
||||
ports:
|
||||
- 8081:8080
|
||||
- "8081:8080"
|
||||
environment:
|
||||
ADMINER_DEFAULT_SERVER: ${MYSQL_HOST:-mysql}
|
||||
depends_on:
|
||||
@@ -136,4 +180,4 @@ services:
|
||||
|
||||
networks:
|
||||
orion-visor-net:
|
||||
driver: bridge
|
||||
driver: bridge
|
||||
@@ -41,7 +41,7 @@ if [ "$DEMO_MODE" = true ]; then
|
||||
echo "Starting services for demo mode..."
|
||||
|
||||
# 启动指定的服务
|
||||
docker compose up -d --remove-orphans mysql redis ui service guacd adminer
|
||||
docker compose up -d --remove-orphans mysql redis ui service guacd influxdb adminer
|
||||
echo "Started services for demo mode..."
|
||||
else
|
||||
# 启动所有服务
|
||||
|
||||
@@ -1 +1 @@
|
||||
FROM --platform=$BUILDPLATFORM adminer:latest
|
||||
FROM --platform=$TARGETPLATFORM adminer:latest
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
FROM maven:3.9.10-eclipse-temurin-8-alpine AS builder
|
||||
#FROM --platform=$BUILDPLATFORM maven:3.9-amazoncorretto-8 AS builder
|
||||
FROM maven:3.9-amazoncorretto-8 AS builder
|
||||
|
||||
# 设置阿里云镜像加速
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#FROM --platform=$BUILDPLATFORM node:18-alpine AS builder
|
||||
FROM node:18-alpine AS builder
|
||||
|
||||
# 设置阿里云镜像加速
|
||||
|
||||
@@ -7,7 +7,7 @@ set -e
|
||||
source ./project-build.sh "$@"
|
||||
|
||||
# 版本号
|
||||
version=2.4.2
|
||||
version=2.5.6
|
||||
# 是否推送镜像
|
||||
push_image=false
|
||||
# 是否构建 latest
|
||||
@@ -46,6 +46,7 @@ declare -A images=(
|
||||
["./service/Dockerfile"]="orion-visor-service"
|
||||
["./mysql/Dockerfile"]="orion-visor-mysql"
|
||||
["./redis/Dockerfile"]="orion-visor-redis"
|
||||
["./influxdb/Dockerfile"]="orion-visor-influxdb"
|
||||
["./adminer/Dockerfile"]="orion-visor-adminer"
|
||||
["./guacd/Dockerfile"]="orion-visor-guacd"
|
||||
)
|
||||
@@ -68,6 +69,30 @@ function prepare_app_jar() {
|
||||
fi
|
||||
}
|
||||
|
||||
# 准备 instance-agent
|
||||
function prepare_instance_agent() {
|
||||
local target_file="./service/instance-agent-release.tar.gz"
|
||||
if [ ! -f "$target_file" ]; then
|
||||
echo "警告: $target_file 不存在, 正在尝试从 Github Release 下载..."
|
||||
# 尝试从 GitHub Release 下载
|
||||
if curl -L --fail \
|
||||
--connect-timeout 30 --max-time 30 \
|
||||
https://github.com/lijiahangmax/orion-visor-agent/releases/latest/download/instance-agent-release.tar.gz \
|
||||
-o "$target_file"; then
|
||||
echo "已成功下载到 $target_file"
|
||||
fi
|
||||
|
||||
# 如果下载失败, 提示用户手动下载
|
||||
echo "错误: 无法从 Release 获取 instance-agent-release.tar.gz"
|
||||
echo "请手动从以下地址下载, 并放置到 $target_file"
|
||||
echo " 1) https://github.com/lijiahangmax/orion-visor-agent/raw/main/instance-agent-release.tar.gz"
|
||||
echo " 2) https://gitee.com/lijiahangmax/orion-visor-agent/raw/main/instance-agent-release.tar.gz"
|
||||
exit 1
|
||||
else
|
||||
echo "$target_file 已存在, 无需下载."
|
||||
fi
|
||||
}
|
||||
|
||||
# 准备前端 dist 目录
|
||||
function prepare_dist_directory() {
|
||||
local source_dir="../orion-visor-ui/dist"
|
||||
@@ -115,7 +140,7 @@ function modify_dockerfiles() {
|
||||
if [ -f "$file" ]; then
|
||||
echo "备份并修改: $file"
|
||||
cp "$file" "$file$backup_suffix"
|
||||
sed -i 's/--platform=\$BUILDPLATFORM//g' "$file"
|
||||
sed -i 's/--platform=\TARGETPLATFORM//g' "$file"
|
||||
else
|
||||
echo "文件不存在 -> $file"
|
||||
fi
|
||||
@@ -185,6 +210,7 @@ fi
|
||||
# 检查资源
|
||||
echo "正在检查并准备必要的构建资源..."
|
||||
prepare_app_jar
|
||||
prepare_instance_agent
|
||||
prepare_dist_directory
|
||||
prepare_sql_directory
|
||||
echo "所有前置资源已准备完毕"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM guacamole/guacd:1.6.0
|
||||
FROM --platform=$TARGETPLATFORM guacamole/guacd:1.6.0
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
8
docker/influxdb/Dockerfile
Normal file
8
docker/influxdb/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM --platform=$TARGETPLATFORM influxdb:2
|
||||
|
||||
# 系统时区
|
||||
ARG TZ=Asia/Shanghai
|
||||
|
||||
# 设置时区
|
||||
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
|
||||
echo "${TZ}" > /etc/timezone
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM mysql:8.0.28
|
||||
FROM --platform=$TARGETPLATFORM mysql:8.0.39
|
||||
|
||||
# 系统时区
|
||||
ARG TZ=Asia/Shanghai
|
||||
@@ -12,3 +12,7 @@ COPY ./mysql/my.cnf /etc/mysql/conf.d/my.cnf
|
||||
|
||||
# 复制初始化脚本
|
||||
COPY ./mysql/sql/init-*.sql /docker-entrypoint-initdb.d/
|
||||
|
||||
# 心跳检测
|
||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=3s --retries=3 \
|
||||
CMD mysqladmin ping -h localhost -u root --password=${MYSQL_ROOT_PASSWORD} || exit 1
|
||||
|
||||
@@ -4,7 +4,7 @@ set -e
|
||||
# DockerContext: orion-visor
|
||||
|
||||
# 版本号
|
||||
version=2.4.2
|
||||
version=2.5.6
|
||||
# 是否构建 service
|
||||
export build_service=false
|
||||
# 是否构建 ui
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM redis:6.0.16-alpine
|
||||
FROM --platform=$TARGETPLATFORM redis:6.0.16-alpine
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM openjdk:8-jdk-alpine
|
||||
FROM --platform=$TARGETPLATFORM openjdk:8u171-jdk-alpine3.7
|
||||
|
||||
USER root
|
||||
|
||||
@@ -17,8 +17,19 @@ RUN \
|
||||
ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
|
||||
echo "${TZ}" > /etc/timezone
|
||||
|
||||
# 复制启动脚本
|
||||
COPY ./service/entrypoint.sh /app/entrypoint.sh
|
||||
RUN chmod +x /app/entrypoint.sh
|
||||
|
||||
# 复制 jar 包
|
||||
COPY ./service/orion-visor-launch.jar /app/app.jar
|
||||
# 复制探针包
|
||||
ADD ./service/instance-agent-release.tar.gz /app/instance-agent-release
|
||||
|
||||
# 启动检测
|
||||
HEALTHCHECK --interval=15s --timeout=5s --retries=5 --start-period=10s \
|
||||
CMD wget -T5 -qO- http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health | grep ok || exit 1
|
||||
|
||||
# 启动
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
CMD ["java", "-jar", "/app/app.jar"]
|
||||
|
||||
23
docker/service/entrypoint.sh
Normal file
23
docker/service/entrypoint.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
|
||||
AGENT_RELEASE_DIR="/root/orion/orion-visor/instance-agent-release"
|
||||
DEFAULT_AGENT_DIR="/app/instance-agent-release"
|
||||
|
||||
# 确保父目录存在
|
||||
mkdir -p "$(dirname "$AGENT_RELEASE_DIR")"
|
||||
|
||||
# 加载探针
|
||||
if [ -d "$AGENT_RELEASE_DIR" ] && [ -n "$(ls -A "$AGENT_RELEASE_DIR" 2>/dev/null)" ]; then
|
||||
echo "Using mounted agent release: $AGENT_RELEASE_DIR"
|
||||
else
|
||||
echo "Using default agent release: $DEFAULT_AGENT_DIR"
|
||||
# 复制探针
|
||||
cp -rf "$DEFAULT_AGENT_DIR" "$AGENT_RELEASE_DIR"
|
||||
fi
|
||||
|
||||
# 打印探针版本信息
|
||||
if [ -f "$AGENT_RELEASE_DIR/.version" ]; then
|
||||
echo "Agent version: $(cat "$AGENT_RELEASE_DIR/.version")"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM nginx:alpine
|
||||
FROM --platform=$TARGETPLATFORM nginx:alpine
|
||||
|
||||
# 系统时区
|
||||
ARG TZ=Asia/Shanghai
|
||||
@@ -11,13 +11,20 @@ RUN \
|
||||
ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
|
||||
echo "${TZ}" > /etc/timezone && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
rm -rf /etc/nginx/nginx.conf && \
|
||||
rm -rf /etc/nginx/conf.d/*
|
||||
|
||||
# 复制包
|
||||
# 复制前端静态文件
|
||||
COPY ./ui/dist /usr/share/nginx/html
|
||||
|
||||
# 复制配置
|
||||
COPY ./ui/nginx.conf /etc/nginx/conf.d
|
||||
COPY ./ui/nginx.conf /etc/nginx
|
||||
COPY ./ui/service.conf /etc/nginx/conf.d
|
||||
|
||||
# 复制启动脚本
|
||||
COPY ./ui/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
# 启动
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
11
docker/ui/entrypoint.sh
Normal file
11
docker/ui/entrypoint.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
# 设置环境变量
|
||||
NGINX_SERVICE_HOST="${NGINX_SERVICE_HOST:-service}"
|
||||
NGINX_SERVICE_PORT="${NGINX_SERVICE_PORT:-9200}"
|
||||
|
||||
# 替换环境变量
|
||||
sed -i "s|\${NGINX_SERVICE_HOST}|${NGINX_SERVICE_HOST}|g" /etc/nginx/conf.d/service.conf
|
||||
sed -i "s|\${NGINX_SERVICE_PORT}|${NGINX_SERVICE_PORT}|g" /etc/nginx/conf.d/service.conf
|
||||
|
||||
exec "$@"
|
||||
@@ -1,56 +1,30 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
client_max_body_size 1024m;
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
# 是否启动 gzip 压缩
|
||||
gzip on;
|
||||
# 需要压缩的常见静态资源
|
||||
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
|
||||
# 如果文件大于 1k 就启动压缩
|
||||
gzip_min_length 1k;
|
||||
# 缓冲区
|
||||
gzip_buffers 4 16k;
|
||||
# 压缩的等级
|
||||
gzip_comp_level 2;
|
||||
# access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# web history 模式 404
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /orion-visor/api {
|
||||
proxy_pass http://service:9200/orion-visor/api;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /orion-visor/keep-alive {
|
||||
proxy_pass http://service:9200/orion-visor/keep-alive;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
server_tokens off;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
#gzip on;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
|
||||
56
docker/ui/service.conf
Normal file
56
docker/ui/service.conf
Normal file
@@ -0,0 +1,56 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
client_max_body_size 1024m;
|
||||
|
||||
# 是否启动 gzip 压缩
|
||||
gzip on;
|
||||
# 需要压缩的常见静态资源
|
||||
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
|
||||
# 如果文件大于 1k 就启动压缩
|
||||
gzip_min_length 1k;
|
||||
# 缓冲区
|
||||
gzip_buffers 4 16k;
|
||||
# 压缩的等级
|
||||
gzip_comp_level 2;
|
||||
# access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
# web history 模式 404
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /orion-visor/api {
|
||||
proxy_pass http://${NGINX_SERVICE_HOST}:${NGINX_SERVICE_PORT}/orion-visor/api;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location /orion-visor/keep-alive {
|
||||
proxy_pass http://${NGINX_SERVICE_HOST}:${NGINX_SERVICE_PORT}/orion-visor/keep-alive;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 3600s;
|
||||
proxy_send_timeout 3600s;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## 文档已迁移至网页端
|
||||
## 文档已迁移至在线文档
|
||||
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
|
||||
BIN
docs/assets/screenshot/monitor-alarm.png
Normal file
BIN
docs/assets/screenshot/monitor-alarm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docs/assets/screenshot/monitor-chart.png
Normal file
BIN
docs/assets/screenshot/monitor-chart.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 232 KiB |
BIN
docs/assets/screenshot/monitor-list.png
Normal file
BIN
docs/assets/screenshot/monitor-list.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 153 KiB |
BIN
docs/assets/screenshot/monitor-override.png
Normal file
BIN
docs/assets/screenshot/monitor-override.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 201 KiB |
@@ -1,5 +1,9 @@
|
||||
#!/bin/bash
|
||||
# 清空
|
||||
git clean -df
|
||||
# 切换到 HEAD
|
||||
git reset --hard HEAD
|
||||
# 拉取远程
|
||||
git pull
|
||||
# 查看日志
|
||||
git log -n 1
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.interfaces;
|
||||
package org.dromara.visor.common.cipher;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.codec.Base62s;
|
||||
import cn.orionsec.kit.lang.utils.crypto.symmetric.SymmetricCrypto;
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.interfaces;
|
||||
package org.dromara.visor.common.cipher;
|
||||
|
||||
/**
|
||||
* rsa 解密器
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.configuration;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.utils.IpUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* 公共配置类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/20 10:34
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class CommonConfiguration {
|
||||
|
||||
@Value("${orion.api.ip-headers}")
|
||||
private String[] ipHeaders;
|
||||
|
||||
/**
|
||||
* 设置 IP 请求头
|
||||
*/
|
||||
@PostConstruct
|
||||
public void setIpHeader() {
|
||||
IpUtils.setIpHeader(ipHeaders);
|
||||
log.info("IpUtils.setIpHeader {}", String.join(",", ipHeaders));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public interface AppConst extends OrionConst {
|
||||
/**
|
||||
* 同 ${orion.version} 迭代时候需要手动更改
|
||||
*/
|
||||
String VERSION = "2.4.2";
|
||||
String VERSION = "2.5.6";
|
||||
|
||||
/**
|
||||
* 同 ${spring.application.name}
|
||||
|
||||
@@ -33,6 +33,8 @@ package org.dromara.visor.common.constant;
|
||||
*/
|
||||
public interface AutoConfigureOrderConst {
|
||||
|
||||
int FRAMEWORK_EXECUTOR = Integer.MIN_VALUE + 1000;
|
||||
|
||||
int FRAMEWORK_WEB = Integer.MIN_VALUE + 1100;
|
||||
|
||||
int FRAMEWORK_SECURITY = Integer.MIN_VALUE + 1200;
|
||||
@@ -53,21 +55,23 @@ public interface AutoConfigureOrderConst {
|
||||
|
||||
int FRAMEWORK_REDIS_CACHE = Integer.MIN_VALUE + 2000;
|
||||
|
||||
int FRAMEWORK_CONFIG = Integer.MIN_VALUE + 2100;
|
||||
int FRAMEWORK_INFLUXDB = Integer.MIN_VALUE + 2100;
|
||||
|
||||
int FRAMEWORK_ENCRYPT = Integer.MIN_VALUE + 2200;
|
||||
int FRAMEWORK_CONFIG = Integer.MIN_VALUE + 2300;
|
||||
|
||||
int FRAMEWORK_STORAGE = Integer.MIN_VALUE + 2300;
|
||||
int FRAMEWORK_CYPHER = Integer.MIN_VALUE + 2400;
|
||||
|
||||
int FRAMEWORK_JOB = Integer.MIN_VALUE + 2400;
|
||||
int FRAMEWORK_STORAGE = Integer.MIN_VALUE + 2500;
|
||||
|
||||
int FRAMEWORK_JOB_QUARTZ = Integer.MIN_VALUE + 2500;
|
||||
int FRAMEWORK_JOB = Integer.MIN_VALUE + 2600;
|
||||
|
||||
int FRAMEWORK_JOB_ASYNC = Integer.MIN_VALUE + 2600;
|
||||
int FRAMEWORK_JOB_QUARTZ = Integer.MIN_VALUE + 2610;
|
||||
|
||||
int FRAMEWORK_MONITOR = Integer.MIN_VALUE + 2700;
|
||||
int FRAMEWORK_BIZ_PUSH = Integer.MIN_VALUE + 2700;
|
||||
|
||||
int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 2800;
|
||||
int FRAMEWORK_BIZ_OPERATOR_LOG = Integer.MIN_VALUE + 7000;
|
||||
|
||||
int FRAMEWORK_MONITOR = Integer.MIN_VALUE + 9000;
|
||||
|
||||
int FRAMEWORK_BANNER = Integer.MIN_VALUE + 10000;
|
||||
|
||||
|
||||
@@ -31,24 +31,29 @@ package org.dromara.visor.common.constant;
|
||||
*/
|
||||
public interface BeanOrderConst {
|
||||
|
||||
/**
|
||||
* 公共返回值包装处理器
|
||||
*/
|
||||
int RESPONSE_ADVICE_WRAPPER = Integer.MIN_VALUE + 1000;
|
||||
|
||||
/**
|
||||
* 演示模式切面
|
||||
*/
|
||||
int DEMO_DISABLE_API_ASPECT = Integer.MIN_VALUE + 10;
|
||||
int DEMO_DISABLE_API_ASPECT = Integer.MIN_VALUE + 100;
|
||||
|
||||
/**
|
||||
* 全局日志打印
|
||||
*/
|
||||
int LOG_PRINT_ASPECT = Integer.MIN_VALUE + 20;
|
||||
int LOG_PRINT_ASPECT = Integer.MIN_VALUE + 200;
|
||||
|
||||
/**
|
||||
* 暴露接口切面
|
||||
*/
|
||||
int EXPOSE_API_ASPECT = Integer.MIN_VALUE + 300;
|
||||
|
||||
/**
|
||||
* 操作日志切面
|
||||
*/
|
||||
int OPERATOR_LOG_ASPECT = Integer.MIN_VALUE + 30;
|
||||
int OPERATOR_LOG_ASPECT = Integer.MIN_VALUE + 400;
|
||||
|
||||
/**
|
||||
* 公共返回值包装处理器
|
||||
*/
|
||||
int RESPONSE_ADVICE_WRAPPER = Integer.MIN_VALUE + 1000;
|
||||
|
||||
}
|
||||
|
||||
@@ -34,116 +34,116 @@ public interface ConfigKeys {
|
||||
/**
|
||||
* SFTP 文件预览大小
|
||||
*/
|
||||
String SFTP_PREVIEW_SIZE = "sftp_previewSize";
|
||||
String SFTP_PREVIEW_SIZE = "sftp.preview-size";
|
||||
|
||||
/**
|
||||
* SFTP 重复文件备份
|
||||
*/
|
||||
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp_uploadPresentBackup";
|
||||
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp.upload-present-backup";
|
||||
|
||||
/**
|
||||
* SFTP 备份文件名称
|
||||
*/
|
||||
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp_uploadBackupFileName";
|
||||
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp.upload-backup-file-name";
|
||||
|
||||
/**
|
||||
* 加密公钥
|
||||
*/
|
||||
String ENCRYPT_PUBLIC_KEY = "encrypt_publicKey";
|
||||
String ENCRYPT_PUBLIC_KEY = "encrypt.public-key";
|
||||
|
||||
/**
|
||||
* 加密私钥
|
||||
*/
|
||||
String ENCRYPT_PRIVATE_KEY = "encrypt_privateKey";
|
||||
String ENCRYPT_PRIVATE_KEY = "encrypt.private-key";
|
||||
|
||||
/**
|
||||
* 日志前端显示行数
|
||||
*/
|
||||
String LOG_WEB_SCROLL_LINES = "log_webScrollLines";
|
||||
String LOG_WEB_SCROLL_LINES = "log.web-scroll-lines";
|
||||
|
||||
/**
|
||||
* 日志加载偏移行
|
||||
*/
|
||||
String LOG_TRACKER_LOAD_LINES = "log_trackerLoadLines";
|
||||
String LOG_TRACKER_LOAD_LINES = "log.tracker-load-lines";
|
||||
|
||||
/**
|
||||
* 日志加载间隔毫秒
|
||||
*/
|
||||
String LOG_TRACKER_LOAD_INTERVAL = "log_trackerLoadInterval";
|
||||
String LOG_TRACKER_LOAD_INTERVAL = "log.tracker-load-interval";
|
||||
|
||||
/**
|
||||
* 是否生成详细的执行日志
|
||||
*/
|
||||
String LOG_EXEC_DETAIL_LOG = "log_execDetailLog";
|
||||
String LOG_EXEC_DETAIL_ENABLED = "log.exec-detail.enabled";
|
||||
|
||||
/**
|
||||
* 凭证有效期分
|
||||
* 凭证有效期 分
|
||||
*/
|
||||
String LOGIN_LOGIN_SESSION_TIME = "login_loginSessionTime";
|
||||
String LOGIN_LOGIN_SESSION_TIME = "login.login-session-time";
|
||||
|
||||
/**
|
||||
* 允许多端登录
|
||||
*/
|
||||
String LOGIN_ALLOW_MULTI_DEVICE = "login_allowMultiDevice";
|
||||
String LOGIN_ALLOW_MULTI_DEVICE = "login.allow-multi-device";
|
||||
|
||||
/**
|
||||
* 允许凭证续签
|
||||
*/
|
||||
String LOGIN_ALLOW_REFRESH = "login_allowRefresh";
|
||||
String LOGIN_ALLOW_REFRESH = "login.allow-refresh";
|
||||
|
||||
/**
|
||||
* 凭证续签最大次数
|
||||
*/
|
||||
String LOGIN_MAX_REFRESH_COUNT = "login_maxRefreshCount";
|
||||
String LOGIN_MAX_REFRESH_COUNT = "login.max-refresh-count";
|
||||
|
||||
/**
|
||||
* 凭证续签间隔分
|
||||
*/
|
||||
String LOGIN_REFRESH_INTERVAL = "login_refreshInterval";
|
||||
String LOGIN_REFRESH_INTERVAL = "login.refresh-interval";
|
||||
|
||||
/**
|
||||
* 登录失败锁定
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_LOCK = "login_loginFailedLock";
|
||||
String LOGIN_LOGIN_FAILED_LOCK = "login.login-failed-lock";
|
||||
|
||||
/**
|
||||
* 登录失败锁定阈值分
|
||||
* 登录失败锁定阈值
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_LOCK_THRESHOLD = "login_loginFailedLockThreshold";
|
||||
String LOGIN_LOGIN_FAILED_LOCK_THRESHOLD = "login.login-failed-lock-threshold";
|
||||
|
||||
/**
|
||||
* 登录失败锁定时间分
|
||||
* 登录失败锁定时间 分
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_LOCK_TIME = "login_loginFailedLockTime";
|
||||
String LOGIN_LOGIN_FAILED_LOCK_TIME = "login.login-failed-lock-time";
|
||||
|
||||
/**
|
||||
* 登录失败发信
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_SEND = "login_loginFailedSend";
|
||||
String LOGIN_LOGIN_FAILED_SEND = "login.login-failed-send";
|
||||
|
||||
/**
|
||||
* 登录失败发信阈值
|
||||
*/
|
||||
String LOGIN_LOGIN_FAILED_SEND_THRESHOLD = "login_loginFailedSendThreshold";
|
||||
String LOGIN_LOGIN_FAILED_SEND_THRESHOLD = "login.login-failed-send-threshold";
|
||||
|
||||
/**
|
||||
* 是否开启自动清理命令记录
|
||||
*/
|
||||
String AUTO_CLEAR_EXEC_LOG_ENABLED = "autoClear_execLogEnabled";
|
||||
String AUTO_CLEAR_EXEC_LOG_ENABLED = "auto-clear.exec-log.enabled";
|
||||
|
||||
/**
|
||||
* 自动清理命令记录保留天数
|
||||
*/
|
||||
String AUTO_CLEAR_EXEC_LOG_KEEP_DAYS = "autoClear_execLogKeepDays";
|
||||
String AUTO_CLEAR_EXEC_LOG_KEEP_DAYS = "auto-clear.exec-log.keep-days";
|
||||
|
||||
/**
|
||||
* 是否开启自动清理终端连接记录
|
||||
*/
|
||||
String AUTO_CLEAR_TERMINAL_LOG_ENABLED = "autoClear_terminalLogEnabled";
|
||||
String AUTO_CLEAR_TERMINAL_LOG_ENABLED = "auto-clear.terminal-log.enabled";
|
||||
|
||||
/**
|
||||
* 自动清理终端连接记录保留天数
|
||||
*/
|
||||
String AUTO_CLEAR_TERMINAL_LOG_KEEP_DAYS = "autoClear_terminalLogKeepDays";
|
||||
String AUTO_CLEAR_TERMINAL_LOG_KEEP_DAYS = "auto-clear.terminal-log.keep-days";
|
||||
|
||||
}
|
||||
|
||||
@@ -55,4 +55,6 @@ public interface Const extends cn.orionsec.kit.lang.constant.Const, FieldConst,
|
||||
|
||||
int BATCH_COUNT = 500;
|
||||
|
||||
String IP_0000 = "0.0.0.0";
|
||||
|
||||
}
|
||||
|
||||
@@ -33,4 +33,8 @@ public interface CustomHeaderConst {
|
||||
|
||||
String APP_VERSION = "X-App-Version";
|
||||
|
||||
String AGENT_KEY_HEADER = "X-Agent-Key";
|
||||
|
||||
String AGENT_VERSION_HEADER = "X-Agent-Version";
|
||||
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ public enum ErrorCode implements CodeInfo {
|
||||
|
||||
UNAUTHORIZED(401, "当前认证信息已失效, 请重新登录"),
|
||||
|
||||
EXPOSE_UNAUTHORIZED(401, "当前认证信息错误, 请检查后重试"),
|
||||
|
||||
FORBIDDEN(403, "无操作权限"),
|
||||
|
||||
NOT_FOUND(404, "未找到该资源"),
|
||||
|
||||
@@ -100,9 +100,15 @@ public interface ErrorMessage {
|
||||
|
||||
String GROUP_ABSENT = "分组不存在";
|
||||
|
||||
String METRICS_ABSENT = "指标不存在";
|
||||
|
||||
String RULE_ABSENT = "规则不存在";
|
||||
|
||||
String ALARM_POLICY_ABSENT = "告警策略不存在";
|
||||
|
||||
String HOST_TYPE_ERROR = "主机类型错误";
|
||||
|
||||
String HOST_NOT_ENABLED = "主机未启用";
|
||||
String HOST_NOT_ENABLED = "{} 主机未启用";
|
||||
|
||||
String CONFIG_NOT_ENABLED = "配置未启用";
|
||||
|
||||
@@ -132,6 +138,8 @@ public interface ErrorMessage {
|
||||
|
||||
String CURRENT_USER_UNSUPPORTED_OPT = "当前" + USER_UNSUPPORTED_OPT;
|
||||
|
||||
String PUSH_USER_NOT_EMPTY = "推送用户不能为空";
|
||||
|
||||
String PATH_NOT_NORMALIZE = "路径不合法";
|
||||
|
||||
String OPERATE_ERROR = "操作失败";
|
||||
@@ -140,12 +148,16 @@ public interface ErrorMessage {
|
||||
|
||||
String DECRYPT_ERROR = "数据解密失败";
|
||||
|
||||
String GET_REQUEST_URL_ERROR = "获取请求路径失败";
|
||||
|
||||
String UNKNOWN_TYPE = "未知类型";
|
||||
|
||||
String ERROR_TYPE = "错误的类型";
|
||||
|
||||
String FILE_ABSENT = "文件不存在";
|
||||
|
||||
String FILE_EXTENSION_TYPE = "文件类型不正确";
|
||||
|
||||
String FILE_ABSENT_CLEAR = "文件不存在 (可能已被清理)";
|
||||
|
||||
String LOG_ABSENT = "日志不存在";
|
||||
@@ -158,6 +170,8 @@ public interface ErrorMessage {
|
||||
|
||||
String FILE_UPLOAD_ERROR = "文件上传失败";
|
||||
|
||||
String CALC_SIGN_FAILED = "计算签名失败";
|
||||
|
||||
String SCRIPT_UPLOAD_ERROR = "脚本上传失败";
|
||||
|
||||
String EXEC_ERROR = "执行失败";
|
||||
@@ -182,6 +196,8 @@ public interface ErrorMessage {
|
||||
|
||||
String COMPRESS_FILE_ABSENT = "压缩文件不存在";
|
||||
|
||||
String DECOMPRESS_FILE_ABSENT = "压缩文件不存在";
|
||||
|
||||
String UNABLE_DOWNLOAD_FOLDER = "无法下载文件夹";
|
||||
|
||||
String VALID_ERROR = "验证失败";
|
||||
@@ -194,6 +210,10 @@ public interface ErrorMessage {
|
||||
|
||||
String PLEASE_SELECT_SUFFIX_FILE = "请选择 {} 类型的文件";
|
||||
|
||||
String SPEC_FORMAT_INCORRECT = "规格格式不正确";
|
||||
|
||||
String INFLUXDB_UNSUPPORTED = "InfluxDB 服务未开启";
|
||||
|
||||
/**
|
||||
* 是否为业务异常
|
||||
*
|
||||
@@ -209,6 +229,27 @@ public interface ErrorMessage {
|
||||
|| ex instanceof ApplicationException;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param ex ex
|
||||
* @return message
|
||||
*/
|
||||
static String getErrorMessage(Exception ex) {
|
||||
return getErrorMessage(ex, ErrorMessage.EXEC_ERROR, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param ex ex
|
||||
* @param len len
|
||||
* @return message
|
||||
*/
|
||||
static String getErrorMessage(Exception ex, int len) {
|
||||
return getErrorMessage(ex, ErrorMessage.EXEC_ERROR, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
@@ -217,6 +258,18 @@ public interface ErrorMessage {
|
||||
* @return message
|
||||
*/
|
||||
static String getErrorMessage(Exception ex, String defaultMsg) {
|
||||
return getErrorMessage(ex, defaultMsg, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param ex ex
|
||||
* @param defaultMsg defaultMsg
|
||||
* @param len len
|
||||
* @return message
|
||||
*/
|
||||
static String getErrorMessage(Exception ex, String defaultMsg, int len) {
|
||||
if (ex == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -226,7 +279,11 @@ public interface ErrorMessage {
|
||||
}
|
||||
// 业务异常
|
||||
if (isBizException(ex)) {
|
||||
return message;
|
||||
if (len > 0) {
|
||||
return Strings.retain(message, len);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
return defaultMsg;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ public interface ExtraFieldConst extends FieldConst {
|
||||
|
||||
String TRACE_ID = "traceId";
|
||||
|
||||
String TASK_ID = "taskId";
|
||||
|
||||
String IDENTITY = "identity";
|
||||
|
||||
String GROUP_NAME = "groupName";
|
||||
@@ -69,4 +71,6 @@ public interface ExtraFieldConst extends FieldConst {
|
||||
|
||||
String DARK = "dark";
|
||||
|
||||
String AGENT_KEY = "agentKey";
|
||||
|
||||
}
|
||||
|
||||
@@ -45,12 +45,18 @@ public interface FieldConst {
|
||||
|
||||
String LABEL = "label";
|
||||
|
||||
String FIELD = "field";
|
||||
|
||||
String TYPE = "type";
|
||||
|
||||
String COLOR = "color";
|
||||
|
||||
String LOADING = "loading";
|
||||
|
||||
String STATUS = "status";
|
||||
|
||||
String SWITCH = "switch";
|
||||
|
||||
String INFO = "info";
|
||||
|
||||
String EXTRA = "extra";
|
||||
@@ -71,6 +77,8 @@ public interface FieldConst {
|
||||
|
||||
String SEQ = "seq";
|
||||
|
||||
String START = "start";
|
||||
|
||||
String PATH = "path";
|
||||
|
||||
String ADDRESS = "address";
|
||||
@@ -119,4 +127,12 @@ public interface FieldConst {
|
||||
|
||||
String CONFIG = "config";
|
||||
|
||||
String VERSION = "version";
|
||||
|
||||
String SYNCED = "synced";
|
||||
|
||||
String SIGN = "sign";
|
||||
|
||||
String SIGN_SHORT = "signShort";
|
||||
|
||||
}
|
||||
|
||||
@@ -37,4 +37,20 @@ public interface FileConst {
|
||||
|
||||
String SCRIPT = "script";
|
||||
|
||||
String INSTANCE_AGENT_PATH = "instance-agent";
|
||||
|
||||
String INSTANCE_AGENT_NAME = "instance_agent";
|
||||
|
||||
String INSTANCE_AGENT_FILE_FORMAT = "instance_agent_{}_{}{}";
|
||||
|
||||
String INSTANCE_AGENT_RELEASE = "instance-agent-release";
|
||||
|
||||
String INSTANCE_AGENT_RELEASE_TEMP = "instance-agent-release-temp";
|
||||
|
||||
String INSTANCE_AGENT_RELEASE_TAR_GZ = "instance-agent-release.tar.gz";
|
||||
|
||||
String VERSION = ".version";
|
||||
|
||||
String CONFIG_YAML = "config.yaml";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.entity;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 推送用户
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 21:46
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "PushUser", description = "推送用户")
|
||||
public class PushUser implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "用户id")
|
||||
private Long id;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "花名")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "手机号")
|
||||
private String mobile;
|
||||
|
||||
}
|
||||
@@ -33,6 +33,20 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface RequestIdentity extends Serializable {
|
||||
|
||||
/**
|
||||
* 获取请求时间戳
|
||||
*
|
||||
* @return timestamp
|
||||
*/
|
||||
Long getTimestamp();
|
||||
|
||||
/**
|
||||
* 设置请求时间戳
|
||||
*
|
||||
* @param timestamp timestamp
|
||||
*/
|
||||
void setTimestamp(Long timestamp);
|
||||
|
||||
/**
|
||||
* 获取请求地址
|
||||
*
|
||||
|
||||
@@ -23,7 +23,10 @@
|
||||
package org.dromara.visor.common.entity;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 请求留痕模型
|
||||
@@ -33,9 +36,15 @@ import lombok.Data;
|
||||
* @since 2023/12/29 11:57
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "RequestIdentityModel", description = "请求留痕模型")
|
||||
public class RequestIdentityModel implements RequestIdentity {
|
||||
|
||||
@Schema(description = "时间戳")
|
||||
private Long timestamp;
|
||||
|
||||
@Schema(description = "请求地址")
|
||||
private String address;
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.entity.chart;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 时序图系列
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/3 21:08
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "TimeChartSeries", description = "时序图系列")
|
||||
public class TimeChartSeries {
|
||||
|
||||
@Schema(description = "name")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "颜色")
|
||||
private String color;
|
||||
|
||||
@Schema(description = "tags")
|
||||
private Map<String, Object> tags;
|
||||
|
||||
@Schema(description = "数据 [0]timestampMills [1]value")
|
||||
private List<List<Object>> data;
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.interfaces;
|
||||
package org.dromara.visor.common.file;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.lock;
|
||||
|
||||
import cn.orionsec.kit.lang.able.Executable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 空实现的锁
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/8/23 13:59
|
||||
*/
|
||||
public class EmptyLocker implements Locker {
|
||||
|
||||
@Override
|
||||
public boolean tryLockExecute(String key, Executable executable) {
|
||||
executable.exec();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLockExecute(String key, long timeout, Executable executable) {
|
||||
executable.exec();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T tryLockExecute(String key, Supplier<T> callable) {
|
||||
return callable.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T tryLockExecute(String key, long timeout, Supplier<T> callable) {
|
||||
return callable.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockExecute(String key, Executable executable) {
|
||||
executable.exec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockExecute(String key, long timeout, Executable executable) {
|
||||
executable.exec();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T lockExecute(String key, Supplier<T> callable) {
|
||||
return callable.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T lockExecute(String key, long timeout, Supplier<T> callable) {
|
||||
return callable.get();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.lock;
|
||||
|
||||
import cn.orionsec.kit.lang.able.Executable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 分布式锁
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/5/16 12:24
|
||||
*/
|
||||
public interface Locker {
|
||||
|
||||
/**
|
||||
* 尝试获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param executable exec
|
||||
* @return 是否获取到锁
|
||||
*/
|
||||
boolean tryLockExecute(String key, Executable executable);
|
||||
|
||||
/**
|
||||
* 尝试获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param executable exec
|
||||
* @return 是否获取到锁
|
||||
*/
|
||||
boolean tryLockExecute(String key, long timeout, Executable executable);
|
||||
|
||||
/**
|
||||
* 尝试获取锁并执行 未获取到锁则抛出异常
|
||||
*
|
||||
* @param key key
|
||||
* @param callable callable
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
*/
|
||||
<T> T tryLockExecute(String key, Supplier<T> callable);
|
||||
|
||||
/**
|
||||
* 尝试获取锁并执行 未获取到锁则抛出异常
|
||||
*
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param callable callable
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
*/
|
||||
<T> T tryLockExecute(String key, long timeout, Supplier<T> callable);
|
||||
|
||||
/**
|
||||
* 阻塞获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param executable exec
|
||||
*/
|
||||
void lockExecute(String key, Executable executable);
|
||||
|
||||
/**
|
||||
* 阻塞获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param executable exec
|
||||
*/
|
||||
void lockExecute(String key, long timeout, Executable executable);
|
||||
|
||||
/**
|
||||
* 阻塞获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param callable callable
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
*/
|
||||
<T> T lockExecute(String key, Supplier<T> callable);
|
||||
|
||||
/**
|
||||
* 阻塞获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param callable callable
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
*/
|
||||
<T> T lockExecute(String key, long timeout, Supplier<T> callable);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.mapstruct;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* date 转换器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/3/7 17:43
|
||||
*/
|
||||
public class DateConversion {
|
||||
|
||||
private DateConversion() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Long > Date
|
||||
*
|
||||
* @param timestamp timestamp
|
||||
* @return Date
|
||||
*/
|
||||
public static Date longToDate(Long timestamp) {
|
||||
return timestamp != null ? new Date(timestamp) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Date > Long
|
||||
*
|
||||
* @param date date
|
||||
* @return Long
|
||||
*/
|
||||
public static Long dateToLong(Date date) {
|
||||
return date != null ? date.getTime() : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -63,6 +63,9 @@ public class BaseConnectConfig implements IBaseConnectConfig {
|
||||
@Schema(description = "主机端口")
|
||||
private Integer hostPort;
|
||||
|
||||
@Schema(description = "agentKey")
|
||||
private String agentKey;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ public interface IBaseConnectConfig {
|
||||
|
||||
void setHostPort(Integer hostPort);
|
||||
|
||||
String getAgentKey();
|
||||
|
||||
void setAgentKey(String agentKey);
|
||||
|
||||
String getUsername();
|
||||
|
||||
void setUsername(String username);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
package org.dromara.visor.common.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import org.dromara.visor.common.interfaces.AesEncryptor;
|
||||
import org.dromara.visor.common.cipher.AesEncryptor;
|
||||
|
||||
/**
|
||||
* aes 数据加密工具类
|
||||
|
||||
@@ -37,13 +37,13 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 验证器
|
||||
* 断言
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/18 11:23
|
||||
*/
|
||||
public class Valid extends cn.orionsec.kit.lang.utils.Valid {
|
||||
public class Assert extends cn.orionsec.kit.lang.utils.Assert {
|
||||
|
||||
private static final Validator validator = SpringHolder.getBean(Validator.class);
|
||||
|
||||
@@ -155,8 +155,8 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
|
||||
* @param path path
|
||||
*/
|
||||
public static String checkNormalize(String path) {
|
||||
Valid.notBlank(path);
|
||||
Valid.isTrue(Files1.isNormalize(path), ErrorMessage.PATH_NOT_NORMALIZE);
|
||||
Assert.notBlank(path);
|
||||
Assert.isTrue(Files1.isNormalize(path), ErrorMessage.PATH_NOT_NORMALIZE);
|
||||
return Files1.getPath(path);
|
||||
}
|
||||
|
||||
@@ -168,8 +168,8 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
|
||||
* @return file
|
||||
*/
|
||||
public static String checkSuffix(String file, String suffix) {
|
||||
Valid.notBlank(file);
|
||||
Valid.isTrue(file.toLowerCase().endsWith(Const.DOT + suffix), ErrorMessage.PLEASE_SELECT_SUFFIX_FILE, suffix);
|
||||
Assert.notBlank(file);
|
||||
Assert.isTrue(file.toLowerCase().endsWith(Const.DOT + suffix), ErrorMessage.PLEASE_SELECT_SUFFIX_FILE, suffix);
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ package org.dromara.visor.common.utils;
|
||||
|
||||
import cn.orionsec.kit.ext.location.Region;
|
||||
import cn.orionsec.kit.ext.location.region.LocationRegions;
|
||||
import cn.orionsec.kit.web.servlet.web.Servlets;
|
||||
import cn.orionsec.kit.lang.utils.net.IPs;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -40,6 +40,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class IpUtils {
|
||||
|
||||
private static String[] IP_HEADER = new String[]{"X-Forwarded-For", "X-Real-IP"};
|
||||
|
||||
private static final Map<String, String> CACHE = new HashMap<>();
|
||||
|
||||
private IpUtils() {
|
||||
@@ -52,13 +54,17 @@ public class IpUtils {
|
||||
* @return addr
|
||||
*/
|
||||
public static String getRemoteAddr(HttpServletRequest request) {
|
||||
// 获取实际地址 X_REAL_IP 在多代理情况下会有问题
|
||||
// String realIp = request.getHeader(StandardHttpHeader.X_REAL_IP);
|
||||
// if (!Strings.isBlank(realIp)) {
|
||||
// return realIp;
|
||||
// }
|
||||
// 获取请求地址
|
||||
return Servlets.getRemoteAddr(request);
|
||||
if (request == null) {
|
||||
return null;
|
||||
} else {
|
||||
for (String remoteAddrHeader : IP_HEADER) {
|
||||
String addr = checkIpHeader(request.getHeader(remoteAddrHeader));
|
||||
if (addr != null) {
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
return checkIpHeader(request.getRemoteAddr());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,4 +118,23 @@ public class IpUtils {
|
||||
return Const.CN_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 ip 请求头
|
||||
*
|
||||
* @param headerValue headerValue
|
||||
* @return header
|
||||
*/
|
||||
private static String checkIpHeader(String headerValue) {
|
||||
if (headerValue == null) {
|
||||
return null;
|
||||
} else {
|
||||
headerValue = headerValue.split(",")[0];
|
||||
return IPs.checkIp(headerValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setIpHeader(String[] ipHeader) {
|
||||
IP_HEADER = ipHeader;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,9 +22,10 @@
|
||||
*/
|
||||
package org.dromara.visor.common.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.able.Executable;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.interfaces.Locker;
|
||||
import org.dromara.visor.common.lock.Locker;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -44,26 +45,97 @@ public class LockerUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取锁
|
||||
* 尝试获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param run run
|
||||
* @param key key
|
||||
* @param executable exec
|
||||
* @return 是否获取到锁
|
||||
*/
|
||||
public static boolean tryLock(String key, Runnable run) {
|
||||
return delegate.tryLock(key, run);
|
||||
public static boolean tryLockExecute(String key, Executable executable) {
|
||||
return delegate.tryLockExecute(key, executable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取锁
|
||||
* 尝试获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param call call
|
||||
* @param <T> T
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param executable exec
|
||||
* @return 是否获取到锁
|
||||
*/
|
||||
public static boolean tryLockExecute(String key, long timeout, Executable executable) {
|
||||
return delegate.tryLockExecute(key, timeout, executable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取锁并执行 未获取到锁则抛出异常
|
||||
*
|
||||
* @param key key
|
||||
* @param callable callable
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
*/
|
||||
public static <T> T tryLock(String key, Supplier<T> call) {
|
||||
return delegate.tryLock(key, call);
|
||||
public static <T> T tryLockExecute(String key, Supplier<T> callable) {
|
||||
return delegate.tryLockExecute(key, callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试获取锁并执行 未获取到锁则抛出异常
|
||||
*
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param callable callable
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
*/
|
||||
public static <T> T tryLockExecute(String key, long timeout, Supplier<T> callable) {
|
||||
return delegate.tryLockExecute(key, timeout, callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param executable exec
|
||||
*/
|
||||
public static void lockExecute(String key, Executable executable) {
|
||||
delegate.lockExecute(key, executable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param executable exec
|
||||
*/
|
||||
public static void lockExecute(String key, long timeout, Executable executable) {
|
||||
delegate.lockExecute(key, timeout, executable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param callable callable
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
*/
|
||||
public static <T> T lockExecute(String key, Supplier<T> callable) {
|
||||
return delegate.lockExecute(key, callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 阻塞获取锁并执行
|
||||
*
|
||||
* @param key key
|
||||
* @param timeout timeout
|
||||
* @param callable callable
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
*/
|
||||
public static <T> T lockExecute(String key, long timeout, Supplier<T> callable) {
|
||||
return delegate.lockExecute(key, timeout, callable);
|
||||
}
|
||||
|
||||
public static void setDelegate(Locker delegate) {
|
||||
|
||||
@@ -64,10 +64,24 @@ public class Requests {
|
||||
.map(ServletRequestAttributes::getRequest)
|
||||
.ifPresent(request -> {
|
||||
String address = IpUtils.getRemoteAddr(request);
|
||||
identity.setTimestamp(System.currentTimeMillis());
|
||||
identity.setAddress(address);
|
||||
identity.setLocation(IpUtils.getLocation(address));
|
||||
identity.setUserAgent(Servlets.getUserAgent(request));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制留痕信息
|
||||
*
|
||||
* @param source source
|
||||
* @param target target
|
||||
*/
|
||||
public static void copyIdentity(RequestIdentityModel source, RequestIdentityModel target) {
|
||||
target.setTimestamp(source.getTimestamp());
|
||||
target.setAddress(source.getAddress());
|
||||
target.setLocation(source.getLocation());
|
||||
target.setUserAgent(source.getUserAgent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
package org.dromara.visor.common.utils;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import org.dromara.visor.common.interfaces.RsaDecryptor;
|
||||
import org.dromara.visor.common.cipher.RsaDecryptor;
|
||||
|
||||
/**
|
||||
* rsa 参数解密工具类
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
package org.dromara.visor.common.validator.group;
|
||||
|
||||
/**
|
||||
* 分页验证分组
|
||||
* id 验证分组
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.validator.group;
|
||||
|
||||
/**
|
||||
* key 验证分组
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/1 19:13
|
||||
*/
|
||||
public interface Key {
|
||||
}
|
||||
@@ -14,11 +14,11 @@
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.4.2</revision>
|
||||
<revision>2.5.6</revision>
|
||||
<spring.boot.version>2.7.17</spring.boot.version>
|
||||
<spring.boot.admin.version>2.7.15</spring.boot.admin.version>
|
||||
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>
|
||||
<orion.kit.version>2.0.2</orion.kit.version>
|
||||
<orion.kit.version>2.0.5</orion.kit.version>
|
||||
<aspectj.version>1.9.7</aspectj.version>
|
||||
<lombok.version>1.18.26</lombok.version>
|
||||
<springdoc.version>1.6.15</springdoc.version>
|
||||
@@ -31,6 +31,7 @@
|
||||
<druid.version>1.2.16</druid.version>
|
||||
<redisson.version>3.18.0</redisson.version>
|
||||
<transmittable.thread.local.version>2.14.2</transmittable.thread.local.version>
|
||||
<influxdb.client.version>6.6.0</influxdb.client.version>
|
||||
<mockito.inline.version>4.11.0</mockito.inline.version>
|
||||
<jedis.mock.version>1.0.7</jedis.mock.version>
|
||||
<podam.version>7.2.11.RELEASE</podam.version>
|
||||
@@ -54,8 +55,12 @@
|
||||
<version>${orion.kit.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>orion-log</artifactId>
|
||||
<groupId>cn.orionsec.kit</groupId>
|
||||
<artifactId>orion-log</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>cn.orionsec.kit</groupId>
|
||||
<artifactId>orion-generator</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
@@ -101,6 +106,11 @@
|
||||
<artifactId>orion-visor-spring-boot-starter-job</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-executor</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-websocket</artifactId>
|
||||
@@ -118,7 +128,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-encrypt</artifactId>
|
||||
<artifactId>orion-visor-spring-boot-starter-cipher</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -146,6 +156,16 @@
|
||||
<artifactId>orion-visor-spring-boot-starter-test</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-influxdb</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-biz-push</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-spring-boot-starter-biz-operator-log</artifactId>
|
||||
@@ -272,6 +292,13 @@
|
||||
<version>${transmittable.thread.local.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- influxdb -->
|
||||
<dependency>
|
||||
<groupId>com.influxdb</groupId>
|
||||
<artifactId>influxdb-client-java</artifactId>
|
||||
<version>${influxdb.client.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -73,7 +73,6 @@ public class OperatorLogAspect {
|
||||
.maxPoolSize(1)
|
||||
.useLinkedBlockingQueue()
|
||||
.allowCoreThreadTimeout()
|
||||
.useLinkedBlockingQueue()
|
||||
.build();
|
||||
|
||||
private final OperatorLogFrameworkService operatorLogFrameworkService;
|
||||
|
||||
@@ -23,8 +23,11 @@
|
||||
package org.dromara.visor.framework.biz.operator.log.core.factory;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Arrays1;
|
||||
import cn.orionsec.kit.spring.SpringHolder;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.annotation.Module;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.model.OperatorType;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
@@ -35,7 +38,14 @@ import javax.annotation.PostConstruct;
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/13 17:45
|
||||
*/
|
||||
public abstract class InitializingOperatorTypes implements OperatorTypeDefinition {
|
||||
public abstract class InitializingOperatorTypes implements OperatorTypeDefinition, BeanNameAware {
|
||||
|
||||
private String beanName;
|
||||
|
||||
@Override
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
@@ -55,6 +65,8 @@ public abstract class InitializingOperatorTypes implements OperatorTypeDefinitio
|
||||
type.setModule(module);
|
||||
OperatorTypeHolder.set(type);
|
||||
}
|
||||
// 自动销毁
|
||||
((BeanDefinitionRegistry) SpringHolder.getBeanFactory()).removeBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -123,8 +123,8 @@ public class OperatorLogModel implements RequestIdentity {
|
||||
private Date endTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
* 时间戳
|
||||
*/
|
||||
private Date createTime;
|
||||
private Long timestamp;
|
||||
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializeFilter;
|
||||
import org.dromara.visor.common.entity.RequestIdentity;
|
||||
import org.dromara.visor.common.enums.BooleanBit;
|
||||
import org.dromara.visor.common.trace.TraceIdHolder;
|
||||
import org.dromara.visor.common.security.LoginUser;
|
||||
import org.dromara.visor.common.trace.TraceIdHolder;
|
||||
import org.dromara.visor.common.utils.Requests;
|
||||
import org.dromara.visor.framework.biz.operator.log.configuration.config.OperatorLogConfig;
|
||||
import org.dromara.visor.framework.biz.operator.log.core.enums.ReturnType;
|
||||
@@ -91,6 +91,7 @@ public class OperatorLogFiller implements Gettable<OperatorLogModel> {
|
||||
*/
|
||||
public OperatorLogFiller fillUsedTime(long start) {
|
||||
long end = System.currentTimeMillis();
|
||||
model.setTimestamp(start);
|
||||
model.setDuration((int) (end - start));
|
||||
model.setStartTime(new Date(start));
|
||||
model.setEndTime(new Date(end));
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-framework</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>orion-visor-spring-boot-starter-biz-push</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<description>项目业务推送包</description>
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.configuration;
|
||||
|
||||
import cn.orionsec.kit.lang.function.Functions;
|
||||
import org.dromara.visor.common.constant.AutoConfigureOrderConst;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.PushTemplateFrameworkService;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.PushTemplateFrameworkServiceDelegate;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.WebsiteMessageFrameworkService;
|
||||
import org.dromara.visor.framework.biz.push.core.listener.PushMessageEventListener;
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.service.*;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageChannelUtils;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.PushUtils;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 推送自动配置类
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:04
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_BIZ_PUSH)
|
||||
public class OrionPushAutoConfiguration {
|
||||
|
||||
/**
|
||||
* @return 钉钉推送服务
|
||||
*/
|
||||
@Bean
|
||||
public DingPushService dingPushService() {
|
||||
return new DingPushService();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 飞书推送服务
|
||||
*/
|
||||
@Bean
|
||||
public FeiShuPushService feiShuPushService() {
|
||||
return new FeiShuPushService();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 企业微信推送服务
|
||||
*/
|
||||
@Bean
|
||||
public WeComPushService weComPushService() {
|
||||
return new WeComPushService();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param websiteMessageFrameworkService websiteMessageFrameworkService
|
||||
* @return 站内信推送服务
|
||||
*/
|
||||
@Bean
|
||||
public WebsitePushService websitePushService(WebsiteMessageFrameworkService websiteMessageFrameworkService) {
|
||||
return new WebsitePushService(websiteMessageFrameworkService);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param impl impl
|
||||
* @return 推送模板服务
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
@ConditionalOnBean(PushTemplateFrameworkService.class)
|
||||
public PushTemplateFrameworkService pushTemplateFrameworkService(PushTemplateFrameworkService impl) {
|
||||
PushTemplateFrameworkServiceDelegate delegate = new PushTemplateFrameworkServiceDelegate(impl);
|
||||
// 设置到工具类
|
||||
PushUtils.setPushTemplateFrameworkService(delegate);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pushServices 推送服务
|
||||
* @return 消息推送事件监听器
|
||||
*/
|
||||
@Bean
|
||||
public PushMessageEventListener pushMessageEventListener(List<IPushService<? extends PushMessage>> pushServices) {
|
||||
// 服务列表
|
||||
Map<PushChannelEnum, IPushService<? extends PushMessage>> serviceMap = pushServices.stream()
|
||||
.collect(Collectors.toMap(
|
||||
MessageChannelUtils::getPushChannel,
|
||||
Function.identity(),
|
||||
Functions.right()));
|
||||
// 创建监听器
|
||||
return new PushMessageEventListener(serviceMap);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.annotation;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 消息渠道
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 21:58
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface MessageChannel {
|
||||
|
||||
/**
|
||||
* 消息渠道
|
||||
*
|
||||
* @return channel
|
||||
*/
|
||||
PushChannelEnum value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import cn.orionsec.kit.lang.able.IJsonObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 钉钉请求体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DingRequestBody implements Serializable, IJsonObject {
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@JSONField(name = "msgtype")
|
||||
private String msgType;
|
||||
|
||||
/**
|
||||
* markdown 内容
|
||||
*/
|
||||
private DingRequestBody.MarkdownPayload markdown;
|
||||
|
||||
/**
|
||||
* at 配置
|
||||
*/
|
||||
private DingRequestBody.AtPayload at;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class MarkdownPayload implements Serializable {
|
||||
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String text;
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class AtPayload implements Serializable {
|
||||
|
||||
/**
|
||||
* 被 at 的手机号
|
||||
*/
|
||||
private List<String> atMobiles;
|
||||
|
||||
/**
|
||||
* 被 at 的 userId
|
||||
*/
|
||||
private List<String> atUserIds;
|
||||
|
||||
/**
|
||||
* 是否 at 所有人
|
||||
*/
|
||||
private Boolean isAtAll;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 钉钉响应体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class DingResponseBody implements Serializable {
|
||||
|
||||
/**
|
||||
* 错误码值
|
||||
*/
|
||||
@JSONField(name = "errcode")
|
||||
private Integer errCode;
|
||||
|
||||
/**
|
||||
* 错误码描述
|
||||
*/
|
||||
@JSONField(name = "errmsg")
|
||||
private String errMsg;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import cn.orionsec.kit.lang.able.IJsonObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 飞书请求体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FeiShuRequestBody implements Serializable, IJsonObject {
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
private Long timestamp;
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@JSONField(name = "msg_type")
|
||||
private String msgType;
|
||||
|
||||
/**
|
||||
* text 内容
|
||||
*/
|
||||
private TextPayload content;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class TextPayload implements Serializable {
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String text;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,36 +20,36 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.common.interfaces;
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 分布式锁
|
||||
* 飞书响应体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/5/16 12:24
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
public interface Locker {
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FeiShuResponseBody implements Serializable {
|
||||
|
||||
/**
|
||||
* 尝试获取锁
|
||||
*
|
||||
* @param key key
|
||||
* @param run run
|
||||
* @return 是否获取到锁
|
||||
* code
|
||||
*/
|
||||
boolean tryLock(String key, Runnable run);
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 尝试获取锁
|
||||
*
|
||||
* @param key key
|
||||
* @param call call
|
||||
* @param <T> T
|
||||
* @return 执行结果
|
||||
* msg
|
||||
*/
|
||||
<T> T tryLock(String key, Supplier<T> call);
|
||||
private String msg;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import cn.orionsec.kit.lang.able.IJsonObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 企业微信请求体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WeComRequestBody implements Serializable, IJsonObject {
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
@JSONField(name = "msgtype")
|
||||
private String msgType;
|
||||
|
||||
/**
|
||||
* markdown 内容
|
||||
*/
|
||||
private MarkdownPayload markdown;
|
||||
|
||||
/**
|
||||
* 被 at 的 userId
|
||||
*/
|
||||
@JSONField(name = "mentioned_list")
|
||||
private List<String> mentionedList;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class MarkdownPayload implements Serializable {
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*/
|
||||
private String content;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.entity;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 企业微信响应体
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:41
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WeComResponseBody implements Serializable {
|
||||
|
||||
/**
|
||||
* 错误码值
|
||||
*/
|
||||
@JSONField(name = "errcode")
|
||||
private Integer errCode;
|
||||
|
||||
/**
|
||||
* 错误码描述
|
||||
*/
|
||||
@JSONField(name = "errmsg")
|
||||
private String errMsg;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.enums;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import org.dromara.visor.framework.biz.push.core.message.*;
|
||||
|
||||
/**
|
||||
* 通知模板类型枚举
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/15 23:20
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PushChannelEnum {
|
||||
|
||||
/**
|
||||
* 站内信
|
||||
*/
|
||||
WEBSITE(WebsiteMessage.class),
|
||||
|
||||
/**
|
||||
* 钉钉
|
||||
*/
|
||||
DING(DingPushMessage.class),
|
||||
|
||||
/**
|
||||
* 飞书
|
||||
*/
|
||||
FEI_SHU(FeiShuPushMessage.class),
|
||||
|
||||
/**
|
||||
* 企业微信
|
||||
*/
|
||||
WE_COM(WeComPushMessage.class),
|
||||
|
||||
;
|
||||
|
||||
public final Class<?> messageClass;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends PushMessage> T createMessage(String config) {
|
||||
try {
|
||||
return (T) JSON.parseObject(config, messageClass);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据渠道名称获取枚举
|
||||
*
|
||||
* @param channel 渠道名称
|
||||
* @return 枚举
|
||||
*/
|
||||
public static PushChannelEnum of(String channel) {
|
||||
for (PushChannelEnum value : values()) {
|
||||
if (value.name().equalsIgnoreCase(channel)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return WEBSITE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.event;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 消息推送事件
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/15
|
||||
*/
|
||||
public class PushMessageEvent extends ApplicationEvent {
|
||||
|
||||
public PushMessageEvent(PushMessage message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.framework.service;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
/**
|
||||
* 推送消息模板框架服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/21 16:26
|
||||
*/
|
||||
public interface PushTemplateFrameworkService {
|
||||
|
||||
/**
|
||||
* 根据 templateId 获取消息
|
||||
*
|
||||
* @param templateId templateId
|
||||
* @return message
|
||||
*/
|
||||
PushMessage getPushMessageByTemplateId(Long templateId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.framework.service;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
/**
|
||||
* 推送消息模板框架服务委托类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/21 16:26
|
||||
*/
|
||||
public class PushTemplateFrameworkServiceDelegate implements PushTemplateFrameworkService {
|
||||
|
||||
private final PushTemplateFrameworkService delegate;
|
||||
|
||||
public PushTemplateFrameworkServiceDelegate(PushTemplateFrameworkService delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushMessage getPushMessageByTemplateId(Long templateId) {
|
||||
return delegate.getPushMessageByTemplateId(templateId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.framework.service;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.WebsiteMessage;
|
||||
|
||||
/**
|
||||
* 发送站内信框架服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 22:09
|
||||
*/
|
||||
public interface WebsiteMessageFrameworkService {
|
||||
|
||||
/**
|
||||
* 发送站内信
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void push(WebsiteMessage message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.listener;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.event.PushMessageEvent;
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.service.IPushService;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息推送事件监听器
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/15
|
||||
*/
|
||||
@Slf4j
|
||||
public class PushMessageEventListener implements ApplicationListener<PushMessageEvent> {
|
||||
|
||||
private final Map<PushChannelEnum, IPushService<? extends PushMessage>> pushServiceMap;
|
||||
|
||||
public PushMessageEventListener(Map<PushChannelEnum, IPushService<? extends PushMessage>> pushServiceMap) {
|
||||
this.pushServiceMap = pushServiceMap;
|
||||
}
|
||||
|
||||
@Async("pushExecutor")
|
||||
@Override
|
||||
public void onApplicationEvent(PushMessageEvent event) {
|
||||
try {
|
||||
// 获取消息
|
||||
PushMessage message = (PushMessage) event.getSource();
|
||||
// 发送消息
|
||||
this.getPushService(message).push(message);
|
||||
log.info("PushMessageEventListener push success");
|
||||
} catch (Exception e) {
|
||||
log.error("PushMessageEventListener push error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends PushMessage> IPushService<T> getPushService(PushMessage message) {
|
||||
return (IPushService<T>) pushServiceMap.get(message.getChannel());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.common.entity.PushUser;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 推送消息基类
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/15 23:28
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "BasePushMessage", description = "推送消息基类")
|
||||
public abstract class BasePushMessage implements PushMessage, Serializable {
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "消息模板")
|
||||
private String template;
|
||||
|
||||
@Schema(description = "消息参数")
|
||||
private Map<String, Object> params;
|
||||
|
||||
@Valid
|
||||
@Schema(description = "推送用户")
|
||||
private List<PushUser> pushUsers;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 钉钉推送消息
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/15 23:29
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "DingPushMessage", description = "钉钉推送消息")
|
||||
public class DingPushMessage extends BasePushMessage {
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "webhook")
|
||||
private String webhook;
|
||||
|
||||
@Schema(description = "密钥")
|
||||
private String secret;
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "推送标题")
|
||||
private String title;
|
||||
|
||||
@Override
|
||||
public PushChannelEnum getChannel() {
|
||||
return PushChannelEnum.DING;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 飞书推送消息
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/16 00:26
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "FeiShuPushMessage", description = "飞书推送消息")
|
||||
public class FeiShuPushMessage extends BasePushMessage {
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "webhook")
|
||||
private String webhook;
|
||||
|
||||
@Schema(description = "密钥")
|
||||
private String secret;
|
||||
|
||||
@Override
|
||||
public PushChannelEnum getChannel() {
|
||||
return PushChannelEnum.FEI_SHU;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import org.dromara.visor.common.entity.PushUser;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 推送消息接口
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:42
|
||||
*/
|
||||
public interface PushMessage extends Serializable {
|
||||
|
||||
/**
|
||||
* 获取模板
|
||||
*
|
||||
* @return 模板
|
||||
*/
|
||||
String getTemplate();
|
||||
|
||||
/**
|
||||
* 设置模板
|
||||
*
|
||||
* @param template template
|
||||
*/
|
||||
void setTemplate(String template);
|
||||
|
||||
/**
|
||||
* 获取参数
|
||||
*
|
||||
* @return 参数
|
||||
*/
|
||||
Map<String, Object> getParams();
|
||||
|
||||
/**
|
||||
* 设置参数
|
||||
*
|
||||
* @param params params
|
||||
*/
|
||||
void setParams(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 获取推送用户
|
||||
*
|
||||
* @return 推送用户
|
||||
*/
|
||||
List<PushUser> getPushUsers();
|
||||
|
||||
/**
|
||||
* 设置推送用户列表
|
||||
*
|
||||
* @param pushUsers pushUsers
|
||||
*/
|
||||
void setPushUsers(List<PushUser> pushUsers);
|
||||
|
||||
/**
|
||||
* 获取推送渠道
|
||||
*
|
||||
* @return 推送渠道
|
||||
*/
|
||||
PushChannelEnum getChannel();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 企业微信推送消息
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/15 23:30
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "WeComPushMessage", description = "企业微信推送消息")
|
||||
public class WeComPushMessage extends BasePushMessage {
|
||||
|
||||
@NotBlank
|
||||
@Schema(description = "webhook")
|
||||
private String webhook;
|
||||
|
||||
@Override
|
||||
public PushChannelEnum getChannel() {
|
||||
return PushChannelEnum.WE_COM;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.message;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
/**
|
||||
* 站内信推送
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 19:56
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "WebsiteMessage", description = "站内信消息")
|
||||
public class WebsiteMessage extends BasePushMessage {
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 10)
|
||||
@Schema(description = "消息分类")
|
||||
private String messageClassify;
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 32)
|
||||
@Schema(description = "消息类型")
|
||||
private String messageType;
|
||||
|
||||
@Schema(description = "消息关联")
|
||||
private String relKey;
|
||||
|
||||
@NotBlank
|
||||
@Size(max = 128)
|
||||
@Schema(description = "标题")
|
||||
private String title;
|
||||
|
||||
@Override
|
||||
public PushChannelEnum getChannel() {
|
||||
return PushChannelEnum.WEBSITE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.entity.PushUser;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 消息推送抽象服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 18:51
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BasePushService<Message extends PushMessage> implements IPushService<Message> {
|
||||
|
||||
@Override
|
||||
public void push(Message message) {
|
||||
try {
|
||||
// 验证消息
|
||||
this.validateMessage(message);
|
||||
} catch (Exception e) {
|
||||
log.error("BasePushService validateMessage message: {}", JSON.toJSONString(message), e);
|
||||
return;
|
||||
}
|
||||
// 请求路径
|
||||
String url = this.buildRequestUrl(message);
|
||||
// 构建请求体
|
||||
String body = this.buildRequestBody(message);
|
||||
// 发送请求
|
||||
this.sendRequest(message, url, body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证消息
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
protected void validateMessage(Message message) {
|
||||
// 验证消息
|
||||
Assert.valid(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求 url
|
||||
*
|
||||
* @param message message
|
||||
* @return url
|
||||
*/
|
||||
protected abstract String buildRequestUrl(Message message);
|
||||
|
||||
/**
|
||||
* 构建请求体
|
||||
*
|
||||
* @param message message
|
||||
* @return body
|
||||
*/
|
||||
protected abstract String buildRequestBody(Message message);
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
*
|
||||
* @param message message
|
||||
* @param url url
|
||||
* @param body body
|
||||
*/
|
||||
protected abstract void sendRequest(Message message, String url, String body);
|
||||
|
||||
/**
|
||||
* 追加 at 用户
|
||||
*
|
||||
* @param message message
|
||||
* @param content content
|
||||
* @return content
|
||||
*/
|
||||
protected String appendAtUsers(Message message, String content) {
|
||||
if (Lists.isEmpty(message.getPushUsers())) {
|
||||
return content;
|
||||
}
|
||||
return content + Const.LF + this.getAtUsers(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 @ 的用户
|
||||
*
|
||||
* @param message message
|
||||
* @return users
|
||||
*/
|
||||
protected String getAtUsers(Message message) {
|
||||
return Lists.stream(message.getPushUsers())
|
||||
.map(PushUser::getNickname)
|
||||
.filter(Strings::isNotBlank)
|
||||
.map(s -> Const.AT + s)
|
||||
.collect(Collectors.joining(Const.SPACE));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import cn.orionsec.kit.http.ok.OkRequests;
|
||||
import cn.orionsec.kit.http.ok.OkResponse;
|
||||
import cn.orionsec.kit.lang.constant.StandardContentType;
|
||||
import cn.orionsec.kit.lang.utils.Assert;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.lang.utils.codec.Base64s;
|
||||
import cn.orionsec.kit.lang.utils.crypto.Signatures;
|
||||
import cn.orionsec.kit.lang.utils.math.Hex;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.DingRequestBody;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.DingResponseBody;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.message.DingPushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
* 钉钉推送服务类
|
||||
* <p>
|
||||
* <a href="https://open.dingtalk.com/document/dingstart/obtain-the-webhook-address-of-a-custom-robot">doc</a>
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:13
|
||||
*/
|
||||
@Slf4j
|
||||
@MessageChannel(value = PushChannelEnum.DING)
|
||||
public class DingPushService extends BasePushService<DingPushMessage> {
|
||||
|
||||
private static final String MARKDOWN = "markdown";
|
||||
|
||||
private static final Integer SUCCESS_CODE = 0;
|
||||
|
||||
@Override
|
||||
protected String buildRequestUrl(DingPushMessage message) {
|
||||
String secret = message.getSecret();
|
||||
if (Strings.isBlank(secret)) {
|
||||
return message.getWebhook();
|
||||
}
|
||||
// 加签
|
||||
try {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String plainText = timestamp + "\n" + secret;
|
||||
String hexSign = Signatures.hmacSha256(plainText, secret);
|
||||
if (hexSign == null) {
|
||||
log.error("DingPushService-sign error plain: {}", plainText);
|
||||
throw Exceptions.argument(ErrorMessage.GET_REQUEST_URL_ERROR);
|
||||
}
|
||||
byte[] signData = Hex.hexToBytes(hexSign);
|
||||
String sign = Base64s.encodeToString(signData);
|
||||
// 实际请求地址
|
||||
return message.getWebhook() + "×tamp=" + timestamp + "&sign=" + sign;
|
||||
} catch (Exception e) {
|
||||
log.error("DingPushService-buildRequestUrl error", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestBody(DingPushMessage message) {
|
||||
// 格式化内容
|
||||
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
|
||||
String formattedTitle = MessageUtils.format(message.getTitle(), message.getParams());
|
||||
// at 用户
|
||||
formattedContent = this.appendAtUsers(message, formattedContent);
|
||||
|
||||
// 构建请求体
|
||||
return DingRequestBody.builder()
|
||||
.msgType(MARKDOWN)
|
||||
.markdown(DingRequestBody.MarkdownPayload.builder()
|
||||
.title(formattedTitle)
|
||||
.text(formattedContent)
|
||||
.build())
|
||||
.build()
|
||||
.toJsonString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(DingPushMessage message, String url, String body) {
|
||||
// 发送请求
|
||||
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
|
||||
DingResponseBody responseBody = JSON.parseObject(response.getBodyString(), DingResponseBody.class);
|
||||
// 验证发送结果
|
||||
Assert.eq(responseBody.getErrCode(), SUCCESS_CODE, responseBody.getErrMsg());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import cn.orionsec.kit.http.ok.OkRequests;
|
||||
import cn.orionsec.kit.http.ok.OkResponse;
|
||||
import cn.orionsec.kit.lang.constant.Const;
|
||||
import cn.orionsec.kit.lang.constant.StandardContentType;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.lang.utils.codec.Base64s;
|
||||
import cn.orionsec.kit.lang.utils.crypto.Signatures;
|
||||
import cn.orionsec.kit.lang.utils.crypto.enums.SecretKeySpecMode;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.FeiShuRequestBody;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.FeiShuResponseBody;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.message.FeiShuPushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
* 飞书推送服务类
|
||||
* <p>
|
||||
* <a href="https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot?lang=zh-CN">doc</a>
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:13
|
||||
*/
|
||||
@Slf4j
|
||||
@MessageChannel(value = PushChannelEnum.FEI_SHU)
|
||||
public class FeiShuPushService extends BasePushService<FeiShuPushMessage> {
|
||||
|
||||
private static final String TEXT = "text";
|
||||
|
||||
private static final Integer SUCCESS_CODE = 0;
|
||||
|
||||
@Override
|
||||
protected String buildRequestUrl(FeiShuPushMessage message) {
|
||||
return message.getWebhook();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param message message
|
||||
* @param timestamp timestamp
|
||||
* @return sign
|
||||
*/
|
||||
protected String buildSign(FeiShuPushMessage message, long timestamp) {
|
||||
String secret = message.getSecret();
|
||||
if (Strings.isBlank(secret)) {
|
||||
return null;
|
||||
}
|
||||
// 加签
|
||||
try {
|
||||
String stringToSign = timestamp + "\n" + secret;
|
||||
byte[] signData = Signatures.hmacHashSignBytes(new byte[]{}, Strings.bytes(stringToSign), SecretKeySpecMode.HMAC_SHA256);
|
||||
return Base64s.encodeToString(signData);
|
||||
} catch (Exception e) {
|
||||
log.error("FeiShuPushService-buildSign error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestBody(FeiShuPushMessage message) {
|
||||
long timestamp = System.currentTimeMillis() / Const.MS_S_1;
|
||||
// 加签
|
||||
String sign = this.buildSign(message, timestamp);
|
||||
// 格式化内容
|
||||
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
|
||||
// at 的用户
|
||||
formattedContent = this.appendAtUsers(message, formattedContent);
|
||||
// 构建请求体
|
||||
return FeiShuRequestBody.builder()
|
||||
.timestamp(timestamp)
|
||||
.sign(sign)
|
||||
.msgType(TEXT)
|
||||
.content(FeiShuRequestBody.TextPayload.builder()
|
||||
.text(formattedContent)
|
||||
.build())
|
||||
.build()
|
||||
.toJsonString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(FeiShuPushMessage message, String url, String body) {
|
||||
// 发送请求
|
||||
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
|
||||
FeiShuResponseBody responseBody = JSON.parseObject(response.getBodyString(), FeiShuResponseBody.class);
|
||||
// 验证发送结果
|
||||
Assert.eq(responseBody.getCode(), SUCCESS_CODE, responseBody.getMsg());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import org.dromara.visor.framework.biz.push.core.message.PushMessage;
|
||||
|
||||
/**
|
||||
* 消息推送服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 17:17
|
||||
*/
|
||||
public interface IPushService<Message extends PushMessage> {
|
||||
|
||||
/**
|
||||
* 推送消息
|
||||
*
|
||||
* @param message message
|
||||
*/
|
||||
void push(Message message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import cn.orionsec.kit.http.ok.OkRequests;
|
||||
import cn.orionsec.kit.http.ok.OkResponse;
|
||||
import cn.orionsec.kit.lang.constant.StandardContentType;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.common.utils.Assert;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.WeComRequestBody;
|
||||
import org.dromara.visor.framework.biz.push.core.entity.WeComResponseBody;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.message.WeComPushMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
* 企业微信推送服务
|
||||
* <p>
|
||||
* <a href="https://developer.work.weixin.qq.com/document/path/99110">docs</a>
|
||||
*
|
||||
* @author Shihao Lv
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/17 23:13
|
||||
*/
|
||||
@Slf4j
|
||||
@MessageChannel(value = PushChannelEnum.WE_COM)
|
||||
public class WeComPushService extends BasePushService<WeComPushMessage> {
|
||||
|
||||
private static final String MAKRDOWN = "markdown";
|
||||
|
||||
private static final Integer SUCCESS_CODE = 0;
|
||||
|
||||
@Override
|
||||
protected String buildRequestUrl(WeComPushMessage message) {
|
||||
return message.getWebhook();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestBody(WeComPushMessage message) {
|
||||
// 格式化内容
|
||||
String formattedContent = MessageUtils.format(message.getTemplate(), message.getParams());
|
||||
// at 用户
|
||||
formattedContent = this.appendAtUsers(message, formattedContent);
|
||||
// 构建请求体
|
||||
WeComRequestBody.WeComRequestBodyBuilder builder = WeComRequestBody.builder()
|
||||
.msgType(MAKRDOWN)
|
||||
.markdown(WeComRequestBody.MarkdownPayload.builder()
|
||||
.content(formattedContent)
|
||||
.build());
|
||||
|
||||
return builder.build().toJsonString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(WeComPushMessage message, String url, String body) {
|
||||
// 发送请求
|
||||
OkResponse response = OkRequests.post(url, StandardContentType.APPLICATION_JSON_UTF8, body);
|
||||
WeComResponseBody responseBody = JSON.parseObject(response.getBodyString(), WeComResponseBody.class);
|
||||
// 验证发送结果
|
||||
Assert.eq(responseBody.getErrCode(), SUCCESS_CODE, responseBody.getErrMsg());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2023 - present Dromara, All rights reserved.
|
||||
*
|
||||
* https://visor.dromara.org
|
||||
* https://visor.dromara.org.cn
|
||||
* https://visor.orionsec.cn
|
||||
*
|
||||
* Members:
|
||||
* Jiahang Li - ljh1553488six@139.com - author
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.dromara.visor.framework.biz.push.core.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.visor.framework.biz.push.core.annotation.MessageChannel;
|
||||
import org.dromara.visor.framework.biz.push.core.enums.PushChannelEnum;
|
||||
import org.dromara.visor.framework.biz.push.core.framework.service.WebsiteMessageFrameworkService;
|
||||
import org.dromara.visor.framework.biz.push.core.message.WebsiteMessage;
|
||||
import org.dromara.visor.framework.biz.push.core.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
* 站内信推送服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/9/18 22:13
|
||||
*/
|
||||
@Slf4j
|
||||
@MessageChannel(value = PushChannelEnum.WEBSITE)
|
||||
public class WebsitePushService extends BasePushService<WebsiteMessage> {
|
||||
|
||||
private final WebsiteMessageFrameworkService websiteMessageFrameworkService;
|
||||
|
||||
public WebsitePushService(WebsiteMessageFrameworkService websiteMessageFrameworkService) {
|
||||
this.websiteMessageFrameworkService = websiteMessageFrameworkService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestUrl(WebsiteMessage message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildRequestBody(WebsiteMessage message) {
|
||||
// 格式化内容
|
||||
message.setTemplate(MessageUtils.format(message.getTemplate(), message.getParams()));
|
||||
// 格式化标题
|
||||
message.setTitle(MessageUtils.format(message.getTitle(), message.getParams()));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(WebsiteMessage message, String url, String body) {
|
||||
websiteMessageFrameworkService.push(message);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user