Compare commits

...

46 Commits

Author SHA1 Message Date
李佳航
ad3edd4ccd Merge pull request #41 from dromara/dev
Dev
2024-07-11 11:08:52 +08:00
lijiahang
81b7b3505e 🚀 修改启动命令. 2024-07-11 10:56:49 +08:00
lijiahang
1522a6f3ad 🔖 升级版本. 2024-07-11 10:28:44 +08:00
lijiahang
04bae45955 修改执行参数. 2024-07-10 11:46:07 +08:00
lijiahang
873e910eb1 定时任务添加参数. 2024-07-10 10:21:16 +08:00
lijiahang
a7f86bf62a 💄 修改表格样式. 2024-07-09 10:34:39 +08:00
lijiahang
7ba278d210 📝 添加 NOTICE 文件. 2024-07-08 14:45:12 +08:00
lijiahang
e9ac9b9f13 修改字典值长度. 2024-07-08 14:44:33 +08:00
lijiahangmax
d34843f90c 主机终端主题从字典中获取. 2024-07-04 21:53:21 +08:00
lijiahang
374d0bdd9c Merge remote-tracking branch 'origin/dev' into dev 2024-07-04 10:29:58 +08:00
lijiahang
5d3dc83bab 🐛 修复操作日志分页无效. 2024-07-04 10:29:39 +08:00
lijiahangmax
05d0f75cdc Merge remote-tracking branch 'origin/dev' into dev 2024-06-30 01:14:58 +08:00
lijiahangmax
711a4a6bab 🐛 修复终端大小适配失效. 2024-06-30 01:14:44 +08:00
李佳航
08895ba170 Merge pull request #37 from LinuxSuRen/fix/e2e
🐛 e2e 配置失效.
2024-06-27 15:27:11 +08:00
rick
c2c8b108ac fix: the e2e testing failure due to auth error 2024-06-27 07:23:24 +00:00
李佳航
318e9f30b8 Merge pull request #36 from dromara/dev
🚀 修改 e2e 配置.
2024-06-27 14:46:36 +08:00
lijiahang
39a1001510 🚀 修改 e2e 配置. 2024-06-27 14:43:59 +08:00
李佳航
5e7b7ebfa7 Merge pull request #35 from dromara/dev
Dev
2024-06-27 13:46:39 +08:00
lijiahang
8d85cdf173 🚀 修改 e2e 配置. 2024-06-27 13:44:39 +08:00
lijiahang
79d95d1997 📝 修改 sql. 2024-06-27 13:31:10 +08:00
李佳航
1eb07d0b24 Merge pull request #34 from dromara/dev
Dev
2024-06-27 13:06:49 +08:00
lijiahang
601564b573 🔖 升级版本. 2024-06-27 13:03:25 +08:00
lijiahang
41384fab17 🐛 字典值排序无效. 2024-06-26 10:23:32 +08:00
lijiahang
f0a122d862 自动清理配置. 2024-06-25 10:42:32 +08:00
lijiahang
b08d75be62 🔨 更新 spring 配置描述文件. 2024-06-25 10:16:15 +08:00
lijiahangmax
02f5bef6b4 📝 修改命名. 2024-06-24 22:49:17 +08:00
lijiahang
ef10c8b8b6 修改前端包结构. 2024-06-24 09:57:33 +08:00
lijiahang
6c4e9cd5c6 🚀 修改 e2e 配置. 2024-06-24 09:54:57 +08:00
lijiahang
8dec40553d 优化 terminal 逻辑. 2024-06-21 10:15:33 +08:00
lijiahang
9ae5a6c627 🐛 修复机器码获取失败. 2024-06-20 11:55:00 +08:00
lijiahang
2ec1678f01 🔨 修改 ip 获取逻辑. 2024-06-20 11:53:39 +08:00
李佳航
f9e436e885 Merge pull request #30 from LinuxSuRen/e2e
🚀 add e2e testing base on docker compose.
2024-06-20 11:28:46 +08:00
rick
2a49e7670d fix the health check command 2024-06-20 02:56:16 +00:00
Rick
95d8988f11 Update e2e.yaml 2024-06-20 10:25:23 +08:00
Rick
e04a972df5 Update Dockerfile 2024-06-20 10:24:58 +08:00
rick
1ca9311625 fix the password in e2e 2024-06-20 01:52:10 +00:00
rick
630a1fd3cd test: add e2e testing base on docker compose 2024-06-20 01:29:43 +00:00
李佳航
0f6d84dab2 Merge pull request #28 from dromara/dev
Dev
2024-06-19 12:11:39 +08:00
lijiahang
f64eb395a8 🐳 修改镜像权限. 2024-06-19 11:53:06 +08:00
lijiahang
2e69c67de0 🐳 修改镜像权限. 2024-06-19 11:29:09 +08:00
lijiahang
7747b4e52e 🔖 升级版本. 2024-06-19 11:07:03 +08:00
lijiahang
830622aafb 💄 修改样式. 2024-06-19 10:48:00 +08:00
lijiahang
2919950c5b ⬆️ 升级 arco 版本. 2024-06-18 11:53:58 +08:00
lijiahangmax
d56cfbba82 添加系统设置页面. 2024-06-18 00:35:45 +08:00
lijiahang
cfe1924f11 获取应用信息. 2024-06-17 19:12:58 +08:00
lijiahang
dfd2ec45f4 🔨 修改 spring 应用名称. 2024-06-17 10:13:23 +08:00
206 changed files with 3084 additions and 1864 deletions

21
.github/workflows/e2e.yaml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: E2E
on:
pull_request:
branches:
- main
concurrency:
group: ${{github.workflow}} - ${{github.ref}}
cancel-in-progress: true
jobs:
testing:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: E2E Testing
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

7
NOTICE Normal file
View File

@@ -0,0 +1,7 @@
* 在使用本项目前,请您仔细阅读免责声明,确保您已充分理解其中的内容
* 本项目采用 APACHE LICENSE 2.0 开源协议,如您需要源码的开发方式,需要遵循以下几点
1. 禁止修改或删除 LICENSE 文件。
2. 不可二次开发或参与同类竞品的开发。
3. 本项目可免费商业使用,商业使用请保留项目源码、出处、描述文件和作者声明等。

View File

@@ -0,0 +1,79 @@
version: '3.3'
services:
orion-visor-service:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.0.11
privileged: true
ports:
- 1081:80
environment:
- MYSQL_HOST=orion-visor-mysql
- MYSQL_PORT=3306
- MYSQL_DATABASE=orion_visor
- MYSQL_USER=root
- MYSQL_PASSWORD=Data@123456
- REDIS_HOST=orion-visor-redis
- REDIS_PASSWORD=Data@123456
- SECRET_KEY=uQeacXV8b3isvKLK
- DEMO_MODE=false
volumes:
- /data/orion-visor-space/docker-volumes/service/root-orion:/root/orion
healthcheck:
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
interval: 3s
timeout: 300s
retries: 200
start_period: 3s
depends_on:
orion-visor-mysql:
condition: service_healthy
orion-visor-redis:
condition: service_healthy
links:
- orion-visor-mysql
- orion-visor-redis
orion-visor-mysql:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.0.11
privileged: true
ports:
- 3307:3306
environment:
- MYSQL_DATABASE=orion_visor
- MYSQL_USER=orion
- MYSQL_PASSWORD=Data@123456
- MYSQL_ROOT_PASSWORD=Data@123456
volumes:
- /data/orion-visor-space/docker-volumes/mysql/var-lib-mysql:/var/lib/mysql
- /data/orion-visor-space/docker-volumes/mysql/var-lib-mysql-files:/var/lib/mysql-files
- /data/orion-visor-space/docker-volumes/mysql/etc-mysql:/etc/mysql
healthcheck:
test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306" ]
interval: 3s
timeout: 60s
retries: 10
start_period: 3s
orion-visor-redis:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.0.11
privileged: true
ports:
- 6380:6379
environment:
- REDIS_PASSWORD=Data@123456
volumes:
- /data/orion-visor-space/docker-volumes/redis/data:/data
command: sh -c "redis-server /usr/local/redis.conf --requirepass $${REDIS_PASSWORD}"
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 3s
timeout: 60s
retries: 10
start_period: 3s
testing:
build:
context: ./docker/e2e
environment:
SERVER: http://orion-visor-service:80
depends_on:
orion-visor-service:
condition: service_healthy
links:
- orion-visor-service

View File

@@ -1,7 +1,8 @@
version: '3.3'
services:
orion-visor-service:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.0.8
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-service:2.0.11
privileged: true
ports:
- 1081:80
environment:
@@ -15,12 +16,23 @@ services:
- SECRET_KEY=uQeacXV8b3isvKLK
- DEMO_MODE=false
volumes:
- /data/orion-visor-space/docker-volumes/orion-visor-service/root-orion:/root/orion
- /data/orion-visor-space/docker-volumes/service/root-orion:/root/orion
healthcheck:
test: [ "CMD", "curl", "http://127.0.0.1:9200/orion-visor/api/server/bootstrap/health" ]
interval: 3s
timeout: 300s
retries: 200
start_period: 3s
depends_on:
orion-visor-mysql:
condition: service_healthy
orion-visor-redis:
condition: service_healthy
links:
- orion-visor-mysql
- orion-visor-redis
orion-visor-mysql:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.0.8
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-mysql:2.0.11
privileged: true
ports:
- 3307:3306
@@ -30,19 +42,31 @@ services:
- MYSQL_PASSWORD=Data@123456
- MYSQL_ROOT_PASSWORD=Data@123456
volumes:
- /data/orion-visor-space/docker-volumes/orion-visor-mysql/var-lib-mysql:/var/lib/mysql
- /data/orion-visor-space/docker-volumes/orion-visor-mysql/var-lib-mysql-files:/var/lib/mysql-files
- /data/orion-visor-space/docker-volumes/orion-visor-mysql/etc-mysql:/etc/mysql
- /data/orion-visor-space/docker-volumes/mysql/var-lib-mysql:/var/lib/mysql
- /data/orion-visor-space/docker-volumes/mysql/var-lib-mysql-files:/var/lib/mysql-files
- /data/orion-visor-space/docker-volumes/mysql/etc-mysql:/etc/mysql
healthcheck:
test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306" ]
interval: 3s
timeout: 60s
retries: 10
start_period: 3s
orion-visor-redis:
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.0.8
image: registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:2.0.11
privileged: true
ports:
- 6380:6379
environment:
- REDIS_PASSWORD=Data@123456
volumes:
- /data/orion-visor-space/docker-volumes/orion-visor-redis/data:/data
- /data/orion-visor-space/docker-volumes/redis/data:/data
command: sh -c "redis-server /usr/local/redis.conf --requirepass $${REDIS_PASSWORD}"
healthcheck:
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
interval: 3s
timeout: 60s
retries: 10
start_period: 3s
orion-visor-adminer:
image: adminer
ports:

View File

@@ -4,4 +4,4 @@ docker compose down
if [ "$1" == "demo" ]; then
sed -i 's/DEMO_MODE=false/DEMO_MODE=true/g' docker-compose.yml
fi
docker compose up -d
docker compose up -d --remove-orphans

7
docker/e2e/Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
FROM ghcr.io/linuxsuren/api-testing:v0.0.17
WORKDIR /workspace
COPY . .
RUN chmod 777 *
CMD [ "/workspace/entrypoint.sh" ]

3
docker/e2e/entrypoint.sh Normal file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
set -e
atest run -p testsuite.yaml --report md

49
docker/e2e/testsuite.yaml Normal file
View File

@@ -0,0 +1,49 @@
#!api-testing
# yaml-language-server: $schema=https://linuxsuren.github.io/api-testing/api-testing-schema.json
name: orion-visor
api: |
{{default "http://orion-visor-service:80" (env "SERVER")}}
items:
- name: login
request:
api: /orion-visor/api/infra/auth/login
method: POST
header:
Content-type: application/json
body: |
{"username":"admin","password":"21232f297a57a5a743894a0e4a801fc3"}
expect:
bodyFieldsExpect:
code: 200
- name: userPermission
request:
api: /orion-visor/api/infra/permission/user
header:
Authorization: Bearer {{.login.data.token}}
expect:
bodyFieldsExpect:
code: 200
msg: "success"
- name: menu
request:
api: /orion-visor/api/infra/permission/menu
header:
Authorization: Bearer {{.login.data.token}}
expect:
bodyFieldsExpect:
code: 200
msg: "success"
- name: haveUnRead
request:
api: /orion-visor/api/infra/system-message/has-unread
- name: queryOperatorLog
request:
api: /orion-visor/api/infra/mine/query-operator-log
method: POST
- name: hostList
request:
api: /orion-visor/api/infra/tag/list?type=HOST
- name: queryHost
request:
api: /orion-visor/api/asset/host/query
method: POST

View File

@@ -1,5 +1,5 @@
#/bin/bash
version=2.0.8
version=2.0.11
cp -r ../../sql ./sql
docker build -t orion-visor-mysql:${version} .
rm -rf ./sql

View File

@@ -1,5 +1,5 @@
#/bin/bash
version=2.0.8
version=2.0.11
docker build -t orion-visor-redis:${version} .
docker tag orion-visor-redis:${version} registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}
docker push registry.cn-hangzhou.aliyuncs.com/lijiahangmax/orion-visor-redis:${version}

View File

@@ -8,6 +8,7 @@ RUN \
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk update && \
apk add tzdata && \
apk add dmidecode && \
apk add openjdk8
# 设置时区
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \

View File

@@ -1,5 +1,5 @@
#/bin/bash
version=2.0.8
version=2.0.11
mv ../../orion-visor-launch/target/orion-visor-launch.jar ./orion-visor-launch.jar
mv ../../orion-visor-ui/dist ./dist
docker build -t orion-visor-service:${version} .

View File

@@ -14,7 +14,7 @@
<url>https://github.com/dromara/orion-visor</url>
<properties>
<revision>2.0.8</revision>
<revision>2.0.11</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>

View File

@@ -14,9 +14,12 @@ public interface AppConst extends OrionConst {
/**
* 同 ${orion.version} 迭代时候需要手动更改
*/
String VERSION = "2.0.8";
String VERSION = "2.0.11";
String ORION_VISOR = "orion-visor";
/**
* 同 ${spring.application.name}
*/
String APP_NAME = "orion-visor";
String GITHUB = "https://github.com/dromara/orion-visor";

View File

@@ -13,4 +13,8 @@ public interface CnConst {
String CN_ROLE = "角色";
String CN_UNKNOWN = "未知";
String CN_INTRANET_IP = "内网IP";
}

View File

@@ -17,10 +17,6 @@ public interface Const extends com.orion.lang.constant.Const, FieldConst, CnCons
int MD5_LEN = 32;
String UNKNOWN = "未知";
String INTRANET_IP = "内网IP";
Long ROOT_PARENT_ID = 0L;
Integer DEFAULT_SORT = 10;

View File

@@ -83,8 +83,12 @@ public interface ErrorMessage {
String UNKNOWN_TYPE = "未知类型";
String ERROR_TYPE = "错误的类型";
String FILE_ABSENT = "文件不存在";
String FILE_ABSENT_CLEAR = "文件不存在 (可能已被清理)";
String LOG_ABSENT = "日志不存在";
String TASK_ABSENT = "任务不存在";
@@ -95,6 +99,8 @@ public interface ErrorMessage {
String FILE_READ_ERROR = "文件读取失败";
String FILE_READ_ERROR_CLEAR = "文件读取失败 (可能已被清理)";
String PLEASE_CHECK_HOST_SSH = "请检查主机 {} 是否存在/权限/SSH配置";
String CLIENT_ABORT = "手动中断";

View File

@@ -43,4 +43,6 @@ public interface ExtraFieldConst extends FieldConst {
String LOG_ID = "logId";
String DARK = "dark";
}

View File

@@ -31,6 +31,8 @@ public interface FieldConst {
String INFO = "info";
String EXTRA = "extra";
String REL_ID = "relId";
String BEFORE = "before";
@@ -59,10 +61,24 @@ public interface FieldConst {
String TIME = "time";
String ISSUE = "issue";
String EXPIRE = "expire";
String LOCATION = "location";
String USER_AGENT = "userAgent";
String ERROR_MESSAGE = "errorMessage";
String UUID = "uuid";
String REDIRECT = "redirect";
String SCHEMA = "schema";
String FILTER = "filter";
String LICENSE = "license";
}

View File

@@ -1,13 +1,13 @@
package com.orion.visor.framework.common.constant;
/**
* 路径常量
* 文件常量
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/17 10:35
*/
public interface PathConst {
public interface FileConst {
String ERROR_LOG = "error.log";

View File

@@ -0,0 +1,25 @@
package com.orion.visor.framework.common.entity;
import lombok.Data;
/**
* 自动清理配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/6/24 15:03
*/
@Data
public class AutoClearConfig {
/**
* 是否开启
*/
private Boolean enabled;
/**
* 保留周期 (天)
*/
private Integer keepPeriod;
}

View File

@@ -0,0 +1,45 @@
package com.orion.visor.framework.common.enums;
import com.orion.lang.utils.Strings;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 端点定义
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/6/21 19:15
*/
@Getter
@AllArgsConstructor
public enum EndpointDefine {
/**
* 上传临时分区
*/
UPLOAD_SWAP("/upload/swap/{}"),
/**
* 批量执行日志
*/
EXEC_LOG("/exec/{}/{}.log"),
;
/**
* 端点
*/
private final String endpoint;
/**
* 格式化
*
* @param params params
* @return path
*/
public String format(Object... params) {
return Strings.format(this.endpoint, params);
}
}

View File

@@ -2,8 +2,12 @@ package com.orion.visor.framework.common.utils;
import com.orion.ext.location.Region;
import com.orion.ext.location.region.LocationRegions;
import com.orion.lang.constant.StandardHttpHeader;
import com.orion.lang.utils.Strings;
import com.orion.visor.framework.common.constant.Const;
import com.orion.web.servlet.web.Servlets;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@@ -21,6 +25,22 @@ public class IpUtils {
private IpUtils() {
}
/**
* 获取请求地址
*
* @param request request
* @return addr
*/
public static String getRemoteAddr(HttpServletRequest request) {
// 获取实际地址
String realIp = request.getHeader(StandardHttpHeader.X_REAL_IP);
if (!Strings.isBlank(realIp)) {
return realIp;
}
// 获取请求地址
return Servlets.getRemoteAddr(request);
}
/**
* 获取 ip 位置
*
@@ -29,7 +49,7 @@ public class IpUtils {
*/
public static String getLocation(String ip) {
if (ip == null) {
return Const.UNKNOWN;
return Const.CN_UNKNOWN;
}
// 查询缓存
return CACHE.computeIfAbsent(ip, IpUtils::queryLocation);
@@ -43,21 +63,21 @@ public class IpUtils {
*/
private static String queryLocation(String ip) {
if (ip == null) {
return Const.UNKNOWN;
return Const.CN_UNKNOWN;
}
Region region;
try {
region = LocationRegions.getRegion(ip, 3);
} catch (Exception e) {
return Const.UNKNOWN;
return Const.CN_UNKNOWN;
}
if (region != null) {
String net = region.getNet();
String province = region.getProvince();
if (net.equals(Const.INTRANET_IP)) {
if (net.equals(Const.CN_INTRANET_IP)) {
return net;
}
if (province.equals(Const.UNKNOWN)) {
if (province.equals(Const.CN_UNKNOWN)) {
return province;
}
StringBuilder location = new StringBuilder()
@@ -69,7 +89,7 @@ public class IpUtils {
location.append(" (").append(net).append(')');
return location.toString();
}
return Const.UNKNOWN;
return Const.CN_UNKNOWN;
}
}

View File

@@ -0,0 +1,38 @@
package com.orion.visor.framework.common.utils;
import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.crypto.Caesars;
/**
* 混淆工具类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/6/17 18:27
*/
public class Mixes {
private Mixes() {
}
/**
* 混淆
* <p>
* 此方法不可修改
*
* @param str str
* @return str
*/
public static String obfuscate(String str) {
char[] chars = str.toCharArray();
Arrays1.reverse(chars);
for (int i = 0; i < chars.length; i += 2) {
char temp = chars[i];
chars[i] = chars[i + 1];
chars[i + 1] = temp;
}
String res = new String(chars);
return new Caesars().encrypt(res);
}
}

View File

@@ -47,7 +47,7 @@ public class PathUtils {
public static String getAppPath(boolean isWindows, String username) {
return getHomePath(isWindows, username)
+ "/" + AppConst.ORION
+ "/" + AppConst.ORION_VISOR;
+ "/" + AppConst.APP_NAME;
}
/**

View File

@@ -41,7 +41,7 @@ public class Requests {
.map(s -> (ServletRequestAttributes) s)
.map(ServletRequestAttributes::getRequest)
.ifPresent(request -> {
String address = Servlets.getRemoteAddr(request);
String address = IpUtils.getRemoteAddr(request);
identity.setAddress(address);
identity.setLocation(IpUtils.getLocation(address));
identity.setUserAgent(Servlets.getUserAgent(request));

View File

@@ -10,7 +10,7 @@
{
"name": "orion.async.executor.core-pool-size",
"type": "java.lang.Integer",
"description": "核心线程数量",
"description": "核心线程数量.",
"defaultValue": "8"
},
{

View File

@@ -51,7 +51,7 @@ public class BannerApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
AnsiAppender appender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_GREEN, ":: orion-visor-launch v" + version + " 服务已启动(" + env + ") ::\n");
.append(AnsiForeground.BRIGHT_GREEN, ":: orion-visor v" + version + " 服务已启动(" + env + ") ::\n");
// swagger 地址
if (apiDocsEnabled) {
appender.append(AnsiForeground.BRIGHT_GREEN, ":: swagger 文档 ")

View File

@@ -1,6 +1,7 @@
package com.orion.visor.framework.log.configuration.config;
import com.orion.visor.framework.common.utils.ConfigUtils;
import com.orion.visor.framework.log.core.enums.LogPrinterMode;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -17,6 +18,11 @@ import java.util.List;
@ConfigurationProperties("orion.logging.printer")
public class LogPrinterConfig {
/**
* 类型
*/
private LogPrinterMode mode;
/**
* 字段配置
*/
@@ -32,12 +38,31 @@ public class LogPrinterConfig {
*/
private String expression;
public void setField(LogPrinterFieldConfig field) {
this.field = field;
}
public void setHeaders(List<String> headers) {
this.headers = ConfigUtils.parseStringList(headers, String::toLowerCase);
}
@Data
public static class LogPrinterFieldConfig {
/**
* 忽略的字段
*/
private List<String> ignore;
/**
* 脱敏的字段
*/
private List<String> desensitize;
public void setIgnore(List<String> ignore) {
this.ignore = ConfigUtils.parseStringList(ignore);
}
public void setDesensitize(List<String> desensitize) {
this.desensitize = ConfigUtils.parseStringList(desensitize);
}
}
}

View File

@@ -1,36 +0,0 @@
package com.orion.visor.framework.log.configuration.config;
import com.orion.visor.framework.common.utils.ConfigUtils;
import lombok.Data;
import java.util.List;
/**
* 日志打印字段配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/28 22:36
*/
@Data
public class LogPrinterFieldConfig {
/**
* 忽略的字段
*/
private List<String> ignore;
/**
* 脱敏的字段
*/
private List<String> desensitize;
public void setIgnore(List<String> ignore) {
this.ignore = ConfigUtils.parseStringList(ignore);
}
public void setDesensitize(List<String> desensitize) {
this.desensitize = ConfigUtils.parseStringList(desensitize);
}
}

View File

@@ -3,6 +3,7 @@ package com.orion.visor.framework.log.core.interceptor;
import com.orion.lang.utils.Exceptions;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.time.Dates;
import com.orion.visor.framework.common.utils.IpUtils;
import com.orion.visor.framework.common.utils.SwaggerUtils;
import com.orion.visor.framework.log.configuration.config.LogPrinterConfig;
import com.orion.web.servlet.web.Servlets;
@@ -60,7 +61,7 @@ public class PrettyLogPrinterInterceptor extends AbstractLogPrinterInterceptor {
// http
if (request != null) {
// remoteAddr
requestLog.append("\tremoteAddr: ").append(Servlets.getRemoteAddr(request)).append('\n');
requestLog.append("\tremoteAddr: ").append(IpUtils.getRemoteAddr(request)).append('\n');
// header
Servlets.getHeaderMap(request).forEach((hk, hv) -> {
if (headerFilter.test(hk.toLowerCase())) {

View File

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.orion.lang.utils.Exceptions;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.time.Dates;
import com.orion.visor.framework.common.utils.IpUtils;
import com.orion.visor.framework.common.utils.SwaggerUtils;
import com.orion.visor.framework.log.configuration.config.LogPrinterConfig;
import com.orion.visor.framework.log.core.enums.LogFieldConst;
@@ -61,7 +62,7 @@ public class RowLogPrinterInterceptor extends AbstractLogPrinterInterceptor impl
// http
if (request != null) {
// remoteAddr
fields.put(REMOTE_ADDR, Servlets.getRemoteAddr(request));
fields.put(REMOTE_ADDR, IpUtils.getRemoteAddr(request));
// header
Map<String, Object> headers = new LinkedHashMap<>();
Servlets.getHeaderMap(request).forEach((hk, hv) -> {

View File

@@ -38,13 +38,13 @@ public class CustomFileFilter {
public List<CustomFile> doFilter() {
// 生成文件副本
List<CustomFile> files = originCustomerFile.stream().map(s ->
new CustomFile.Builder()
.enableFileOverride()
.templatePath(s.getTemplatePath())
.filePath(s.getFilePath())
.fileName(s.getFileName())
.packageName(s.getPackageName())
.build())
new CustomFile.Builder()
.enableFileOverride()
.templatePath(s.getTemplatePath())
.filePath(s.getFilePath())
.fileName(s.getFileName())
.packageName(s.getPackageName())
.build())
.collect(Collectors.toList());
// 不生成对外 api 文件
if (!table.isEnableProviderApi()) {

View File

@@ -1,6 +1,6 @@
<template>
<a-modal v-model:visible="visible"
body-class="modal-form-large"
modal-class="modal-form-large"
title-align="start"
:title="title"
:top="80"

View File

@@ -6,7 +6,7 @@ const columns = [
title: 'id',
dataIndex: 'id',
slotName: 'id',
width: 100,
width: 80,
align: 'left',
fixed: 'left',
}, #foreach($field in ${table.fields})#if("$!field.propertyName" != "id"){
@@ -15,6 +15,7 @@ const columns = [
slotName: '${field.propertyName}',
align: 'left',
#if(${field.propertyType} == 'String')
minWidth: 238,
ellipsis: true,
tooltip: true,
#elseif(${field.propertyType} == 'Date')

View File

@@ -2,9 +2,9 @@ package com.orion.visor.framework.security.configuration;
import com.orion.visor.framework.common.constant.AutoConfigureOrderConst;
import com.orion.visor.framework.common.crypto.ValueCrypto;
import com.orion.visor.framework.security.configuration.config.CryptoConfig;
import com.orion.visor.framework.security.configuration.config.AesCryptoConfig;
import com.orion.visor.framework.security.core.crypto.PrimaryValueCrypto;
import com.orion.visor.framework.security.core.crypto.aes.AesCryptoProcessor;
import com.orion.visor.framework.security.core.crypto.processor.AesCryptoProcessor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -12,8 +12,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import javax.annotation.Resource;
/**
* 项目加密解密配置
*
@@ -22,13 +20,10 @@ import javax.annotation.Resource;
* @since 2023/7/7 23:59
*/
@AutoConfiguration
@EnableConfigurationProperties(CryptoConfig.class)
@EnableConfigurationProperties({AesCryptoConfig.class})
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_SECURITY_CRYPTO)
public class OrionCryptoAutoConfiguration {
@Resource
private CryptoConfig config;
/**
* @return 默认加密器
*/
@@ -43,8 +38,8 @@ public class OrionCryptoAutoConfiguration {
*/
@Bean(initMethod = "init")
@ConditionalOnProperty(value = "orion.crypto.aes.enabled", havingValue = "true")
public ValueCrypto aesValueCrypto() {
return new AesCryptoProcessor(config.getAes());
public ValueCrypto aesValueCrypto(AesCryptoConfig config) {
return new AesCryptoProcessor(config);
}
}

View File

@@ -1,4 +1,4 @@
package com.orion.visor.framework.security.core.crypto.aes;
package com.orion.visor.framework.security.configuration.config;
import com.orion.lang.utils.crypto.CryptoConst;
import com.orion.lang.utils.crypto.enums.PaddingMode;
@@ -6,6 +6,7 @@ import com.orion.lang.utils.crypto.enums.WorkingMode;
import com.orion.visor.framework.security.core.crypto.CryptoConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* aes 加密器配置
@@ -16,6 +17,7 @@ import lombok.EqualsAndHashCode;
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties("orion.crypto.aes")
public class AesCryptoConfig extends CryptoConfig {
/**

View File

@@ -1,23 +0,0 @@
package com.orion.visor.framework.security.configuration.config;
import com.orion.visor.framework.security.core.crypto.aes.AesCryptoConfig;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 加密配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/7/8 0:01
*/
@Data
@ConfigurationProperties("orion.crypto")
public class CryptoConfig {
/**
* aes 加密器配置
*/
private AesCryptoConfig aes;
}

View File

@@ -1,4 +1,4 @@
package com.orion.visor.framework.security.core.crypto.aes;
package com.orion.visor.framework.security.core.crypto.processor;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.crypto.Keys;
@@ -6,6 +6,7 @@ import com.orion.lang.utils.crypto.enums.CipherAlgorithm;
import com.orion.lang.utils.crypto.enums.WorkingMode;
import com.orion.lang.utils.crypto.symmetric.SymmetricBuilder;
import com.orion.lang.utils.crypto.symmetric.SymmetricCrypto;
import com.orion.visor.framework.security.configuration.config.AesCryptoConfig;
import com.orion.visor.framework.security.core.crypto.CryptoProcessor;
import javax.crypto.SecretKey;

View File

@@ -5,15 +5,10 @@
"type": "com.orion.visor.framework.security.configuration.config.SecurityConfig",
"sourceType": "com.orion.visor.framework.security.configuration.config.SecurityConfig"
},
{
"name": "orion.crypto",
"type": "com.orion.visor.framework.security.configuration.config.CryptoConfig",
"sourceType": "com.orion.visor.framework.security.configuration.config.CryptoConfig"
},
{
"name": "orion.crypto.aes",
"type": "com.orion.visor.framework.security.core.crypto.aes.AesCryptoConfig",
"sourceType": "com.orion.visor.framework.security.core.crypto.aes.AesCryptoConfig"
"type": "com.orion.visor.framework.security.configuration.config.AesCryptoConfig",
"sourceType": "com.orion.visor.framework.security.configuration.config.AesCryptoConfig"
}
],
"properties": [

View File

@@ -2,7 +2,8 @@ package com.orion.visor.framework.storage.configuration;
import com.orion.visor.framework.common.constant.AutoConfigureOrderConst;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.storage.configuration.config.StorageConfig;
import com.orion.visor.framework.storage.configuration.config.LocalStorageConfig;
import com.orion.visor.framework.storage.configuration.config.LogsStorageConfig;
import com.orion.visor.framework.storage.core.client.PrimaryFileClient;
import com.orion.visor.framework.storage.core.client.local.LocalFileClient;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -12,8 +13,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import javax.annotation.Resource;
/**
* 存储配置类
*
@@ -25,12 +24,9 @@ import javax.annotation.Resource;
*/
@AutoConfiguration
@AutoConfigureOrder(AutoConfigureOrderConst.FRAMEWORK_STORAGE)
@EnableConfigurationProperties(StorageConfig.class)
@EnableConfigurationProperties({LocalStorageConfig.class, LogsStorageConfig.class})
public class OrionStorageAutoConfiguration {
@Resource
private StorageConfig config;
/**
* @return 默认文件客户端
*/
@@ -45,8 +41,8 @@ public class OrionStorageAutoConfiguration {
*/
@Bean
@ConditionalOnProperty(value = "orion.storage.local.enabled", havingValue = "true")
public FileClient localFileClient() {
return new LocalFileClient(config.getLocal());
public FileClient localFileClient(LocalStorageConfig config) {
return new LocalFileClient(config);
}
/**
@@ -54,8 +50,8 @@ public class OrionStorageAutoConfiguration {
*/
@Bean
@ConditionalOnProperty(value = "orion.storage.logs.enabled", havingValue = "true")
public FileClient logsFileClient() {
return new LocalFileClient(config.getLogs());
public FileClient logsFileClient(LogsStorageConfig config) {
return new LocalFileClient(config);
}
}

View File

@@ -2,27 +2,19 @@ package com.orion.visor.framework.storage.configuration.config;
import com.orion.visor.framework.storage.core.client.local.LocalFileClientConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 存储配置
* 本地存储配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/30 18:40
*/
@Data
@ConfigurationProperties(prefix = "orion.storage")
public class StorageConfig {
/**
* 本地文件客户端 配置
*/
private LocalFileClientConfig local;
/**
* 日志文件客户端 配置
*/
private LocalFileClientConfig logs;
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties(prefix = "orion.storage.local")
public class LocalStorageConfig extends LocalFileClientConfig {
}

View File

@@ -0,0 +1,20 @@
package com.orion.visor.framework.storage.configuration.config;
import com.orion.visor.framework.storage.core.client.local.LocalFileClientConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 日志存储配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/30 18:40
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties(prefix = "orion.storage.logs")
public class LogsStorageConfig extends LocalFileClientConfig {
}

View File

@@ -1,19 +1,14 @@
{
"groups": [
{
"name": "orion.storage",
"type": "com.orion.visor.framework.storage.configuration.config.StorageConfig",
"sourceType": "com.orion.visor.framework.storage.configuration.config.StorageConfig"
},
{
"name": "orion.storage.local",
"type": "com.orion.visor.framework.storage.core.client.local.LocalFileClientConfig",
"sourceType": "com.orion.visor.framework.storage.core.client.local.LocalFileClientConfig"
"type": "com.orion.visor.framework.storage.configuration.config.LocalStorageConfig",
"sourceType": "com.orion.visor.framework.storage.configuration.config.LocalStorageConfig"
},
{
"name": "orion.storage.logs",
"type": "com.orion.visor.framework.storage.core.client.local.LocalFileClientConfig",
"sourceType": "com.orion.visor.framework.storage.core.client.local.LocalFileClientConfig"
"type": "com.orion.visor.framework.storage.configuration.config.LogsStorageConfig",
"sourceType": "com.orion.visor.framework.storage.configuration.config.LogsStorageConfig"
}
],
"properties": [

View File

@@ -10,7 +10,7 @@
{
"name": "orion.websocket.prefix",
"type": "java.lang.String",
"description": "公共 websocket 前缀"
"description": "公共 websocket 前缀."
},
{
"name": "orion.websocket.binary-buffer-size",

View File

@@ -3,7 +3,7 @@ server:
spring:
application:
name: orion-visor-launch
name: orion-visor
profiles:
active: dev
main:
@@ -142,7 +142,7 @@ knife4j:
logging:
file:
path: ${user.home}/orion/logs/orion-visor
path: ${user.home}/orion/logs/${spring.application.name}
name: ${logging.file.path}/app.log
logback:
rollingpolicy:
@@ -189,10 +189,16 @@ app:
exec-log:
# 是否拼接 ansi 执行状态日志
append-ansi: true
# 自动清理执行文件
auto-clear: true
# 保留周期 (天)
keep-period: 30
# 自动清理配置
auto-clear:
# 批量执行日志
exec-log:
enabled: false
keep-period: 30
# 主机连接日志
host-connect-log:
enabled: false
keep-period: 30
# orion framework config
orion:
@@ -253,14 +259,14 @@ orion:
timestamp-prefix: false
date-directory: false
storage-path: ${user.home}
base-path: /orion/orion-visor/storage
base-path: /orion/${spring.application.name}/storage
# 日志文件存储
logs:
enabled: true
timestamp-prefix: false
date-directory: false
storage-path: ${user.home}
base-path: /orion/orion-visor/logs
base-path: /orion/${spring.application.name}/logs
security:
password-encoder-length: 4
# 匿名接口

View File

@@ -1,9 +1,9 @@
package com.orion.visor.module.asset.controller;
import com.alibaba.fastjson.JSONArray;
import com.orion.visor.framework.log.core.annotation.IgnoreLog;
import com.orion.visor.framework.log.core.enums.IgnoreLogMode;
import com.orion.visor.framework.web.core.annotation.RestWrapper;
import com.orion.visor.module.asset.entity.vo.HostTerminalThemeVO;
import com.orion.visor.module.asset.service.HostTerminalService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* 主机终端 api
@@ -38,7 +39,7 @@ public class HostTerminalController {
@IgnoreLog(IgnoreLogMode.ALL)
@GetMapping("/themes")
@Operation(summary = "获取主机终端主题")
public JSONArray getTerminalThemes() {
public List<HostTerminalThemeVO> getTerminalThemes() {
return hostTerminalService.getTerminalThemes();
}

View File

@@ -0,0 +1,22 @@
package com.orion.visor.module.asset.define.config;
import com.orion.visor.framework.common.entity.AutoClearConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 批量执行日志自动清理配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/6/24 15:01
*/
@Data
@Component
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties(prefix = "app.auto-clear.exec-log")
public class AppExecLogAutoClearConfig extends AutoClearConfig {
}

View File

@@ -21,20 +21,8 @@ public class AppExecLogConfig {
*/
private Boolean appendAnsi;
/**
* 自动清理执行文件
*/
private Boolean autoClear;
/**
* 保留周期 (天)
*/
private Integer keepPeriod;
public AppExecLogConfig() {
this.appendAnsi = true;
this.autoClear = true;
this.keepPeriod = 30;
}
}

View File

@@ -0,0 +1,22 @@
package com.orion.visor.module.asset.define.config;
import com.orion.visor.framework.common.entity.AutoClearConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 主机连接日志自动清理配置
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/6/24 15:01
*/
@Data
@Component
@EqualsAndHashCode(callSuper = true)
@ConfigurationProperties(prefix = "app.auto-clear.host-connect-log")
public class AppHostConnectLogAutoClearConfig extends AutoClearConfig {
}

View File

@@ -20,16 +20,13 @@ import lombok.NoArgsConstructor;
@Schema(name = "ExecParameterSchemaDTO", description = "命令执行参数 schema 对象")
public class ExecParameterSchemaDTO {
@Schema(description = "参数名")
@Schema(description = "参数名")
private String name;
@Schema(description = "参数描述")
private String desc;
@Schema(description = "默认")
private Object defaultValue;
@Schema(description = "")
@Schema(description = "参数")
private Object value;
}

View File

@@ -7,6 +7,7 @@ import lombok.*;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.List;
/**
* 批量执行日志 查询请求对象
@@ -54,4 +55,11 @@ public class ExecLogQueryRequest extends PageRequest {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date[] startTimeRange;
@Schema(description = "创建时间 <=")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTimeLe;
@Schema(description = "状态")
private List<String> statusList;
}

View File

@@ -56,4 +56,11 @@ public class HostConnectLogQueryRequest extends PageRequest {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date[] startTimeRange;
@Schema(description = "创建时间 <=")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTimeLe;
@Schema(description = "状态")
private List<String> statusList;
}

View File

@@ -0,0 +1,33 @@
package com.orion.visor.module.asset.entity.vo;
import com.alibaba.fastjson.JSONObject;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 主机终端主题 视图响应对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/7/4 19:27
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "HostTerminalThemeVO", description = "主机终端主题 视图响应对象")
public class HostTerminalThemeVO {
@Schema(description = "主题名称")
private String name;
@Schema(description = "是否为暗色")
private Boolean dark;
@Schema(description = "主题 schema")
private JSONObject schema;
}

View File

@@ -1,8 +1,11 @@
package com.orion.visor.module.asset.enums;
import com.orion.lang.utils.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
/**
* 批量执行状态
*
@@ -36,6 +39,8 @@ public enum ExecStatusEnum {
;
public static final List<String> FINISH_STATUS_LIST = Lists.of(COMPLETED.name(), FAILED.name());
private final boolean closeable;
public static ExecStatusEnum of(String status) {

View File

@@ -1,5 +1,9 @@
package com.orion.visor.module.asset.enums;
import com.orion.lang.utils.collect.Lists;
import java.util.List;
/**
* 主机连接状态
*
@@ -31,6 +35,8 @@ public enum HostConnectStatusEnum {
;
public static final List<String> FINISH_STATUS_LIST = Lists.of(COMPLETE.name(), FAILED.name(), FORCE_OFFLINE.name());
public static HostConnectStatusEnum of(String type) {
if (type == null) {
return null;

View File

@@ -31,7 +31,7 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
// 拼接启动日志
AnsiAppender appender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_GREEN, "> 准备执行命令 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE, "执行记录: ")
.append(execCommand.getLogId())
@@ -81,7 +81,7 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
// 非脚本执行拼接开始执行日志
if (!Booleans.isTrue(execCommand.getScriptExec())) {
appender.append(AnsiForeground.BRIGHT_GREEN, "> 开始执行命令 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine();
}
this.appendLog(appender);
@@ -94,7 +94,7 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
AnsiAppender startAppender = AnsiAppender.create()
.newLine()
.append(AnsiForeground.BRIGHT_GREEN, "> 准备上传脚本 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE, "文件路径: ")
.append(execHostCommand.getScriptPath())
@@ -105,18 +105,18 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
// 拼接完成日志
AnsiAppender finishAppender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_GREEN, "< 脚本上传成功 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine()
.newLine()
.append(AnsiForeground.BRIGHT_GREEN, "> 开始执行脚本 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine();
this.appendLog(finishAppender);
} catch (Exception e) {
// 拼接失败日志
AnsiAppender errorAppender = AnsiAppender.create()
.append(AnsiForeground.BRIGHT_RED, "< 脚本上传失败 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine();
this.appendLog(errorAppender);
throw e;
@@ -133,12 +133,12 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
if (this.status == ExecHostStatusEnum.INTERRUPTED) {
// 中断执行
appender.append(AnsiForeground.BRIGHT_YELLOW, "< 命令执行中断 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine();
} else if (this.status == ExecHostStatusEnum.FAILED) {
// 执行失败
appender.append(AnsiForeground.BRIGHT_RED, "< 命令执行失败 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine()
.append(AnsiForeground.BRIGHT_RED, "错误原因: ")
.append(this.getErrorMessage(e))
@@ -146,14 +146,14 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
} else if (this.status == ExecHostStatusEnum.TIMEOUT) {
// 更新执行超时
appender.append(AnsiForeground.BRIGHT_YELLOW, "< 命令执行超时 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine();
} else {
long ms = updateRecord.getFinishTime().getTime() - updateRecord.getStartTime().getTime();
Integer exitCode = updateRecord.getExitCode();
// 执行完成
appender.append(AnsiForeground.BRIGHT_GREEN, "< 命令执行完成 ")
.append(Dates.current())
.append(this.getCurrentTime())
.newLine()
.append(AnsiForeground.BRIGHT_BLUE, "exit: ");
if (ExitCode.isSuccess(exitCode)) {
@@ -173,4 +173,13 @@ public class ExecCommandAnsiHandler extends BaseExecCommandHandler {
this.appendLog(appender);
}
/**
* 获取当前时间
*
* @return 当前时间
*/
private String getCurrentTime() {
return Const.SPACE + Dates.current();
}
}

View File

@@ -13,6 +13,7 @@ import com.orion.visor.framework.common.enums.BooleanBit;
import com.orion.visor.framework.websocket.core.utils.WebSockets;
import com.orion.visor.module.asset.dao.HostDAO;
import com.orion.visor.module.asset.define.operator.HostTerminalOperatorType;
import com.orion.visor.module.asset.entity.domain.HostConnectLogDO;
import com.orion.visor.module.asset.entity.domain.HostDO;
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
import com.orion.visor.module.asset.entity.request.host.HostConnectLogCreateRequest;
@@ -95,9 +96,9 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
log.error("TerminalCheckHandler-handle exception userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId, e);
}
// 记录主机日志
Long logId = this.saveHostLog(channel, userId, host, startTime, ex, sessionId, connectType);
HostConnectLogDO connectLog = this.saveHostLog(channel, userId, host, startTime, ex, sessionId, connectType);
if (connect != null) {
connect.setLogId(logId);
connect.setLogId(connectLog.getId());
}
// 响应检查结果
this.send(channel,
@@ -170,15 +171,15 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
* @param ex ex
* @param sessionId sessionId
* @param connectType connectType
* @return logId
* @return connectLog
*/
private Long saveHostLog(WebSocketSession channel,
Long userId,
HostDO host,
long startTime,
Exception ex,
String sessionId,
HostConnectTypeEnum connectType) {
private HostConnectLogDO saveHostLog(WebSocketSession channel,
Long userId,
HostDO host,
long startTime,
Exception ex,
String sessionId,
HostConnectTypeEnum connectType) {
Long hostId = host.getId();
String hostName = host.getName();
String username = WebSockets.getAttr(channel, ExtraFieldConst.USERNAME);

View File

@@ -65,7 +65,7 @@ public class HostTerminalManager {
*
* @param channelId channelId
* @param sessionId sessionId
* @param T T
* @param <T> T
* @return session
*/
@SuppressWarnings("unchecked")

View File

@@ -8,6 +8,7 @@ import com.orion.net.host.SessionStore;
import com.orion.net.host.sftp.SftpExecutor;
import com.orion.spring.SpringHolder;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.enums.EndpointDefine;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.common.utils.PathUtils;
import com.orion.visor.module.asset.dao.UploadTaskFileDAO;
@@ -18,7 +19,6 @@ import com.orion.visor.module.asset.enums.HostSshOsTypeEnum;
import com.orion.visor.module.asset.enums.UploadTaskFileStatusEnum;
import com.orion.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO;
import com.orion.visor.module.asset.service.HostTerminalService;
import com.orion.visor.module.asset.service.UploadTaskService;
import com.orion.visor.module.asset.utils.SftpUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@@ -134,7 +134,7 @@ public class FileUploader implements IFileUploader {
this.updateStatus(file, UploadTaskFileStatusEnum.UPLOADING);
try {
// 获取本地文件路径
String endpoint = Strings.format(UploadTaskService.SWAP_ENDPOINT, taskId);
String endpoint = EndpointDefine.UPLOAD_SWAP.format(taskId);
String localPath = localFileClient.getReturnPath(endpoint + Const.SLASH + file.getFileId());
// 检查文件是否存在
String remotePath = file.getRemotePath();

View File

@@ -71,6 +71,14 @@ public interface ExecLogService {
*/
Integer deleteExecLogByIdList(List<Long> idList, String source);
/**
* 批量删除批量执行日志
*
* @param idList idList
* @return effect
*/
Integer deleteExecLogByIdList(List<Long> idList);
/**
* 查询批量执行日志数量
*
@@ -128,4 +136,11 @@ public interface ExecLogService {
*/
void downloadLogFile(Long id, String source, HttpServletResponse response);
/**
* 异步删除日志文件
*
* @param idList idList
*/
void asyncDeleteLogFiles(List<Long> idList);
}

View File

@@ -1,6 +1,7 @@
package com.orion.visor.module.asset.service;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.visor.module.asset.entity.domain.HostConnectLogDO;
import com.orion.visor.module.asset.entity.request.host.HostConnectLogCreateRequest;
import com.orion.visor.module.asset.entity.request.host.HostConnectLogQueryRequest;
import com.orion.visor.module.asset.entity.vo.HostConnectLogVO;
@@ -25,9 +26,9 @@ public interface HostConnectLogService {
*
* @param type type
* @param request request
* @return id
* @return record
*/
Long create(HostConnectTypeEnum type, HostConnectLogCreateRequest request);
HostConnectLogDO create(HostConnectTypeEnum type, HostConnectLogCreateRequest request);
/**
* 分页查询主机连接日志

View File

@@ -1,10 +1,12 @@
package com.orion.visor.module.asset.service;
import com.alibaba.fastjson.JSONArray;
import com.orion.net.host.SessionStore;
import com.orion.visor.module.asset.entity.domain.HostDO;
import com.orion.visor.module.asset.entity.dto.HostTerminalAccessDTO;
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
import com.orion.visor.module.asset.entity.vo.HostTerminalThemeVO;
import java.util.List;
/**
* 主机终端服务
@@ -20,7 +22,7 @@ public interface HostTerminalService {
*
* @return themes
*/
JSONArray getTerminalThemes();
List<HostTerminalThemeVO> getTerminalThemes();
/**
* 获取主机终端访问 accessToken

View File

@@ -19,8 +19,6 @@ import java.util.List;
*/
public interface UploadTaskService {
String SWAP_ENDPOINT = "/upload/swap/{}";
/**
* 创建上传任务
*

View File

@@ -109,6 +109,7 @@ public class CommandSnippetGroupServiceImpl implements CommandSnippetGroupServic
.createWrapper()
.select(CommandSnippetDO::getUserId, CommandSnippetDO::getGroupId)
.isNotNull(CommandSnippetDO::getGroupId)
.groupBy(CommandSnippetDO::getUserId)
.groupBy(CommandSnippetDO::getGroupId)
.then()
.stream()

View File

@@ -51,7 +51,7 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
@Override
public Long createCommandSnippet(CommandSnippetCreateRequest request) {
Long userId = SecurityUtils.getLoginUserId();
log.info("CommandSnippetService-createCommandSnippet request: {}" , JSON.toJSONString(request));
log.info("CommandSnippetService-createCommandSnippet request: {}", JSON.toJSONString(request));
// 转换
CommandSnippetDO record = CommandSnippetConvert.MAPPER.to(request);
record.setUserId(userId);
@@ -60,7 +60,7 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
// 插入
int effect = commandSnippetDAO.insert(record);
Long id = record.getId();
log.info("CommandSnippetService-createCommandSnippet id: {}, effect: {}" , id, effect);
log.info("CommandSnippetService-createCommandSnippet id: {}, effect: {}", id, effect);
// 删除缓存
RedisMaps.delete(CommandSnippetCacheKeyDefine.SNIPPET.format(userId));
return id;
@@ -70,7 +70,7 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
public Integer updateCommandSnippetById(CommandSnippetUpdateRequest request) {
Long id = Valid.notNull(request.getId(), ErrorMessage.ID_MISSING);
Long userId = SecurityUtils.getLoginUserId();
log.info("CommandSnippetService-updateCommandSnippetById id: {}, request: {}" , id, JSON.toJSONString(request));
log.info("CommandSnippetService-updateCommandSnippetById id: {}, request: {}", id, JSON.toJSONString(request));
// 查询
CommandSnippetDO record = commandSnippetDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
@@ -85,7 +85,7 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
.eq(CommandSnippetDO::getId, id)
.eq(CommandSnippetDO::getUserId, userId);
int effect = commandSnippetDAO.update(null, update);
log.info("CommandSnippetService-updateCommandSnippetById effect: {}" , effect);
log.info("CommandSnippetService-updateCommandSnippetById effect: {}", effect);
// 删除缓存
RedisMaps.delete(CommandSnippetCacheKeyDefine.SNIPPET.format(userId));
return effect;
@@ -154,13 +154,13 @@ public class CommandSnippetServiceImpl implements CommandSnippetService {
@Override
public Integer deleteCommandSnippetById(Long id) {
Long userId = SecurityUtils.getLoginUserId();
log.info("CommandSnippetService-deleteCommandSnippetById id: {}" , id);
log.info("CommandSnippetService-deleteCommandSnippetById id: {}", id);
// 检查数据是否存在
CommandSnippetDO record = commandSnippetDAO.selectById(id);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 删除
int effect = commandSnippetDAO.deleteById(id);
log.info("CommandSnippetService-deleteCommandSnippetById id: {}, effect: {}" , id, effect);
log.info("CommandSnippetService-deleteCommandSnippetById id: {}, effect: {}", id, effect);
// 删除缓存
RedisMaps.delete(CommandSnippetCacheKeyDefine.SNIPPET.format(userId), id);
return effect;

View File

@@ -14,7 +14,8 @@ import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.visor.framework.common.annotation.Keep;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.constant.ErrorMessage;
import com.orion.visor.framework.common.constant.PathConst;
import com.orion.visor.framework.common.constant.FileConst;
import com.orion.visor.framework.common.enums.EndpointDefine;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.common.security.LoginUser;
import com.orion.visor.framework.common.utils.PathUtils;
@@ -140,10 +141,10 @@ public class ExecCommandServiceImpl implements ExecCommandService {
execLogDAO.insert(execLog);
Long execId = execLog.getId();
// 获取内置参数
Map<String, Object> builtinsParams = this.getBaseBuiltinsParams(execId, request);
Map<String, Object> builtinParams = this.getBaseBuiltinParams(execId, request);
// 设置主机日志
List<ExecHostLogDO> execHostLogs = hosts.stream()
.map(s -> this.convertExecHostLog(s, execLog, hostConfigMap.get(s.getId()), builtinsParams))
.map(s -> this.convertExecHostLog(s, execLog, hostConfigMap.get(s.getId()), builtinParams))
.collect(Collectors.toList());
execHostLogDAO.insertBatch(execHostLogs);
// 操作日志
@@ -228,16 +229,16 @@ public class ExecCommandServiceImpl implements ExecCommandService {
/**
* 转换为 execHostLog
*
* @param host host
* @param execLog execLog
* @param config config
* @param builtinsParams builtinsParams
* @param host host
* @param execLog execLog
* @param config config
* @param builtinParams builtinParams
* @return execHostLog
*/
private ExecHostLogDO convertExecHostLog(HostDO host,
ExecLogDO execLog,
HostSshConfigModel config,
Map<String, Object> builtinsParams) {
Map<String, Object> builtinParams) {
Long execId = execLog.getId();
Long hostId = host.getId();
// 脚本路径
@@ -246,7 +247,7 @@ public class ExecCommandServiceImpl implements ExecCommandService {
scriptPath = this.buildScriptPath(config.getUsername(), config.getOsType(), execId, hostId);
}
// 获取参数
String parameter = JSON.toJSONString(this.getHostParams(builtinsParams, host, config, scriptPath));
String parameter = JSON.toJSONString(this.getHostParams(builtinParams, host, config, scriptPath));
return ExecHostLogDO.builder()
.logId(execId)
.hostId(hostId)
@@ -267,7 +268,7 @@ public class ExecCommandServiceImpl implements ExecCommandService {
* @param request request
* @return params
*/
private Map<String, Object> getBaseBuiltinsParams(Long execId, ExecCommandExecDTO request) {
private Map<String, Object> getBaseBuiltinParams(Long execId, ExecCommandExecDTO request) {
String uuid = UUIds.random();
Date date = new Date();
// 输入参数
@@ -334,9 +335,6 @@ public class ExecCommandServiceImpl implements ExecCommandService {
.collect(Collectors.toMap(ExecParameterSchemaDTO::getName,
s -> {
Object value = s.getValue();
if (value == null) {
value = s.getDefaultValue();
}
if (value == null) {
value = Const.EMPTY;
}
@@ -353,8 +351,7 @@ public class ExecCommandServiceImpl implements ExecCommandService {
* @return logPath
*/
private String buildLogPath(Long logId, Long hostId) {
String logFile = "/" + PathConst.EXEC + "/" + logId + "/" + logId + "_" + hostId + ".log";
return logsFileClient.getReturnPath(logFile);
return logsFileClient.getReturnPath(EndpointDefine.EXEC_LOG.format(logId, hostId));
}
/**
@@ -368,11 +365,11 @@ public class ExecCommandServiceImpl implements ExecCommandService {
*/
private String buildScriptPath(String username, String osType, Long logId, Long hostId) {
HostSshOsTypeEnum os = HostSshOsTypeEnum.of(osType);
String name = PathConst.EXEC
+ "_" + logId
+ "_" + hostId
String name = FileConst.EXEC
+ "/" + logId
+ "/" + hostId
+ os.getScriptSuffix();
return PathUtils.buildAppPath(HostSshOsTypeEnum.WINDOWS.equals(os), username, PathConst.SCRIPT, name);
return PathUtils.buildAppPath(HostSshOsTypeEnum.WINDOWS.equals(os), username, FileConst.SCRIPT, name);
}
}

View File

@@ -11,11 +11,13 @@ import com.orion.lang.utils.Strings;
import com.orion.lang.utils.collect.Lists;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.io.Streams;
import com.orion.spring.SpringHolder;
import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.visor.framework.common.annotation.Keep;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.constant.ErrorMessage;
import com.orion.visor.framework.common.constant.PathConst;
import com.orion.visor.framework.common.constant.FileConst;
import com.orion.visor.framework.common.enums.EndpointDefine;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.common.utils.Valid;
import com.orion.visor.framework.redis.core.utils.RedisStrings;
@@ -46,11 +48,13 @@ import com.orion.visor.module.asset.service.ExecLogService;
import com.orion.visor.module.asset.service.HostConfigService;
import com.orion.web.servlet.web.Servlets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
@@ -173,20 +177,7 @@ public class ExecLogServiceImpl implements ExecLogService {
@Override
@Transactional(rollbackFor = Exception.class)
public Integer deleteExecLogById(Long id, String source) {
log.info("ExecLogService-deleteExecLogById id: {}", id);
// 检查数据是否存在
ExecLogDO record = execLogDAO.selectByIdSource(id, source);
Valid.notNull(record, ErrorMessage.DATA_ABSENT);
// 中断命令执行
this.interruptTask(Lists.singleton(id));
// 删除执行日志
int effect = execLogDAO.deleteById(id);
// 删除主机日志
execHostLogService.deleteExecHostLogByLogId(Lists.singleton(id));
log.info("ExecLogService-deleteExecLogById id: {}, effect: {}", id, effect);
// 设置日志参数
OperatorLogs.add(OperatorLogs.COUNT, effect);
return effect;
return this.deleteExecLogByIdList(Lists.singleton(id), source);
}
@Override
@@ -201,15 +192,28 @@ public class ExecLogServiceImpl implements ExecLogService {
.count()
.intValue();
Valid.isTrue(idList.size() == count, ErrorMessage.DATA_MODIFIED);
// 删除
return this.deleteExecLogByIdList(idList);
}
@Override
public Integer deleteExecLogByIdList(List<Long> idList) {
log.info("ExecLogService-deleteExecLogByIdList start: {}", idList);
if (Lists.isEmpty(idList)) {
OperatorLogs.add(OperatorLogs.COUNT, Const.N_0);
return Const.N_0;
}
// 中断命令执行
this.interruptTask(idList);
// 删除执行日志
int effect = execLogDAO.deleteBatchIds(idList);
// 删除主机日志
execHostLogService.deleteExecHostLogByLogId(idList);
log.info("ExecLogService-deleteExecLogByIdList effect: {}", effect);
log.info("ExecLogService-deleteExecLogByIdList end effect: {}", effect);
// 设置日志参数
OperatorLogs.add(OperatorLogs.COUNT, effect);
// 异步删除文件
SpringHolder.getBean(ExecLogService.class).asyncDeleteLogFiles(idList);
return effect;
}
@@ -229,19 +233,8 @@ public class ExecLogServiceImpl implements ExecLogService {
.stream()
.map(ExecLogDO::getId)
.collect(Collectors.toList());
int effect = 0;
if (!idList.isEmpty()) {
// 中断命令执行
this.interruptTask(idList);
// 删除执行日志
effect = execLogDAO.delete(wrapper);
// 删除主机日志
execHostLogService.deleteExecHostLogByLogId(idList);
}
log.info("ExecLogService.clearExecLog finish {}", effect);
// 设置日志参数
OperatorLogs.add(OperatorLogs.COUNT, effect);
return effect;
// 删除
return this.deleteExecLogByIdList(idList);
}
@Override
@@ -428,19 +421,35 @@ public class ExecLogServiceImpl implements ExecLogService {
} catch (Exception e) {
log.error("ExecLogService.downloadLogFile error id: {}", id, e);
Streams.close(in);
String errorMessage = ErrorMessage.FILE_READ_ERROR;
String errorMessage = ErrorMessage.FILE_READ_ERROR_CLEAR;
if (e instanceof InvalidArgumentException) {
errorMessage = e.getMessage();
}
// 响应错误信息
try {
Servlets.transfer(response, Strings.bytes(errorMessage), PathConst.ERROR_LOG);
Servlets.transfer(response, Strings.bytes(errorMessage), FileConst.ERROR_LOG);
} catch (Exception ex) {
log.error("ExecLogService.downloadLogFile transfer-error id: {}", id, ex);
}
}
}
@Override
@Async("asyncExecutor")
public void asyncDeleteLogFiles(List<Long> idList) {
if (Lists.isEmpty(idList)) {
return;
}
// 删除
idList.stream()
.map(s -> EndpointDefine.EXEC_LOG.format(s, Const.EMPTY))
.map(logsFileClient::getReturnPath)
.map(logsFileClient::getAbsolutePath)
.map(Files1::getParentPath)
.map(File::new)
.forEach(Files1::delete);
}
/**
* 构建查询 wrapper
*
@@ -457,8 +466,10 @@ public class ExecLogServiceImpl implements ExecLogService {
.like(ExecLogDO::getDescription, request.getDescription())
.like(ExecLogDO::getCommand, request.getCommand())
.eq(ExecLogDO::getStatus, request.getStatus())
.in(ExecLogDO::getStatus, request.getStatusList())
.ge(ExecLogDO::getStartTime, Arrays1.getIfPresent(request.getStartTimeRange(), 0))
.le(ExecLogDO::getStartTime, Arrays1.getIfPresent(request.getStartTimeRange(), 1))
.le(ExecLogDO::getCreateTime, request.getCreateTimeLe())
.orderByDesc(ExecLogDO::getId);
}

View File

@@ -2,13 +2,13 @@ package com.orion.visor.module.asset.service.impl;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.orion.lang.constant.Const;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.Valid;
import com.orion.lang.utils.collect.Lists;
import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.constant.ErrorMessage;
import com.orion.visor.framework.common.utils.Valid;
import com.orion.visor.framework.security.core.utils.SecurityUtils;
import com.orion.visor.module.asset.convert.HostConnectLogConvert;
import com.orion.visor.module.asset.dao.HostConnectLogDAO;
@@ -52,7 +52,7 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
private HostTerminalManager hostTerminalManager;
@Override
public Long create(HostConnectTypeEnum type, HostConnectLogCreateRequest request) {
public HostConnectLogDO create(HostConnectTypeEnum type, HostConnectLogCreateRequest request) {
HostConnectLogDO record = HostConnectLogConvert.MAPPER.to(request);
record.setType(type.name());
String status = request.getStatus();
@@ -64,7 +64,7 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
record.setEndTime(new Date());
}
hostConnectLogDAO.insert(record);
return record.getId();
return record;
}
@Override
@@ -169,6 +169,11 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
@Override
public Integer deleteHostConnectLog(List<Long> idList) {
log.info("HostConnectLogService.deleteHostConnectLog start {}", JSON.toJSONString(idList));
if (Lists.isEmpty(idList)) {
OperatorLogs.add(OperatorLogs.COUNT, Const.N_0);
return Const.N_0;
}
// 删除
int effect = hostConnectLogDAO.deleteBatchIds(idList);
log.info("HostConnectLogService.deleteHostConnectLog finish {}", effect);
// 设置日志参数
@@ -184,13 +189,21 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
@Override
public Integer clearHostConnectLog(HostConnectLogQueryRequest request) {
log.info("HostConnectLogService.clearHostConnectLog start {}", JSON.toJSONString(request));
// 查询
LambdaQueryWrapper<HostConnectLogDO> wrapper = this.buildQueryWrapper(request)
.select(HostConnectLogDO::getId);
List<HostConnectLogDO> list = hostConnectLogDAO.selectList(wrapper);
if (list.isEmpty()) {
log.info("HostConnectLogService.clearHostConnectLog empty");
// 设置日志参数
OperatorLogs.add(OperatorLogs.COUNT, Const.N_0);
return Const.N_0;
}
// 删除
LambdaQueryWrapper<HostConnectLogDO> wrapper = this.buildQueryWrapper(request);
int effect = hostConnectLogDAO.delete(wrapper);
log.info("HostConnectLogService.clearHostConnectLog finish {}", effect);
// 设置日志参数
OperatorLogs.add(OperatorLogs.COUNT, effect);
return effect;
List<Long> idList = list.stream()
.map(HostConnectLogDO::getId)
.collect(Collectors.toList());
return this.deleteHostConnectLog(idList);
}
@Override
@@ -229,8 +242,10 @@ public class HostConnectLogServiceImpl implements HostConnectLogService {
.eq(HostConnectLogDO::getType, request.getType())
.like(HostConnectLogDO::getToken, request.getToken())
.eq(HostConnectLogDO::getStatus, request.getStatus())
.in(HostConnectLogDO::getStatus, request.getStatusList())
.ge(HostConnectLogDO::getStartTime, Arrays1.getIfPresent(request.getStartTimeRange(), 0))
.le(HostConnectLogDO::getStartTime, Arrays1.getIfPresent(request.getStartTimeRange(), 1))
.le(HostConnectLogDO::getCreateTime, request.getCreateTimeLe())
.orderByDesc(HostConnectLogDO::getId);
}

View File

@@ -1,15 +1,15 @@
package com.orion.visor.module.asset.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.orion.lang.exception.AuthenticationException;
import com.orion.lang.id.UUIds;
import com.orion.lang.utils.Exceptions;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.io.StreamReaders;
import com.orion.net.host.SessionHolder;
import com.orion.net.host.SessionStore;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.constant.ErrorMessage;
import com.orion.visor.framework.common.constant.ExtraFieldConst;
import com.orion.visor.framework.common.security.LoginUser;
import com.orion.visor.framework.common.utils.CryptoUtils;
import com.orion.visor.framework.common.utils.Valid;
@@ -24,6 +24,7 @@ import com.orion.visor.module.asset.entity.domain.HostIdentityDO;
import com.orion.visor.module.asset.entity.domain.HostKeyDO;
import com.orion.visor.module.asset.entity.dto.HostTerminalAccessDTO;
import com.orion.visor.module.asset.entity.dto.HostTerminalConnectDTO;
import com.orion.visor.module.asset.entity.vo.HostTerminalThemeVO;
import com.orion.visor.module.asset.enums.*;
import com.orion.visor.module.asset.handler.host.config.model.HostSshConfigModel;
import com.orion.visor.module.asset.handler.host.extra.model.HostSshExtraModel;
@@ -31,14 +32,15 @@ import com.orion.visor.module.asset.service.HostConfigService;
import com.orion.visor.module.asset.service.HostExtraService;
import com.orion.visor.module.asset.service.HostTerminalService;
import com.orion.visor.module.infra.api.DataPermissionApi;
import com.orion.visor.module.infra.api.DictValueApi;
import com.orion.visor.module.infra.enums.DataPermissionTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 主机连接服务
@@ -51,7 +53,7 @@ import java.util.Optional;
@Service
public class HostTerminalServiceImpl implements HostTerminalService {
private static final String TERMINAL_PATH = "/theme/terminal.theme.json";
private static final String THEME_DICT_KEY = "terminalTheme";
@Resource
private HostConfigService hostConfigService;
@@ -74,20 +76,24 @@ public class HostTerminalServiceImpl implements HostTerminalService {
@Resource
private DataPermissionApi dataPermissionApi;
@Resource
private DictValueApi dictValueApi;
@Override
public JSONArray getTerminalThemes() {
try (InputStream in = HostTerminalService.class.getResourceAsStream(TERMINAL_PATH)) {
Valid.notNull(in, ErrorMessage.CONFIG_ABSENT);
byte[] bytes = StreamReaders.readAllBytes(in);
return JSONArray.parseArray(new String(bytes));
} catch (Exception e) {
throw Exceptions.ioRuntime(e);
}
public List<HostTerminalThemeVO> getTerminalThemes() {
List<JSONObject> themes = dictValueApi.getDictValue(THEME_DICT_KEY);
return themes.stream()
.map(s -> HostTerminalThemeVO.builder()
.name(s.getString(ExtraFieldConst.LABEL))
.dark(s.getBoolean(ExtraFieldConst.DARK))
.schema(s.getJSONObject(ExtraFieldConst.VALUE))
.build())
.collect(Collectors.toList());
}
@Override
public String getTerminalAccessToken() {
LoginUser user = SecurityUtils.getLoginUser();
LoginUser user = Valid.notNull(SecurityUtils.getLoginUser());
log.info("HostConnectService.getHostAccessToken userId: {}", user.getId());
String accessToken = UUIds.random19();
HostTerminalAccessDTO access = HostTerminalAccessDTO.builder()

View File

@@ -110,6 +110,7 @@ public class PathBookmarkGroupServiceImpl implements PathBookmarkGroupService {
.select(PathBookmarkDO::getUserId, PathBookmarkDO::getGroupId)
.isNotNull(PathBookmarkDO::getGroupId)
.groupBy(PathBookmarkDO::getGroupId)
.groupBy(PathBookmarkDO::getUserId)
.then()
.stream()
.collect(Collectors.groupingBy(PathBookmarkDO::getUserId,

View File

@@ -15,6 +15,7 @@ import com.orion.visor.framework.biz.operator.log.core.utils.OperatorLogs;
import com.orion.visor.framework.common.annotation.Keep;
import com.orion.visor.framework.common.constant.Const;
import com.orion.visor.framework.common.constant.ErrorMessage;
import com.orion.visor.framework.common.enums.EndpointDefine;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.common.security.LoginUser;
import com.orion.visor.framework.common.utils.Valid;
@@ -38,8 +39,8 @@ import com.orion.visor.module.asset.enums.HostConfigTypeEnum;
import com.orion.visor.module.asset.enums.UploadTaskFileStatusEnum;
import com.orion.visor.module.asset.enums.UploadTaskStatusEnum;
import com.orion.visor.module.asset.handler.host.upload.FileUploadTasks;
import com.orion.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO;
import com.orion.visor.module.asset.handler.host.upload.manager.FileUploadTaskManager;
import com.orion.visor.module.asset.handler.host.upload.model.FileUploadFileItemDTO;
import com.orion.visor.module.asset.handler.host.upload.task.IFileUploadTask;
import com.orion.visor.module.asset.handler.host.upload.uploader.IFileUploader;
import com.orion.visor.module.asset.service.AssetAuthorizedDataService;
@@ -140,7 +141,7 @@ public class UploadTaskServiceImpl implements UploadTaskService {
}
uploadTaskFileDAO.insertBatch(uploadFiles);
// 设置 uploadToken
String token = fileUploadApi.createUploadToken(user.getId(), Strings.format(SWAP_ENDPOINT, id));
String token = fileUploadApi.createUploadToken(user.getId(), EndpointDefine.UPLOAD_SWAP.format(id));
log.info("UploadTaskService-createUploadTask id: {}, effect: {}", id, effect);
// 添加日志参数
OperatorLogs.add(OperatorLogs.NAME, record.getDescription());
@@ -296,7 +297,7 @@ public class UploadTaskServiceImpl implements UploadTaskService {
}
// 查询记录
List<String> paths = idList.stream()
.map(s -> Strings.format(SWAP_ENDPOINT, s))
.map(EndpointDefine.UPLOAD_SWAP::format)
.map(localFileClient::getReturnPath)
.map(localFileClient::getAbsolutePath)
.collect(Collectors.toList());
@@ -348,7 +349,7 @@ public class UploadTaskServiceImpl implements UploadTaskService {
.collect(Collectors.groupingBy(UploadTaskFileDO::getFileId));
fileIdGroups.forEach((k, v) -> {
// 获取文件实际路径
String path = localFileClient.getReturnPath(Strings.format(SWAP_ENDPOINT, id) + Const.SLASH + k);
String path = localFileClient.getReturnPath(EndpointDefine.UPLOAD_SWAP.format(id) + Const.SLASH + k);
File file = new File(localFileClient.getAbsolutePath(path));
// 文件不存在/大小不正确
if (!Files1.isFile(file) || file.length() != v.get(0).getFileSize()) {

View File

@@ -33,7 +33,7 @@ public class CommandSnippetGroupAutoClearTask {
@Scheduled(cron = "0 10 2 * * ?")
public void clear() {
log.info("CommandSnippetGroupAutoClearTask.clear start");
// 获取锁并清理
// 获取锁并执行
LockerUtils.tryLock(LOCK_KEY, commandSnippetGroupService::clearUnusedGroup);
log.info("CommandSnippetGroupAutoClearTask.clear finish");
}

View File

@@ -1,22 +1,18 @@
package com.orion.visor.module.asset.task;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.time.Dates;
import com.orion.visor.framework.common.annotation.Keep;
import com.orion.visor.framework.common.file.FileClient;
import com.orion.visor.framework.common.utils.LockerUtils;
import com.orion.visor.module.asset.dao.ExecHostLogDAO;
import com.orion.visor.module.asset.define.config.AppExecLogConfig;
import com.orion.visor.module.asset.entity.domain.ExecHostLogDO;
import com.orion.visor.module.asset.define.config.AppExecLogAutoClearConfig;
import com.orion.visor.module.asset.entity.request.exec.ExecLogQueryRequest;
import com.orion.visor.module.asset.enums.ExecStatusEnum;
import com.orion.visor.module.asset.service.ExecLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.File;
import java.util.List;
import java.util.Date;
/**
* 执行日志文件自动清理
@@ -27,23 +23,19 @@ import java.util.List;
*/
@Slf4j
@Component
@ConditionalOnProperty(value = "app.exec-log.auto-clear", havingValue = "true", matchIfMissing = true)
@ConditionalOnProperty(value = "app.auto-clear.exec-log.enabled", havingValue = "true")
public class ExecLogFileAutoClearTask {
/**
* 分布式锁名称
*/
private static final String LOCK_KEY = "clear:elf:lock";
private static final String LOCK_KEY = "clear:exl:lock";
@Resource
private AppExecLogConfig appExecLogConfig;
@Keep
@Resource
private FileClient logsFileClient;
private AppExecLogAutoClearConfig appExecLogAutoClearConfig;
@Resource
private ExecHostLogDAO execHostLogDAO;
private ExecLogService execLogService;
/**
* 清理
@@ -51,46 +43,24 @@ public class ExecLogFileAutoClearTask {
@Scheduled(cron = "0 0 3 * * ?")
public void clear() {
log.info("ExecLogFileAutoClearTask.clear start");
// 获取锁并执行
LockerUtils.tryLock(LOCK_KEY, this::doClearFile);
// 获取锁并执行
LockerUtils.tryLock(LOCK_KEY, this::doClear);
log.info("ExecLogFileAutoClearTask.clear finish");
}
/**
* 执行清理文件
* 执行清理
*/
private void doClearFile() {
private void doClear() {
// 删除的时间区间
String maxPeriod = Dates.stream()
.subDay(appExecLogConfig.getKeepPeriod())
.format();
// 获取需要删除的最大id
ExecHostLogDO hostLog = execHostLogDAO.of()
.createWrapper()
.select(ExecHostLogDO::getLogId, ExecHostLogDO::getLogPath)
.lt(ExecHostLogDO::getCreateTime, maxPeriod)
.orderByDesc(ExecHostLogDO::getId)
.then()
.getOne();
if (hostLog == null) {
return;
}
// 获取执行日志根目录
String hostLogPath = logsFileClient.getAbsolutePath(hostLog.getLogPath());
String execLogPath = Files1.getParentPath(hostLogPath);
String parentPath = Files1.getParentPath(execLogPath);
// 获取需要删除的文件
List<File> files = Files1.listFilesFilter(parentPath, s -> {
if (!Strings.isInteger(s.getName())) {
return false;
}
return Long.parseLong(s.getName()) <= hostLog.getLogId();
}, false, true);
if (files.isEmpty()) {
return;
}
// 删除日志文件
files.forEach(Files1::delete);
Date createLessEq = Dates.stream()
.subDay(appExecLogAutoClearConfig.getKeepPeriod())
.date();
// 清理
ExecLogQueryRequest request = new ExecLogQueryRequest();
request.setCreateTimeLe(createLessEq);
request.setStatusList(ExecStatusEnum.FINISH_STATUS_LIST);
execLogService.clearExecLog(request);
}
}

View File

@@ -0,0 +1,66 @@
package com.orion.visor.module.asset.task;
import com.orion.lang.utils.time.Dates;
import com.orion.visor.framework.common.utils.LockerUtils;
import com.orion.visor.module.asset.define.config.AppHostConnectLogAutoClearConfig;
import com.orion.visor.module.asset.entity.request.host.HostConnectLogQueryRequest;
import com.orion.visor.module.asset.enums.HostConnectStatusEnum;
import com.orion.visor.module.asset.service.HostConnectLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
/**
* 主机连接日志自动清理
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/4/24 23:50
*/
@Slf4j
@Component
@ConditionalOnProperty(value = "app.auto-clear.host-connect-log.enabled", havingValue = "true")
public class HostConnectLogAutoClearTask {
/**
* 分布式锁名称
*/
private static final String LOCK_KEY = "clear:hcl:lock";
@Resource
private AppHostConnectLogAutoClearConfig appHostConnectLogAutoClearConfig;
@Resource
private HostConnectLogService hostConnectLogService;
/**
* 清理
*/
@Scheduled(cron = "0 10 3 * * ?")
public void clear() {
log.info("HostConnectLogAutoClearTask.clear start");
// 获取锁并执行
LockerUtils.tryLock(LOCK_KEY, this::doClear);
log.info("HostConnectLogAutoClearTask.clear finish");
}
/**
* 执行清理
*/
private void doClear() {
// 删除的时间区间
Date createLessEq = Dates.stream()
.subDay(appHostConnectLogAutoClearConfig.getKeepPeriod())
.date();
// 清理
HostConnectLogQueryRequest request = new HostConnectLogQueryRequest();
request.setCreateTimeLe(createLessEq);
request.setStatusList(HostConnectStatusEnum.FINISH_STATUS_LIST);
hostConnectLogService.clearHostConnectLog(request);
}
}

View File

@@ -33,7 +33,7 @@ public class PathBookmarkGroupAutoClearTask {
@Scheduled(cron = "0 20 2 * * ?")
public void clear() {
log.info("PathBookmarkGroupAutoClearTask.clear start");
// 获取锁并清理
// 获取锁并执行
LockerUtils.tryLock(LOCK_KEY, pathBookmarkGroupService::clearUnusedGroup);
log.info("PathBookmarkGroupAutoClearTask.clear finish");
}

View File

@@ -14,25 +14,35 @@
"name": "app.exec-log",
"type": "com.orion.visor.module.asset.define.config.AppExecLogConfig",
"sourceType": "com.orion.visor.module.asset.define.config.AppExecLogConfig"
},
{
"name": "app.auto-clear.exec-log",
"type": "com.orion.visor.module.asset.define.config.AppExecLogAutoClearConfig",
"sourceType": "com.orion.visor.module.asset.define.config.AppExecLogAutoClearConfig"
},
{
"name": "app.auto-clear.host-connect-log",
"type": "com.orion.visor.module.asset.define.config.AppHostConnectLogAutoClearConfig",
"sourceType": "com.orion.visor.module.asset.define.config.AppHostConnectLogAutoClearConfig"
}
],
"properties": [
{
"name": "app.tracker.offset",
"type": "java.lang.Integer",
"description": "加载偏移量 (行)",
"description": "加载偏移量 (行).",
"defaultValue": "300"
},
{
"name": "app.tracker.delay",
"type": "java.lang.Integer",
"description": "延迟时间 (ms)",
"description": "延迟时间 (ms).",
"defaultValue": "100"
},
{
"name": "app.tracker.wait-times",
"type": "java.lang.Integer",
"description": "文件未找到等待次数",
"description": "文件未找到等待次数.",
"defaultValue": "100"
},
{
@@ -54,16 +64,24 @@
"defaultValue": "true"
},
{
"name": "app.exec-log.auto-clear",
"name": "app.auto-clear.exec-log.enabled",
"type": "java.lang.Boolean",
"description": "自动清理执行文件.",
"defaultValue": "true"
"description": "开启 批量执行日志自动清理."
},
{
"name": "app.exec-log.keep-period",
"name": "app.auto-clear.exec-log.keep-period",
"type": "java.lang.Integer",
"description": "保留周期 (天)",
"defaultValue": "30"
"description": "批量执行日志自动清理 保留周期 (天)."
},
{
"name": "app.auto-clear.host-connect-log.enabled",
"type": "java.lang.Boolean",
"description": "开启 主机连接日志自动清理."
},
{
"name": "app.auto-clear.host-connect-log.keep-period",
"type": "java.lang.Integer",
"description": "主机连接日志自动清理 保留周期 (天)."
}
]
}

View File

@@ -1,338 +0,0 @@
[
{
"name": "catppuccin-mocha",
"dark": true,
"schema": {
"background": "#1E1E2E",
"foreground": "#CDD6F4",
"cursor": "#F5E0DC",
"selectionBackground": "#585B70",
"black": "#45475A",
"red": "#F38BA8",
"green": "#A6E3A1",
"yellow": "#F9E2AF",
"blue": "#89B4FA",
"cyan": "#94E2D5",
"white": "#BAC2DE",
"brightBlack": "#585B70",
"brightRed": "#F38BA8",
"brightGreen": "#A6E3A1",
"brightYellow": "#F9E2AF",
"brightBlue": "#89B4FA",
"brightCyan": "#94E2D5",
"brightWhite": "#A6ADC8"
}
},
{
"name": "MaterialDesignColors",
"dark": true,
"schema": {
"background": "#1D262A",
"foreground": "#E7EBED",
"cursor": "#EAEAEA",
"selectionBackground": "#4E6A78",
"black": "#435B67",
"red": "#FC3841",
"green": "#5CF19E",
"yellow": "#FED032",
"blue": "#37B6FF",
"cyan": "#59FFD1",
"white": "#FFFFFF",
"brightBlack": "#A1B0B8",
"brightRed": "#FC746D",
"brightGreen": "#ADF7BE",
"brightYellow": "#FEE16C",
"brightBlue": "#70CFFF",
"brightCyan": "#9AFFE6",
"brightWhite": "#FFFFFF"
}
},
{
"name": "catppuccin-macchiato",
"dark": true,
"schema": {
"background": "#24273A",
"foreground": "#CAD3F5",
"cursor": "#F4DBD6",
"selectionBackground": "#5B6078",
"black": "#494D64",
"red": "#ED8796",
"green": "#A6DA95",
"yellow": "#EED49F",
"blue": "#8AADF4",
"cyan": "#8BD5CA",
"white": "#B8C0E0",
"brightBlack": "#5B6078",
"brightRed": "#ED8796",
"brightGreen": "#A6DA95",
"brightYellow": "#EED49F",
"brightBlue": "#8AADF4",
"brightCyan": "#8BD5CA",
"brightWhite": "#A5ADCB"
}
},
{
"name": "OneHalfDark",
"dark": true,
"schema": {
"background": "#282C34",
"foreground": "#DCDFE4",
"cursor": "#A3B3CC",
"selectionBackground": "#474E5D",
"black": "#282C34",
"red": "#E06C75",
"green": "#98C379",
"yellow": "#E5C07B",
"blue": "#61AFEF",
"cyan": "#56B6C2",
"white": "#DCDFE4",
"brightBlack": "#282C34",
"brightRed": "#E06C75",
"brightGreen": "#98C379",
"brightYellow": "#E5C07B",
"brightBlue": "#61AFEF",
"brightCyan": "#56B6C2",
"brightWhite": "#DCDFE4"
}
},
{
"name": "Dracula",
"dark": true,
"schema": {
"background": "#1E1F29",
"foreground": "#F8F8F2",
"cursor": "#BBBBBB",
"selectionBackground": "#44475A",
"black": "#000000",
"red": "#FF5555",
"green": "#50FA7B",
"yellow": "#F1FA8C",
"blue": "#BD93F9",
"cyan": "#8BE9FD",
"white": "#BBBBBB",
"brightBlack": "#555555",
"brightRed": "#FF5555",
"brightGreen": "#50FA7B",
"brightYellow": "#F1FA8C",
"brightBlue": "#BD93F9",
"brightCyan": "#8BE9FD",
"brightWhite": "#FFFFFF"
}
},
{
"name": "Atom",
"dark": true,
"schema": {
"background": "#161719",
"foreground": "#C5C8C6",
"cursor": "#D0D0D0",
"selectionBackground": "#444444",
"black": "#000000",
"red": "#FD5FF1",
"green": "#87C38A",
"yellow": "#FFD7B1",
"blue": "#85BEFD",
"cyan": "#85BEFD",
"white": "#E0E0E0",
"brightBlack": "#000000",
"brightRed": "#FD5FF1",
"brightGreen": "#94FA36",
"brightYellow": "#F5FFA8",
"brightBlue": "#96CBFE",
"brightCyan": "#85BEFD",
"brightWhite": "#E0E0E0"
}
},
{
"name": "Apple System Colors",
"dark": true,
"schema": {
"background": "#1E1E1E",
"foreground": "#FFFFFF",
"cursor": "#98989D",
"selectionBackground": "#3F638B",
"black": "#1A1A1A",
"red": "#CC372E",
"green": "#26A439",
"yellow": "#CDAC08",
"blue": "#0869CB",
"cyan": "#479EC2",
"white": "#98989D",
"brightBlack": "#464646",
"brightRed": "#FF453A",
"brightGreen": "#32D74B",
"brightYellow": "#FFD60A",
"brightBlue": "#0A84FF",
"brightCyan": "#76D6FF",
"brightWhite": "#FFFFFF"
}
},
{
"name": "Builtin Tango Light",
"dark": false,
"schema": {
"background": "#FFFFFF",
"foreground": "#000000",
"cursor": "#000000",
"selectionBackground": "#B5D5FF",
"black": "#000000",
"red": "#CC0000",
"green": "#4E9A06",
"yellow": "#C4A000",
"blue": "#3465A4",
"cyan": "#06989A",
"white": "#D3D7CF",
"brightBlack": "#555753",
"brightRed": "#EF2929",
"brightGreen": "#8AE234",
"brightYellow": "#FCE94F",
"brightBlue": "#729FCF",
"brightCyan": "#34E2E2",
"brightWhite": "#EEEEEC"
}
},
{
"name": "Duotone Dark",
"dark": true,
"schema": {
"background": "#1F1D27",
"foreground": "#B7A1FF",
"cursor": "#FF9839",
"selectionBackground": "#353147",
"black": "#1F1D27",
"red": "#D9393E",
"green": "#2DCD73",
"yellow": "#D9B76E",
"blue": "#FFC284",
"cyan": "#2488FF",
"white": "#B7A1FF",
"brightBlack": "#353147",
"brightRed": "#D9393E",
"brightGreen": "#2DCD73",
"brightYellow": "#D9B76E",
"brightBlue": "#FFC284",
"brightCyan": "#2488FF",
"brightWhite": "#EAE5FF"
}
},
{
"name": "BlulocoLight",
"dark": false,
"schema": {
"background": "#F9F9F9",
"foreground": "#373A41",
"cursor": "#F32759",
"selectionBackground": "#DAF0FF",
"black": "#373A41",
"red": "#D52753",
"green": "#23974A",
"yellow": "#DF631C",
"blue": "#275FE4",
"cyan": "#27618D",
"white": "#BABBC2",
"brightBlack": "#676A77",
"brightRed": "#FF6480",
"brightGreen": "#3CBC66",
"brightYellow": "#C5A332",
"brightBlue": "#0099E1",
"brightCyan": "#6D93BB",
"brightWhite": "#D3D3D3"
}
},
{
"name": "Chester",
"dark": true,
"schema": {
"background": "#2C3643",
"foreground": "#FFFFFF",
"cursor": "#B4B1B1",
"selectionBackground": "#67747C",
"black": "#080200",
"red": "#FA5E5B",
"green": "#16C98D",
"yellow": "#FFC83F",
"blue": "#288AD6",
"cyan": "#28DDDE",
"white": "#E7E7E7",
"brightBlack": "#6F6B68",
"brightRed": "#FA5E5B",
"brightGreen": "#16C98D",
"brightYellow": "#FEEF6D",
"brightBlue": "#278AD6",
"brightCyan": "#27DEDE",
"brightWhite": "#FFFFFF"
}
},
{
"name": "CLRS",
"dark": false,
"schema": {
"background": "#FFFFFF",
"foreground": "#262626",
"cursor": "#6FD3FC",
"selectionBackground": "#6FD3FC",
"black": "#000000",
"red": "#F8282A",
"green": "#328A5D",
"yellow": "#FA701D",
"blue": "#135CD0",
"cyan": "#33C3C1",
"white": "#B3B3B3",
"brightBlack": "#555753",
"brightRed": "#FB0416",
"brightGreen": "#2CC631",
"brightYellow": "#FDD727",
"brightBlue": "#1670FF",
"brightCyan": "#3AD5CE",
"brightWhite": "#EEEEEC"
}
},
{
"name": "Calamity",
"dark": true,
"schema": {
"background": "#2F2833",
"foreground": "#D5CED9",
"cursor": "#D5CED9",
"selectionBackground": "#7E6C88",
"black": "#2F2833",
"red": "#FC644D",
"green": "#A5F69C",
"yellow": "#E9D7A5",
"blue": "#3B79C7",
"cyan": "#74D3DE",
"white": "#D5CED9",
"brightBlack": "#7E6C88",
"brightRed": "#FC644D",
"brightGreen": "#A5F69C",
"brightYellow": "#E9D7A5",
"brightBlue": "#3B79C7",
"brightCyan": "#74D3DE",
"brightWhite": "#FFFFFF"
}
},
{
"name": "Tomorrow",
"dark": false,
"schema": {
"background": "#FFFFFF",
"foreground": "#4D4D4C",
"cursor": "#4D4D4C",
"selectionBackground": "#D6D6D6",
"black": "#000000",
"red": "#C82829",
"green": "#718C00",
"yellow": "#EAB700",
"blue": "#4271AE",
"cyan": "#3E999F",
"white": "#FFFFFF",
"brightBlack": "#000000",
"brightRed": "#C82829",
"brightGreen": "#718C00",
"brightYellow": "#EAB700",
"brightBlue": "#4271AE",
"brightCyan": "#3E999F",
"brightWhite": "#FFFFFF"
}
}
]

View File

@@ -28,9 +28,9 @@ public class TerminalThemeGenerator {
List<File> files = Files1.listFiles("D:\\idea-project\\iTerm2-Color-Schemes\\vhs");
// 过滤的 theme
List<String> schemaFilter = Lists.of(
"Dracula", "Atom",
"catppuccin-mocha", "MaterialDesignColors",
"catppuccin-macchiato", "OneHalfDark",
"Dracula", "Atom",
"Apple System Colors", "Builtin Tango Light",
"Duotone Dark", "BlulocoLight",
"Chester", "CLRS",
@@ -66,8 +66,15 @@ public class TerminalThemeGenerator {
arr.sort(Comparator.comparing(s -> schemaFilter.indexOf(s.getName())));
}
// 打印 json
String json = JSON.toJSONString(arr, colorFilter);
System.out.println("\n\n" + json);
System.out.println();
for (TerminalTheme theme : arr) {
System.out.println("name: " + theme.name);
System.out.println("dark: " + theme.dark);
System.out.println("value: \n" + JSON.toJSONString(theme.schema, colorFilter));
System.out.println();
}
// String json = JSON.toJSONString(arr, colorFilter);
// System.out.println("\n" + json);
}
/*

View File

@@ -0,0 +1,33 @@
package com.orion.visor.module.infra.api;
import com.alibaba.fastjson.JSONObject;
import java.util.List;
import java.util.Map;
/**
* 字典配置值 对外服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/7/4 18:54
*/
public interface DictValueApi {
/**
* 查询字典配置值
*
* @param key key
* @return rows
*/
List<JSONObject> getDictValue(String key);
/**
* 查询字典配置值
*
* @param keys keys
* @return rows
*/
Map<String, List<JSONObject>> getDictValueList(List<String> keys);
}

View File

@@ -0,0 +1,37 @@
package com.orion.visor.module.infra.api.impl;
import com.alibaba.fastjson.JSONObject;
import com.orion.visor.module.infra.api.DictValueApi;
import com.orion.visor.module.infra.service.DictValueService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
/**
* 字典配置值 对外服务实现类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/7/4 18:55
*/
@Slf4j
@Service
public class DictValueApiImpl implements DictValueApi {
@Resource
private DictValueService dictValueService;
@Override
public List<JSONObject> getDictValue(String key) {
return dictValueService.getDictValue(key);
}
@Override
public Map<String, List<JSONObject>> getDictValueList(List<String> keys) {
return dictValueService.getDictValueList(keys);
}
}

View File

@@ -1,5 +1,6 @@
package com.orion.visor.module.infra.controller;
import com.orion.visor.framework.log.core.annotation.IgnoreLog;
import com.orion.visor.framework.web.core.annotation.RestWrapper;
import com.orion.visor.module.infra.entity.request.preference.PreferenceUpdatePartialRequest;
import com.orion.visor.module.infra.entity.request.preference.PreferenceUpdateRequest;
@@ -56,6 +57,7 @@ public class PreferenceController {
return preferenceService.getPreferenceByType(type, items);
}
@IgnoreLog
@GetMapping("/get-default")
@Operation(summary = "查询默认偏好")
@Parameter(name = "type", description = "type", required = true)

View File

@@ -0,0 +1,4 @@
### 查询应用信息
GET {{baseUrl}}/infra/system-setting/app-info
Authorization: {{token}}

View File

@@ -0,0 +1,45 @@
package com.orion.visor.module.infra.controller;
import com.orion.visor.framework.log.core.annotation.IgnoreLog;
import com.orion.visor.framework.log.core.enums.IgnoreLogMode;
import com.orion.visor.framework.web.core.annotation.RestWrapper;
import com.orion.visor.module.infra.entity.vo.AppInfoVO;
import com.orion.visor.module.infra.service.SystemSettingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 系统服务
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023-7-17 11:39
*/
@Tag(name = "infra - 系统服务")
@Slf4j
@Validated
@RestWrapper
@RestController
@RequestMapping("/infra/system-setting")
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
public class SystemSettingController {
@Resource
private SystemSettingService systemSettingService;
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/app-info")
@Operation(summary = "查询应用信息")
public AppInfoVO getAppInfo() {
return systemSettingService.getAppInfo();
}
}

View File

@@ -25,8 +25,8 @@ public class DictValueOperatorType extends InitializingOperatorTypes {
@Override
public OperatorType[] types() {
return new OperatorType[]{
new OperatorType(L, CREATE, "创建字典配置值 <sb>${keyName}</sb>: <sb>${label}</sb> | <sb>${value}</sb>"),
new OperatorType(M, UPDATE, "更新字典配置值 <sb>${keyName}</sb>: <sb>${label}</sb> | <sb>${value}</sb>"),
new OperatorType(L, CREATE, "创建字典配置值 <sb>${keyName}</sb> - <sb>${label}</sb> | <sb>${value}</sb>"),
new OperatorType(M, UPDATE, "更新字典配置值 <sb>${keyName}</sb> - <sb>${label}</sb> | <sb>${value}</sb>"),
new OperatorType(H, DELETE, "删除字典配置值 <sb>${value}</sb>"),
};
}

View File

@@ -30,7 +30,6 @@ public class DictValueCreateRequest implements Serializable {
private Long keyId;
@NotBlank
@Size(max = 512)
@Schema(description = "配置值")
private String value;

View File

@@ -28,7 +28,6 @@ public class DictValueQueryRequest extends PageRequest {
@Schema(description = "配置项名称")
private String keyName;
@Size(max = 512)
@Schema(description = "配置值")
private String value;

View File

@@ -34,7 +34,6 @@ public class DictValueUpdateRequest implements Serializable {
private Long keyId;
@NotBlank
@Size(max = 512)
@Schema(description = "配置值")
private String value;

View File

@@ -27,7 +27,7 @@ import java.util.Map;
public class PreferenceUpdatePartialRequest implements Serializable {
@NotBlank
@Size(max = 12)
@Size(max = 16)
@Schema(description = "类型")
private String type;

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