新增机器资产管理

This commit is contained in:
bwcx_jzy 2023-02-18 23:22:37 +08:00
parent 7f90ed0927
commit 80b73ea6b0
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
54 changed files with 3292 additions and 1813 deletions

View File

@ -1,5 +1,12 @@
# 🚀 版本日志
1. 删除 node_info unLockType 字段
2. 取消节点解绑功能
3. 停止使用 NODE_STAT 表(暂时保留相关数据)
4. MACHINE_NODE_STAT_LOG 表替代 SYSTEMMONITORLOG 表(并暂时保留 SYSTEMMONITORLOG 数据)
5. 节点列表更名逻辑节点
6. 新增机器资产管理
## 2.10.17 (2023-02-16)
1. 【server】新增 构建配置新增严格执行命令模式判断命令执行状态码是否为0

View File

@ -69,4 +69,11 @@ public interface INodeInfo {
* @return proxy
*/
Proxy proxy();
/**
* 超时时间
*
* @return 超时时间 单位秒
*/
Integer timeout();
}

View File

@ -23,13 +23,16 @@
package io.jpom.controller;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import com.alibaba.fastjson2.JSONObject;
import io.jpom.common.BaseAgentController;
import io.jpom.common.JpomManifest;
import io.jpom.common.JsonMessage;
import io.jpom.common.RemoteVersion;
import io.jpom.common.commander.AbstractProjectCommander;
import io.jpom.common.commander.AbstractSystemCommander;
import io.jpom.common.interceptor.NotAuthorize;
import io.jpom.model.data.NodeProjectInfoModel;
import io.jpom.model.data.NodeScriptModel;
@ -37,12 +40,18 @@ import io.jpom.plugin.PluginFactory;
import io.jpom.service.manage.ProjectInfoService;
import io.jpom.service.script.NodeScriptServer;
import io.jpom.util.JvmUtil;
import io.jpom.util.OshiUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
/**
* 首页
@ -51,6 +60,7 @@ import java.util.List;
* @since 2019/4/17
*/
@RestController
@Slf4j
public class IndexController extends BaseAgentController {
private final ProjectInfoService projectInfoService;
@ -82,32 +92,73 @@ public class IndexController extends BaseAgentController {
}
/**
* 返回节点项目状态信息
* 获取节点统计信息
*
* @return array
* @return json
*/
@RequestMapping(value = "status", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public JsonMessage<JSONObject> status() {
@PostMapping(value = "get-stat-info", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonMessage<JSONObject> getDirectTop() {
JSONObject jsonObject = new JSONObject();
JSONObject topInfo = OshiUtils.getSimpleInfo();
jsonObject.put("simpleStatus", topInfo);
JSONObject systemInfo = OshiUtils.getSystemInfo();
jsonObject.put("systemInfo", systemInfo);
JSONObject jpomInfo = this.getJpomInfo();
jsonObject.put("jpomInfo", jpomInfo);
return JsonMessage.success("ok", jsonObject);
}
private JSONObject getJpomInfo() {
List<NodeProjectInfoModel> nodeProjectInfoModels = projectInfoService.list();
List<NodeScriptModel> list = nodeScriptServer.list();
JSONObject jsonObject = new JSONObject();
jsonObject.put("javaVirtualCount", JvmUtil.getJavaVirtualCount());
JpomManifest instance = JpomManifest.getInstance();
jsonObject.put("osName", instance.getOsName());
jsonObject.put("jpomVersion", instance.getVersion());
jsonObject.put("jpomManifest", instance);
jsonObject.put("javaVersion", SystemUtil.getJavaRuntimeInfo().getVersion());
// 获取JVM中内存总大小
long totalMemory = SystemUtil.getTotalMemory();
jsonObject.put("totalMemory", FileUtil.readableFileSize(totalMemory));
jsonObject.put("totalMemory", SystemUtil.getTotalMemory());
//
long freeMemory = SystemUtil.getFreeMemory();
jsonObject.put("freeMemory", FileUtil.readableFileSize(freeMemory));
jsonObject.put("freeMemory", SystemUtil.getFreeMemory());
//
jsonObject.put("count", CollUtil.size(nodeProjectInfoModels));
jsonObject.put("projectCount", CollUtil.size(nodeProjectInfoModels));
jsonObject.put("scriptCount", CollUtil.size(list));
// 运行时间
jsonObject.put("runTime", instance.getUpTimeStr());
jsonObject.put("runTimeLong", instance.getUpTime());
return JsonMessage.success("", jsonObject);
return jsonObject;
}
@RequestMapping(value = "processList", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public JsonMessage<List<JSONObject>> getProcessList(String processName, Integer count) {
processName = StrUtil.emptyToDefault(processName, "java");
List<JSONObject> processes = OshiUtils.getProcesses(processName, Convert.toInt(count, 20));
processes = processes.stream()
.peek(jsonObject -> {
int processId = jsonObject.getIntValue("processId");
String port = AbstractProjectCommander.getInstance().getMainPort(processId);
jsonObject.put("port", port);
//
try {
String jpomName = AbstractProjectCommander.getInstance().getJpomNameByPid(processId);
jsonObject.put("jpomName", jpomName);
} catch (IOException e) {
log.error("解析进程失败", e);
}
})
.collect(Collectors.toList());
return JsonMessage.success("ok", processes);
}
@RequestMapping(value = "kill.json", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public JsonMessage<Object> kill(int pid) {
long jpomAgentId = JpomManifest.getInstance().getPid();
Assert.state(!StrUtil.equals(StrUtil.toString(jpomAgentId), StrUtil.toString(pid)), "不支持在线关闭 Agent 进程");
String result = AbstractSystemCommander.getInstance().kill(null, pid);
if (StrUtil.isEmpty(result)) {
result = "成功kill";
}
return JsonMessage.success(result);
}
}

View File

@ -1,124 +0,0 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.controller;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import io.jpom.common.BaseAgentController;
import io.jpom.common.JpomManifest;
import io.jpom.common.JsonMessage;
import io.jpom.common.commander.AbstractProjectCommander;
import io.jpom.common.commander.AbstractSystemCommander;
import io.jpom.util.OshiUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author jiangzeyin
* @since 2019/4/16
*/
@RestController
@Slf4j
public class WelcomeController extends BaseAgentController {
@PostMapping(value = "getDirectTop", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonMessage<JSONObject> getDirectTop() {
JSONObject topInfo = OshiUtils.getSimpleInfo();
return JsonMessage.success("ok", topInfo);
}
// @RequestMapping(value = "getTop", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
// public JsonMessage<JSONObject> getTop(Long millis) {
// Iterator<CacheObj<String, JSONObject>> cacheObjIterator = TopManager.get();
// List<JSONObject> series = new ArrayList<>();
// List<String> scale = new ArrayList<>();
// int count = 60;
// int minSize = 12;
// while (cacheObjIterator.hasNext()) {
// CacheObj<String, JSONObject> cacheObj = cacheObjIterator.next();
// String key = cacheObj.getKey();
// scale.add(key);
// JSONObject value = cacheObj.getValue();
// series.add(value);
// }
// //限定数组最大数量
// if (series.size() > count) {
// series = series.subList(series.size() - count, series.size());
// scale = scale.subList(scale.size() - count, scale.size());
// }
// while (scale.size() <= minSize) {
// if (scale.size() == 0) {
// scale.add(DateUtil.formatTime(DateUtil.date()));
// }
// String time = scale.get(scale.size() - 1);
// String newTime = StringUtil.getNextScaleTime(time, millis);
// scale.add(newTime);
// }
// JSONObject object = new JSONObject();
// object.put("scales", scale);
// object.put("series", series);
// return JsonMessage.success("", object);
// }
@RequestMapping(value = "processList", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public JsonMessage<List<JSONObject>> getProcessList(String processName, Integer count) {
processName = StrUtil.emptyToDefault(processName, "java");
List<JSONObject> processes = OshiUtils.getProcesses(processName, Convert.toInt(count, 20));
processes = processes.stream().peek(jsonObject -> {
int processId = jsonObject.getIntValue("processId");
String port = AbstractProjectCommander.getInstance().getMainPort(processId);
jsonObject.put("port", port);
//
try {
String jpomName = AbstractProjectCommander.getInstance().getJpomNameByPid(processId);
jsonObject.put("jpomName", jpomName);
} catch (IOException e) {
log.error("解析进程失败", e);
}
}).collect(Collectors.toList());
return JsonMessage.success("ok", processes);
}
@RequestMapping(value = "kill.json", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public JsonMessage<Object> kill(int pid) {
long jpomAgentId = JpomManifest.getInstance().getPid();
Assert.state(!StrUtil.equals(StrUtil.toString(jpomAgentId), StrUtil.toString(pid)), "不支持在线关闭 Agent 进程");
String result = AbstractSystemCommander.getInstance().kill(null, pid);
if (StrUtil.isEmpty(result)) {
result = "成功kill";
}
return JsonMessage.success(result);
}
}

View File

@ -28,14 +28,20 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.system.oshi.CpuInfo;
import cn.hutool.system.oshi.OshiUtil;
import com.alibaba.fastjson2.JSONObject;
import oshi.hardware.GlobalMemory;
import lombok.Data;
import oshi.hardware.*;
import oshi.software.os.FileSystem;
import oshi.software.os.NetworkParams;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.GlobalConfig;
import oshi.util.Util;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author bwcx_jzy
@ -48,14 +54,58 @@ public class OshiUtils {
GlobalConfig.set(GlobalConfig.OSHI_OS_WINDOWS_CPU_UTILITY, true);
}
/**
* 获取系统信息
*
* @return json
*/
public static JSONObject getSystemInfo() {
JSONObject jsonObject = new JSONObject();
OperatingSystem os = OshiUtil.getOs();
jsonObject.put("systemUptime", os.getSystemUptime());
String manufacturer = os.getManufacturer();
String family = os.getFamily();
OperatingSystem.OSVersionInfo versionInfo = os.getVersionInfo();
String versionStr = versionInfo.toString();
jsonObject.put("osVersion", StrUtil.format("{} {} {}", manufacturer, family, versionStr));
NetworkParams networkParams = os.getNetworkParams();
String hostName = networkParams.getHostName();
jsonObject.put("hostName", hostName);
//
HardwareAbstractionLayer hardware = OshiUtil.getHardware();
ComputerSystem computerSystem = hardware.getComputerSystem();
jsonObject.put("hardwareVersion", StrUtil.format("{} {}", computerSystem.getManufacturer(), computerSystem.getModel()));
List<NetworkIF> networks = hardware.getNetworkIFs();
List<String> collect = networks.stream()
.flatMap((Function<NetworkIF, Stream<String>>) network -> Arrays.stream(network.getIPv4addr()))
.collect(Collectors.toList());
jsonObject.put("hostIpv4s", collect);
//
CentralProcessor processor = OshiUtil.getProcessor();
CentralProcessor.ProcessorIdentifier identifier = processor.getProcessorIdentifier();
jsonObject.put("osCpuIdentifierName", identifier.getName());
jsonObject.put("osCpuCores", processor.getLogicalProcessorCount());
GlobalMemory globalMemory = OshiUtil.getMemory();
jsonObject.put("osMoneyTotal", globalMemory.getTotal());
double[] systemLoadAverage = processor.getSystemLoadAverage(3);
jsonObject.put("osLoadAverage", systemLoadAverage);
FileSystem fileSystem = os.getFileSystem();
List<OSFileStore> fileStores = fileSystem.getFileStores();
long total = fileStores.stream().mapToLong(OSFileStore::getTotalSpace).sum();
jsonObject.put("osFileStoreTotal", total);
//
return jsonObject;
}
/**
* 获取信息简单的基础状态信息
*
* @return json
*/
public static JSONObject getSimpleInfo() {
CpuInfo cpuInfo = OshiUtil.getCpuInfo();
JSONObject jsonObject = new JSONObject();
jsonObject.put("time", SystemClock.now());
CpuInfo cpuInfo = OshiUtil.getCpuInfo();
jsonObject.put("cpu", cpuInfo.getUsed());
//
GlobalMemory globalMemory = OshiUtil.getMemory();
@ -69,10 +119,37 @@ public class OshiUtils {
used += (fs.getTotalSpace() - fs.getUsableSpace());
}
jsonObject.put("disk", NumberUtil.div(used, total, 2) * 100);
jsonObject.put("time", SystemClock.now());
//
NetIoInfo startNetInfo = getNetInfo();
//暂停1秒
Util.sleep(1000);
NetIoInfo endNetInfo = getNetInfo();
jsonObject.put("netTxBytes", endNetInfo.getTxbyt() - startNetInfo.getTxbyt());
jsonObject.put("netRxBytes", endNetInfo.getRxbyt() - startNetInfo.getRxbyt());
return jsonObject;
}
private static NetIoInfo getNetInfo() {
//
long rxBytesBegin = 0;
long txBytesBegin = 0;
long rxPacketsBegin = 0;
long txPacketsBegin = 0;
List<NetworkIF> listBegin = OshiUtil.getNetworkIFs();
for (NetworkIF net : listBegin) {
rxBytesBegin += net.getBytesRecv();
txBytesBegin += net.getBytesSent();
rxPacketsBegin += net.getPacketsRecv();
txPacketsBegin += net.getPacketsSent();
}
NetIoInfo netIoInfo = new NetIoInfo();
netIoInfo.setRxbyt(rxBytesBegin);
netIoInfo.setTxbyt(txBytesBegin);
netIoInfo.setRxpck(rxPacketsBegin);
netIoInfo.setTxpck(txPacketsBegin);
return netIoInfo;
}
/**
* 查询进程列表
*
@ -111,4 +188,28 @@ public class OshiUtils {
})
.collect(Collectors.toList());
}
@Data
private static class NetIoInfo {
/**
* 接收的数据包,rxpck/s
*/
private Long rxpck;
/**
* 发送的数据包,txpck/s
*/
private Long txpck;
/**
* 接收的KB数,rxkB/s
*/
private Long rxbyt;
/**
* 发送的KB数,txkB/s
*/
private Long txbyt;
}
}

View File

@ -0,0 +1,312 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package oshi;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import oshi.hardware.*;
import oshi.software.os.FileSystem;
import oshi.software.os.NetworkParams;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.FormatUtil;
import oshi.util.Util;
import java.util.Arrays;
import java.util.List;
/**
* @author bwcx_jzy
* @since 2023/2/18
*/
@Slf4j
public class SystemInfoTest {
/**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args) {
// Options: ERROR > WARN > INFO > DEBUG > TRACE
log.info("Initializing System...");
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
OperatingSystem os = si.getOperatingSystem();
System.out.println(os);
log.info("Checking computer system...");
printComputerSystem(hal.getComputerSystem());
log.info("Checking Processor...");
printProcessor(hal.getProcessor());
log.info("Checking Memory...");
printMemory(hal.getMemory());
log.info("Checking CPU...");
printCpu(hal.getProcessor());
log.info("Checking Processes...");
printProcesses(os, hal.getMemory());
log.info("Checking Sensors...");
printSensors(hal.getSensors());
log.info("Checking Power sources...");
printPowerSources(hal.getPowerSources());
log.info("Checking Disks...");
printDisks(hal.getDiskStores());
log.info("Checking File System...");
printFileSystem(os.getFileSystem());
log.info("Checking Network interfaces...");
printNetworkInterfaces(hal.getNetworkIFs());
log.info("Checking Network parameterss...");
printNetworkParameters(os.getNetworkParams());
// hardware: displays
log.info("Checking Displays...");
printDisplays(hal.getDisplays());
// hardware: USB devices
log.info("Checking USB Devices...");
printUsbDevices(hal.getUsbDevices(true));
}
private static void printComputerSystem(final ComputerSystem computerSystem) {
System.out.println("manufacturer: " + computerSystem.getManufacturer());
System.out.println("model: " + computerSystem.getModel());
System.out.println("serialnumber: " + computerSystem.getSerialNumber());
final Firmware firmware = computerSystem.getFirmware();
System.out.println("firmware:");
System.out.println(" manufacturer: " + firmware.getManufacturer());
System.out.println(" name: " + firmware.getName());
System.out.println(" description: " + firmware.getDescription());
System.out.println(" version: " + firmware.getVersion());
System.out.println(" release date: " + (firmware.getReleaseDate() == null ? "unknown"
: firmware.getReleaseDate() == null ? "unknown" : DateUtil.parse(firmware.getReleaseDate())));
final Baseboard baseboard = computerSystem.getBaseboard();
System.out.println("baseboard:");
System.out.println(" manufacturer: " + baseboard.getManufacturer());
System.out.println(" model: " + baseboard.getModel());
System.out.println(" version: " + baseboard.getVersion());
System.out.println(" serialnumber: " + baseboard.getSerialNumber());
}
private static void printProcessor(CentralProcessor processor) {
System.out.println(processor);
System.out.println(" " + processor.getPhysicalPackageCount() + " physical CPU package(s)");
System.out.println(" " + processor.getPhysicalProcessorCount() + " physical CPU core(s)");
System.out.println(" " + processor.getLogicalProcessorCount() + " logical CPU(s)");
// System.out.println("Identifier: " + processor.getIdentifier());
// System.out.println("ProcessorID: " + processor.getProcessorID());
}
private static void printMemory(GlobalMemory memory) {
// System.out.println("Memory: " + FormatUtil.formatBytes(memory.getAvailable()) + "/"
// + FormatUtil.formatBytes(memory.getTotal()));
// System.out.println("Swap used: " + FormatUtil.formatBytes(memory.getSwapUsed()) + "/"
// + FormatUtil.formatBytes(memory.getSwapTotal()));
}
private static void printCpu(CentralProcessor processor) {
// System.out.println("Uptime: " + FormatUtil.formatElapsedSecs(processor.getSystemUptime()));
System.out.println(
"Context Switches/Interrupts: " + processor.getContextSwitches() + " / " + processor.getInterrupts());
long[] prevTicks = processor.getSystemCpuLoadTicks();
System.out.println("CPU, IOWait, and IRQ ticks @ 0 sec:" + Arrays.toString(prevTicks));
// Wait a second...
Util.sleep(1000);
long[] ticks = processor.getSystemCpuLoadTicks();
System.out.println("CPU, IOWait, and IRQ ticks @ 1 sec:" + Arrays.toString(ticks));
// long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];
// long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];
// long sys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];
// long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];
// long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];
// long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];
// long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];
// long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];
// long totalCpu = user + nice + sys + idle + iowait + irq + softirq + steal;
// System.out.format(
// "User: %.1f%% Nice: %.1f%% System: %.1f%% Idle: %.1f%% IOwait: %.1f%% IRQ: %.1f%% SoftIRQ: %.1f%% Steal: %.1f%%%n",
// 100d * user / totalCpu, 100d * nice / totalCpu, 100d * sys / totalCpu, 100d * idle / totalCpu,
// 100d * iowait / totalCpu, 100d * irq / totalCpu, 100d * softirq / totalCpu, 100d * steal / totalCpu);
// System.out.format("CPU load: %.1f%% (counting ticks)%n", processor.getSystemCpuLoadBetweenTicks() * 100);
// System.out.format("CPU load: %.1f%% (OS MXBean)%n", processor.getSystemCpuLoad() * 100);
// double[] loadAverage = processor.getSystemLoadAverage(3);
// System.out.println("CPU load averages:" + (loadAverage[0] < 0 ? " N/A" : String.format(" %.2f", loadAverage[0]))
// + (loadAverage[1] < 0 ? " N/A" : String.format(" %.2f", loadAverage[1]))
// + (loadAverage[2] < 0 ? " N/A" : String.format(" %.2f", loadAverage[2])));
// // per core CPU
// StringBuilder procCpu = new StringBuilder("CPU load per processor:");
// double[] load = processor.getProcessorCpuLoadBetweenTicks();
// for (double avg : load) {
// procCpu.append(String.format(" %.1f%%", avg * 100));
// }
// System.out.println(procCpu.toString());
}
private static void printProcesses(OperatingSystem os, GlobalMemory memory) {
// System.out.println("Processes: " + os.getProcessCount() + ", Threads: " + os.getThreadCount());
// // Sort by highest CPU
// List<OSProcess> procs = Arrays.asList(os.getProcesses(5, ProcessSort.CPU));
//
// System.out.println(" PID %CPU %MEM VSZ RSS Name");
// for (int i = 0; i < procs.size() && i < 5; i++) {
// OSProcess p = procs.get(i);
// System.out.format(" %5d %5.1f %4.1f %9s %9s %s%n", p.getProcessID(),
// 100d * (p.getKernelTime() + p.getUserTime()) / p.getUpTime(),
// 100d * p.getResidentSetSize() / memory.getTotal(), FormatUtil.formatBytes(p.getVirtualSize()),
// FormatUtil.formatBytes(p.getResidentSetSize()), p.getName());
// }
}
private static void printSensors(Sensors sensors) {
System.out.println("Sensors:");
System.out.format(" CPU Temperature: %.1f°C%n", sensors.getCpuTemperature());
System.out.println(" Fan Speeds: " + Arrays.toString(sensors.getFanSpeeds()));
System.out.format(" CPU Voltage: %.1fV%n", sensors.getCpuVoltage());
}
private static void printPowerSources(List<PowerSource> powerSources) {
// StringBuilder sb = new StringBuilder("Power: ");
// if (powerSources.length == 0) {
// sb.append("Unknown");
// } else {
// double timeRemaining = powerSources[0].getTimeRemaining();
// if (timeRemaining < -1d) {
// sb.append("Charging");
// } else if (timeRemaining < 0d) {
// sb.append("Calculating time remaining");
// } else {
// sb.append(String.format("%d:%02d remaining", (int) (timeRemaining / 3600),
// (int) (timeRemaining / 60) % 60));
// }
// }
// for (PowerSource pSource : powerSources) {
// sb.append(String.format("%n %s @ %.1f%%", pSource.getName(), pSource.getRemainingCapacity() * 100d));
// }
// System.out.println(sb.toString());
}
private static void printDisks(List<HWDiskStore> diskStores) {
System.out.println("Disks:");
for (HWDiskStore disk : diskStores) {
boolean readwrite = disk.getReads() > 0 || disk.getWrites() > 0;
System.out.format(" %s: (model: %s - S/N: %s) size: %s, reads: %s (%s), writes: %s (%s), xfer: %s ms%n",
disk.getName(), disk.getModel(), disk.getSerial(),
disk.getSize() > 0 ? FormatUtil.formatBytesDecimal(disk.getSize()) : "?",
readwrite ? disk.getReads() : "?", readwrite ? FormatUtil.formatBytes(disk.getReadBytes()) : "?",
readwrite ? disk.getWrites() : "?", readwrite ? FormatUtil.formatBytes(disk.getWriteBytes()) : "?",
readwrite ? disk.getTransferTime() : "?");
List<HWPartition> partitions = disk.getPartitions();
if (partitions == null) {
// TODO Remove when all OS's implemented
continue;
}
for (HWPartition part : partitions) {
System.out.format(" |-- %s: %s (%s) Maj:Min=%d:%d, size: %s%s%n", part.getIdentification(),
part.getName(), part.getType(), part.getMajor(), part.getMinor(),
FormatUtil.formatBytesDecimal(part.getSize()),
part.getMountPoint().isEmpty() ? "" : " @ " + part.getMountPoint());
}
}
}
private static void printFileSystem(FileSystem fileSystem) {
System.out.println("File System:");
System.out.format(" File Descriptors: %d/%d%n", fileSystem.getOpenFileDescriptors(),
fileSystem.getMaxFileDescriptors());
List<OSFileStore> fsArray = fileSystem.getFileStores();
for (OSFileStore fs : fsArray) {
long usable = fs.getUsableSpace();
long total = fs.getTotalSpace();
System.out.format(
" %s (%s) [%s] %s of %s free (%.1f%%) is %s "
+ (fs.getLogicalVolume() != null && fs.getLogicalVolume().length() > 0 ? "[%s]" : "%s")
+ " and is mounted at %s%n",
fs.getName(), fs.getDescription().isEmpty() ? "file system" : fs.getDescription(), fs.getType(),
FormatUtil.formatBytes(usable), FormatUtil.formatBytes(fs.getTotalSpace()), 100d * usable / total,
fs.getVolume(), fs.getLogicalVolume(), fs.getMount());
}
}
private static void printNetworkInterfaces(List<NetworkIF> networkIFs) {
System.out.println("Network interfaces:");
for (NetworkIF net : networkIFs) {
System.out.format(" Name: %s (%s)%n", net.getName(), net.getDisplayName());
System.out.format(" MAC Address: %s %n", net.getMacaddr());
System.out.format(" MTU: %s, Speed: %s %n", net.getMTU(), FormatUtil.formatValue(net.getSpeed(), "bps"));
System.out.format(" IPv4: %s %n", Arrays.toString(net.getIPv4addr()));
System.out.format(" IPv6: %s %n", Arrays.toString(net.getIPv6addr()));
boolean hasData = net.getBytesRecv() > 0 || net.getBytesSent() > 0 || net.getPacketsRecv() > 0
|| net.getPacketsSent() > 0;
System.out.format(" Traffic: received %s/%s%s; transmitted %s/%s%s %n",
hasData ? net.getPacketsRecv() + " packets" : "?",
hasData ? FormatUtil.formatBytes(net.getBytesRecv()) : "?",
hasData ? " (" + net.getInErrors() + " err)" : "",
hasData ? net.getPacketsSent() + " packets" : "?",
hasData ? FormatUtil.formatBytes(net.getBytesSent()) : "?",
hasData ? " (" + net.getOutErrors() + " err)" : "");
}
}
private static void printNetworkParameters(NetworkParams networkParams) {
System.out.println("Network parameters:");
System.out.format(" Host name: %s%n", networkParams.getHostName());
System.out.format(" Domain name: %s%n", networkParams.getDomainName());
System.out.format(" DNS servers: %s%n", Arrays.toString(networkParams.getDnsServers()));
System.out.format(" IPv4 Gateway: %s%n", networkParams.getIpv4DefaultGateway());
System.out.format(" IPv6 Gateway: %s%n", networkParams.getIpv6DefaultGateway());
}
private static void printDisplays(List<Display> displays) {
System.out.println("Displays:");
int i = 0;
for (Display display : displays) {
System.out.println(" Display " + i + ":");
System.out.println(display.toString());
i++;
}
}
private static void printUsbDevices(List<UsbDevice> usbDevices) {
System.out.println("USB Devices:");
for (UsbDevice usbDevice : usbDevices) {
System.out.println(usbDevice.toString());
}
}
}

View File

@ -0,0 +1,42 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package oshi;
import com.alibaba.fastjson2.JSONObject;
import io.jpom.util.OshiUtils;
import org.junit.Test;
/**
* @author bwcx_jzy
* @since 2023/2/18
*/
public class TestInfo {
@Test
public void test() {
JSONObject systemInfo = OshiUtils.getSystemInfo();
//System.out.println(processor);
System.out.println(systemInfo);
}
}

View File

@ -40,6 +40,10 @@ public class Const {
* 默认的工作空间
*/
public static final String WORKSPACE_DEFAULT_ID = "DEFAULT";
/**
* 默认的分组名
*/
public static final String DEFAULT_GROUP_NAME = "默认";
// /**
// * websocket 传输 agent buffer size
// */

View File

@ -22,6 +22,7 @@
*/
package io.jpom.common;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
@ -51,14 +52,15 @@ public class ReplaceStreamFilter implements Filter {
ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
chain.doFilter(wrapper, response);
long endTime = System.currentTimeMillis();
if (endTime - startTime > 1000 * 5) {
long l = endTime - startTime;
if (l > 1000 * 5) {
byte[] contentAsByteArray = wrapper.getContentAsByteArray();
String str = StrUtil.str(contentAsByteArray, CharsetUtil.CHARSET_UTF_8);
String reqData = Opt.ofBlankAble(str)
.map(s -> wrapper.getParameterMap())
.map(JSONObject::toJSONString)
.orElse(StrUtil.EMPTY);
log.warn("[timeout] {} {}", wrapper.getRequestURI(), reqData);
log.warn("[timeout] {} {} {}", wrapper.getRequestURI(), reqData, DateUtil.formatBetween(l));
}
}
}

View File

@ -0,0 +1,92 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.common.forward;
import cn.hutool.core.lang.Opt;
import cn.hutool.extra.spring.SpringUtil;
import io.jpom.common.Const;
import io.jpom.system.ServerConfig;
import top.jpom.transport.DataContentType;
import top.jpom.transport.IUrlItem;
import java.util.Optional;
/**
* @author bwcx_jzy
* @since 2023/2/18
*/
public class DefaultUrlItem implements IUrlItem {
private final NodeUrl nodeUrl;
private final Integer timeout;
private final String workspaceId;
private final DataContentType dataContentType;
public DefaultUrlItem(NodeUrl nodeUrl, Integer timeout, String workspaceId, DataContentType dataContentType) {
this.nodeUrl = nodeUrl;
this.timeout = timeout;
this.workspaceId = workspaceId;
this.dataContentType = dataContentType;
}
@Override
public String path() {
return nodeUrl.getUrl();
}
@Override
public Integer timeout() {
if (nodeUrl.isFileTimeout()) {
ServerConfig serverConfig = SpringUtil.getBean(ServerConfig.class);
ServerConfig.NodeConfig configNode = serverConfig.getNode();
return configNode.getUploadFileTimeout();
} else {
return Optional.of(nodeUrl.getTimeout())
.flatMap(timeOut -> {
if (timeOut == 0) {
// 读取节点配置的超时时间
return Optional.ofNullable(timeout);
}
// < 0 url 指定不超时
return timeOut > 0 ? Optional.of(timeOut) : Optional.empty();
})
.map(timeOut -> {
if (timeOut <= 0) {
return null;
}
// 超时时间不能小于 2
return Math.max(timeOut, 2);
})
.orElse(null);
}
}
@Override
public String workspaceId() {
return Opt.ofBlankAble(workspaceId).orElse(Const.WORKSPACE_DEFAULT_ID);
}
@Override
public DataContentType contentType() {
return dataContentType;
}
}

View File

@ -22,17 +22,16 @@
*/
package io.jpom.common.forward;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.resource.BytesResource;
import cn.hutool.core.io.unit.DataSize;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.extra.spring.SpringUtil;
@ -41,9 +40,9 @@ import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import io.jpom.common.Const;
import io.jpom.common.JsonMessage;
import io.jpom.func.assets.model.MachineNodeModel;
import io.jpom.func.assets.server.MachineNodeServer;
import io.jpom.model.data.NodeModel;
import io.jpom.model.user.UserModel;
import io.jpom.service.node.NodeService;
import io.jpom.system.AgentException;
import io.jpom.system.AuthorizeException;
import io.jpom.system.ServerConfig;
@ -61,6 +60,8 @@ import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;
@ -68,6 +69,7 @@ import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
@ -79,72 +81,120 @@ import java.util.function.Function;
@Slf4j
public class NodeForward {
/**
* 创建代理
*
* @param type 代理类型
* @param httpProxy 代理地址
* @return proxy
*/
public static Proxy crateProxy(String type, String httpProxy) {
if (StrUtil.isNotEmpty(httpProxy)) {
List<String> split = StrUtil.splitTrim(httpProxy, StrUtil.COLON);
String host = CollUtil.getFirst(split);
int port = Convert.toInt(CollUtil.getLast(split), 0);
Proxy.Type type1 = EnumUtil.fromString(Proxy.Type.class, type, Proxy.Type.HTTP);
return new Proxy(type1, new InetSocketAddress(host, port));
}
return null;
}
public static INodeInfo parseNodeInfo(NodeModel nodeModel) {
Assert.hasText(nodeModel.getMachineId(), "节点信息不完整,缺少机器id");
MachineNodeServer machineNodeServer = SpringUtil.getBean(MachineNodeServer.class);
MachineNodeModel model = machineNodeServer.getByKey(nodeModel.getMachineId(), false);
Assert.notNull(model, "对应的机器信息不存在");
return model;
}
public static INodeInfo coverNodeInfo(MachineNodeModel machineNodeModel) {
if (StrUtil.isEmpty(machineNodeModel.getId())) {
// 新增的情况
return machineNodeModel;
}
MachineNodeServer machineNodeServer = SpringUtil.getBean(MachineNodeServer.class);
MachineNodeModel model = machineNodeServer.getByKey(machineNodeModel.getId(), false);
Optional.ofNullable(model)
.ifPresent(exits -> {
String password = Opt.ofBlankAble(machineNodeModel.getJpomPassword()).orElse(exits.getJpomPassword());
machineNodeModel.setJpomPassword(password);
});
return machineNodeModel;
}
/**
* 创建节点 url
*
* @param iNodeInfo 节点信息
* @param nodeUrl 节点功能 url
* @param dataContentType 传输的数据类型
*/
public static IUrlItem parseUrlItem(INodeInfo iNodeInfo, String workspaceId, NodeUrl nodeUrl, DataContentType dataContentType) {
//
return new DefaultUrlItem(nodeUrl, iNodeInfo.timeout(), workspaceId, dataContentType);
}
/**
* 创建节点 url
*
* @param iNodeInfo 节点信息
* @param nodeUrl 节点功能 url
*/
public static IUrlItem parseUrlItem(INodeInfo iNodeInfo, String workspaceId, NodeUrl nodeUrl) {
//
return new DefaultUrlItem(nodeUrl, iNodeInfo.timeout(), workspaceId, DataContentType.FORM_URLENCODED);
}
/**
* 创建节点 url
*
* @param nodeModel 节点信息
* @param nodeUrl 节点功能 url
* @param dataContentType 传输的数据类型
* @return item
*/
public static IUrlItem createUrlItem(NodeModel nodeModel, NodeUrl nodeUrl, DataContentType dataContentType) {
// 修正节点密码
if (StrUtil.isEmpty(nodeModel.getLoginPwd())) {
NodeService nodeService = SpringUtil.getBean(NodeService.class);
NodeModel model = nodeService.getByKey(nodeModel.getId(), false);
nodeModel.setLoginPwd(model.getLoginPwd());
nodeModel.setLoginName(model.getLoginName());
}
public static <T> T createUrlItem(NodeModel nodeModel, NodeUrl nodeUrl, DataContentType dataContentType, BiFunction<INodeInfo, IUrlItem, T> consumer) {
INodeInfo parseNodeInfo = parseNodeInfo(nodeModel);
//
return new IUrlItem() {
@Override
public String path() {
return nodeUrl.getUrl();
}
@Override
public Integer timeout() {
if (nodeUrl.isFileTimeout()) {
ServerConfig serverConfig = SpringUtil.getBean(ServerConfig.class);
ServerConfig.NodeConfig configNode = serverConfig.getNode();
return configNode.getUploadFileTimeout();
} else {
return Optional.of(nodeUrl.getTimeout())
.flatMap(timeOut -> {
if (timeOut == 0) {
// 读取节点配置的超时时间
return Optional.ofNullable(nodeModel.getTimeOut());
}
// < 0 url 指定不超时
return timeOut > 0 ? Optional.of(timeOut) : Optional.empty();
})
.map(timeOut -> {
if (timeOut <= 0) {
return null;
}
// 超时时间不能小于 2
return Math.max(timeOut, 2);
})
.orElse(null);
}
}
@Override
public String workspaceId() {
return Optional.ofNullable(nodeModel.getWorkspaceId()).orElse(Const.WORKSPACE_DEFAULT_ID);
}
@Override
public DataContentType contentType() {
return dataContentType;
}
};
IUrlItem iUrlItem = new DefaultUrlItem(nodeUrl, parseNodeInfo.timeout(), nodeModel.getWorkspaceId(), dataContentType);
return consumer.apply(parseNodeInfo, iUrlItem);
}
private static IUrlItem createUrlItem(NodeModel nodeModel, NodeUrl nodeUrl) {
return createUrlItem(nodeModel, nodeUrl, DataContentType.FORM_URLENCODED);
private static <T> T createUrlItem(NodeModel nodeModel, NodeUrl nodeUrl, BiFunction<INodeInfo, IUrlItem, T> consumer) {
return createUrlItem(nodeModel, nodeUrl, DataContentType.FORM_URLENCODED, consumer);
}
private static <T> T createUrlItem(INodeInfo nodeInfo, String workspaceId, NodeUrl nodeUrl, BiFunction<INodeInfo, IUrlItem, T> consumer) {
return createUrlItem(nodeInfo, workspaceId, nodeUrl, DataContentType.FORM_URLENCODED, consumer);
}
private static <T> T createUrlItem(INodeInfo nodeInfo, String workspaceId, NodeUrl nodeUrl, DataContentType dataContentType, BiFunction<INodeInfo, IUrlItem, T> consumer) {
//
IUrlItem iUrlItem = new DefaultUrlItem(nodeUrl, nodeInfo.timeout(), workspaceId, dataContentType);
return consumer.apply(nodeInfo, iUrlItem);
}
// /**
// * 普通消息转发
// *
// * @param nodeInfo 节点信息
// * @param request 请求
// * @param nodeUrl 节点的url
// * @param <T> 泛型
// * @return JSON
// */
// private static <T> JsonMessage<T> request(INodeInfo nodeInfo, HttpServletRequest request, NodeUrl nodeUrl, String... removeKeys) {
// Map<String, String> map = Optional.ofNullable(request)
// .map(ServletUtil::getParamMap)
// .map(map1 -> MapUtil.removeAny(map1, removeKeys))
// .orElse(null);
// TypeReference<JsonMessage<T>> tTypeReference = new TypeReference<JsonMessage<T>>() {
// };
// return createUrlItem(nodeInfo, StrUtil.EMPTY, nodeUrl,
// (nodeInfo1, urlItem) ->
// TransportServerFactory.get().executeToType(nodeInfo1, urlItem, map, tTypeReference)
// );
// }
/**
* 普通消息转发
*
@ -159,9 +209,12 @@ public class NodeForward {
.map(ServletUtil::getParamMap)
.map(map1 -> MapUtil.removeAny(map1, removeKeys))
.orElse(null);
IUrlItem urlItem = createUrlItem(nodeModel, nodeUrl);
return TransportServerFactory.get().executeToType(nodeModel, urlItem, map, new TypeReference<JsonMessage<T>>() {
});
TypeReference<JsonMessage<T>> tTypeReference = new TypeReference<JsonMessage<T>>() {
};
return createUrlItem(nodeModel, nodeUrl,
(nodeInfo, urlItem) ->
TransportServerFactory.get().executeToType(nodeInfo, urlItem, map, tTypeReference)
);
}
/**
@ -173,11 +226,40 @@ public class NodeForward {
* @return JSON
*/
public static <T> JsonMessage<T> request(NodeModel nodeModel, NodeUrl nodeUrl, JSONObject jsonObject) {
IUrlItem urlItem = createUrlItem(nodeModel, nodeUrl);
return TransportServerFactory.get().executeToType(nodeModel, urlItem, jsonObject, new TypeReference<JsonMessage<T>>() {
});
TypeReference<JsonMessage<T>> tTypeReference = new TypeReference<JsonMessage<T>>() {
};
return createUrlItem(nodeModel, nodeUrl, (nodeInfo, urlItem) -> TransportServerFactory.get().executeToType(nodeInfo, urlItem, jsonObject, tTypeReference));
}
/**
* 普通消息转发
*
* @param machineNodeModel 节点
* @param nodeUrl 节点的url
* @param jsonObject 数据
* @return JSON
*/
public static <T> JsonMessage<T> request(MachineNodeModel machineNodeModel, NodeUrl nodeUrl, JSONObject jsonObject) {
TypeReference<JsonMessage<T>> typeReference = new TypeReference<JsonMessage<T>>() {
};
INodeInfo nodeInfo = coverNodeInfo(machineNodeModel);
return createUrlItem(nodeInfo, StrUtil.EMPTY, nodeUrl, (nodeInfo1, urlItem) -> TransportServerFactory.get().executeToType(nodeInfo1, urlItem, jsonObject, typeReference));
}
// /**
// * 普通消息转发
// *
// * @param nodeInfo 节点
// * @param nodeUrl 节点的url
// * @param jsonObject 数据
// * @return JSON
// */
// private static <T> JsonMessage<T> request(INodeInfo nodeInfo, NodeUrl nodeUrl, JSONObject jsonObject) {
// TypeReference<JsonMessage<T>> typeReference = new TypeReference<JsonMessage<T>>() {
// };
// return createUrlItem(nodeInfo, StrUtil.EMPTY, nodeUrl, (nodeInfo1, urlItem) -> TransportServerFactory.get().executeToType(nodeInfo1, urlItem, jsonObject, typeReference));
// }
/**
* 普通消息转发
*
@ -187,7 +269,35 @@ public class NodeForward {
* @return JSON
*/
public static <T> JsonMessage<T> requestSharding(NodeModel nodeModel, NodeUrl nodeUrl, JSONObject jsonObject, File file, Function<JSONObject, JsonMessage<T>> doneCallback, BiConsumer<Long, Long> streamProgress) throws IOException {
IUrlItem urlItem = createUrlItem(nodeModel, nodeUrl);
INodeInfo nodeInfo = parseNodeInfo(nodeModel);
return requestSharding(nodeInfo, nodeModel.getWorkspaceId(), nodeUrl, jsonObject, file, doneCallback, streamProgress);
}
/**
* 普通消息转发
*
* @param machineNodeModel 节点
* @param nodeUrl 节点的url
* @param jsonObject 数据
* @return JSON
*/
public static <T> JsonMessage<T> requestSharding(MachineNodeModel machineNodeModel, NodeUrl nodeUrl, JSONObject jsonObject, File file, Function<JSONObject, JsonMessage<T>> doneCallback, BiConsumer<Long, Long> streamProgress) throws IOException {
INodeInfo nodeInfo = coverNodeInfo(machineNodeModel);
return requestSharding(nodeInfo, StrUtil.EMPTY, nodeUrl, jsonObject, file, doneCallback, streamProgress);
}
/**
* 普通消息转发
*
* @param nodeInfo 节点
* @param workspaceId 工作空间id
* @param streamProgress 进度回调
* @param nodeUrl 节点的url
* @param jsonObject 数据
* @return JSON
*/
private static <T> JsonMessage<T> requestSharding(INodeInfo nodeInfo, String workspaceId, NodeUrl nodeUrl, JSONObject jsonObject, File file, Function<JSONObject, JsonMessage<T>> doneCallback, BiConsumer<Long, Long> streamProgress) throws IOException {
IUrlItem urlItem = parseUrlItem(nodeInfo, workspaceId, nodeUrl, DataContentType.FORM_URLENCODED);
ServerConfig serverConfig = SpringUtil.getBean(ServerConfig.class);
ServerConfig.NodeConfig nodeConfig = serverConfig.getNode();
long length = file.length();
@ -242,7 +352,7 @@ public class NodeForward {
}
}
// 上传
JsonMessage<T> message = transportServer.executeToType(nodeModel, urlItem, uploadData, typeReference);
JsonMessage<T> message = transportServer.executeToType(nodeInfo, urlItem, uploadData, typeReference);
if (message.success()) {
// 使用成功的个数计算
success.add(currentChunk);
@ -292,13 +402,49 @@ public class NodeForward {
* @return JSON
*/
public static <T> JsonMessage<T> request(NodeModel nodeModel, NodeUrl nodeUrl, String pName, Object pVal, Object... parameters) {
INodeInfo parseNodeInfo = parseNodeInfo(nodeModel);
return request(parseNodeInfo, nodeModel.getWorkspaceId(), nodeUrl, pName, pVal, parameters);
}
/**
* 普通消息转发
*
* @param machineNodeModel 节点
* @param workspaceId 工作空间id
* @param nodeUrl 节点的url
* @param pName 主参数名
* @param pVal 主参数值
* @param parameters 其他参数
* @return JSON
*/
public static <T> JsonMessage<T> request(MachineNodeModel machineNodeModel, String workspaceId, NodeUrl nodeUrl, String pName, Object pVal, Object... parameters) {
Map<String, Object> parametersMap = MapUtil.of(pName, pVal);
for (int i = 0; i < parameters.length; i += 2) {
parametersMap.put(parameters[i].toString(), parameters[i + 1]);
}
IUrlItem urlItem = createUrlItem(nodeModel, nodeUrl);
INodeInfo nodeInfo = coverNodeInfo(machineNodeModel);
return request(nodeInfo, workspaceId, nodeUrl, pName, pVal, parameters);
}
return TransportServerFactory.get().executeToType(nodeModel, urlItem, parametersMap, new TypeReference<JsonMessage<T>>() {
/**
* 普通消息转发
*
* @param nodeInfo 节点
* @param workspaceId 工作空间id
* @param nodeUrl 节点的url
* @param pName 主参数名
* @param pVal 主参数值
* @param parameters 其他参数
* @return JSON
*/
private static <T> JsonMessage<T> request(INodeInfo nodeInfo, String workspaceId, NodeUrl nodeUrl, String pName, Object pVal, Object... parameters) {
Map<String, Object> parametersMap = MapUtil.of(pName, pVal);
for (int i = 0; i < parameters.length; i += 2) {
parametersMap.put(parameters[i].toString(), parameters[i + 1]);
}
IUrlItem iUrlItem = parseUrlItem(nodeInfo, workspaceId, nodeUrl);
return TransportServerFactory.get().executeToType(nodeInfo, iUrlItem, parametersMap, new TypeReference<JsonMessage<T>>() {
});
}
@ -312,10 +458,11 @@ public class NodeForward {
* @return JSON
*/
public static <T> JsonMessage<T> requestBody(NodeModel nodeModel, NodeUrl nodeUrl, JSONObject jsonData) {
TypeReference<JsonMessage<T>> tTypeReference = new TypeReference<JsonMessage<T>>() {
};
return createUrlItem(nodeModel, nodeUrl, DataContentType.JSON,
(nodeInfo, urlItem) -> TransportServerFactory.get().executeToType(nodeInfo, urlItem, jsonData, tTypeReference));
IUrlItem urlItem = createUrlItem(nodeModel, nodeUrl, DataContentType.JSON);
return TransportServerFactory.get().executeToType(nodeModel, urlItem, jsonData, new TypeReference<JsonMessage<T>>() {
});
}
/**
@ -357,10 +504,8 @@ public class NodeForward {
*/
public static <T> T requestData(NodeModel nodeModel, NodeUrl nodeUrl, HttpServletRequest request, Class<T> tClass) {
Map<String, String> map = Optional.ofNullable(request).map(ServletUtil::getParamMap).orElse(null);
IUrlItem urlItem = createUrlItem(nodeModel, nodeUrl);
return TransportServerFactory.get().executeToTypeOnlyData(nodeModel, urlItem, map, tClass);
// JsonMessage<T> jsonMessage = request(nodeModel, request, nodeUrl);
// return jsonMessage.getData(tClass);
return createUrlItem(nodeModel, nodeUrl, (nodeInfo, urlItem) -> TransportServerFactory.get().executeToTypeOnlyData(nodeInfo, urlItem, map, tClass));
}
@ -374,7 +519,7 @@ public class NodeForward {
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static JsonMessage<String> requestMultipart(NodeModel nodeModel, MultipartHttpServletRequest request, NodeUrl nodeUrl) {
IUrlItem urlItem = createUrlItem(nodeModel, nodeUrl);
//
Map params = ServletUtil.getParamMap(request);
//
@ -387,8 +532,11 @@ public class NodeForward {
throw Lombok.sneakyThrow(e);
}
});
return TransportServerFactory.get().executeToType(nodeModel, urlItem, params, new TypeReference<JsonMessage<String>>() {
});
TypeReference<JsonMessage<String>> tTypeReference = new TypeReference<JsonMessage<String>>() {
};
return createUrlItem(nodeModel, nodeUrl,
(nodeInfo, urlItem) -> TransportServerFactory.get().executeToType(nodeInfo, urlItem, params, tTypeReference));
}
/**
@ -400,57 +548,55 @@ public class NodeForward {
* @param nodeUrl 节点的url
*/
public static void requestDownload(NodeModel nodeModel, HttpServletRequest request, HttpServletResponse response, NodeUrl nodeUrl) {
IUrlItem urlItem = createUrlItem(nodeModel, nodeUrl);
//
Map<String, String> params = ServletUtil.getParamMap(request);
TransportServerFactory.get().download(nodeModel, urlItem, params, downloadCallback -> {
Opt.ofBlankAble(downloadCallback.getContentDisposition())
.ifPresent(s -> response.setHeader(HttpHeaders.CONTENT_DISPOSITION, s));
response.setContentType(downloadCallback.getContentType());
ServletUtil.write(response, downloadCallback.getInputStream());
createUrlItem(nodeModel, nodeUrl, (nodeInfo, urlItem) -> {
TransportServerFactory.get().download(nodeInfo, urlItem, params, downloadCallback -> {
Opt.ofBlankAble(downloadCallback.getContentDisposition())
.ifPresent(s -> response.setHeader(HttpHeaders.CONTENT_DISPOSITION, s));
response.setContentType(downloadCallback.getContentType());
ServletUtil.write(response, downloadCallback.getInputStream());
});
return null;
});
}
/**
* 获取节点socket 信息
*
* @param nodeModel 节点信息
* @param nodeUrl url
* @return url
*/
public static String getSocketUrl(NodeModel nodeModel, NodeUrl nodeUrl, UserModel userInfo, Object... parameters) {
String ws;
if ("https".equalsIgnoreCase(nodeModel.getProtocol())) {
ws = "wss";
} else {
ws = "ws";
}
if (StrUtil.isEmpty(nodeModel.getLoginPwd())) {
NodeService nodeService = SpringUtil.getBean(NodeService.class);
NodeModel model = nodeService.getByKey(nodeModel.getId(), false);
nodeModel.setLoginPwd(model.getLoginPwd());
nodeModel.setLoginName(model.getLoginName());
}
UrlQuery urlQuery = new UrlQuery();
urlQuery.add(Const.JPOM_AGENT_AUTHORIZE, nodeModel.toAuthorize());
//
String optUser = userInfo.getId();
optUser = URLUtil.encode(optUser);
urlQuery.add("optUser", optUser);
if (ArrayUtil.isNotEmpty(parameters)) {
for (int i = 0; i < parameters.length; i += 2) {
Object parameter = parameters[i + 1];
String value = Convert.toStr(parameter, StrUtil.EMPTY);
urlQuery.add(parameters[i].toString(), URLUtil.encode(value));
}
}
// 兼容旧版本-节点升级 @author jzy
//urlQuery.add("name", URLUtil.encode(nodeModel.getLoginName()));
//urlQuery.add("password", URLUtil.encode(nodeModel.getLoginPwd()));
String format = StrUtil.format("{}://{}{}?{}", ws, nodeModel.getUrl(), nodeUrl.getUrl(), urlQuery.toString());
log.debug("web socket url:{}", format);
return format;
}
// /**
// * 获取节点socket 信息
// *
// * @param nodeModel 节点信息
// * @param nodeUrl url
// * @return url
// */
// public static String getSocketUrl(NodeModel nodeModel, NodeUrl nodeUrl, UserModel userInfo, Object... parameters) {
// INodeInfo nodeInfo = parseNodeInfo(nodeModel);
// String ws;
// if ("https".equalsIgnoreCase(nodeInfo.scheme())) {
// ws = "wss";
// } else {
// ws = "ws";
// }
// UrlQuery urlQuery = new UrlQuery();
// urlQuery.add(Const.JPOM_AGENT_AUTHORIZE, nodeInfo.authorize());
// //
// String optUser = userInfo.getId();
// optUser = URLUtil.encode(optUser);
// urlQuery.add("optUser", optUser);
// if (ArrayUtil.isNotEmpty(parameters)) {
// for (int i = 0; i < parameters.length; i += 2) {
// Object parameter = parameters[i + 1];
// String value = Convert.toStr(parameter, StrUtil.EMPTY);
// urlQuery.add(parameters[i].toString(), URLUtil.encode(value));
// }
// }
// // 兼容旧版本-节点升级 @author jzy
// //urlQuery.add("name", URLUtil.encode(nodeModel.getLoginName()));
// //urlQuery.add("password", URLUtil.encode(nodeModel.getLoginPwd()));
// String format = StrUtil.format("{}://{}{}?{}", ws, nodeInfo.url(), nodeUrl.getUrl(), urlQuery.toString());
// log.debug("web socket url:{}", format);
// return format;
// }
public static <T> T toJsonMessage(String body, TypeReference<T> tTypeReference) {
if (StrUtil.isEmpty(body)) {

View File

@ -40,8 +40,7 @@ public enum NodeUrl {
*
*/
// GetTop("/getTop"),
GetDirectTop("/getDirectTop"),
Status("/status"),
GetStatInfo("/get-stat-info"),
exportTop("/exportTop"),
Kill("/kill.json"),

View File

@ -23,14 +23,11 @@
package io.jpom.controller.node;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import io.jpom.common.BaseServerController;
import io.jpom.common.JsonMessage;
import io.jpom.common.ServerConst;
import io.jpom.common.forward.NodeForward;
import io.jpom.common.forward.NodeUrl;
import io.jpom.common.validator.ValidatorItem;
import io.jpom.func.assets.server.MachineNodeServer;
import io.jpom.model.data.NodeModel;
import io.jpom.model.node.ProjectInfoCacheModel;
import io.jpom.model.node.ScriptCacheModel;
@ -45,10 +42,12 @@ import io.jpom.service.node.script.NodeScriptExecuteLogServer;
import io.jpom.service.node.script.NodeScriptServer;
import io.jpom.service.outgiving.LogReadServer;
import io.jpom.service.outgiving.OutGivingServer;
import io.jpom.service.stat.NodeStatService;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.jpom.model.PageResultDto;
import javax.servlet.http.HttpServletRequest;
@ -71,8 +70,8 @@ public class NodeEditController extends BaseServerController {
private final LogReadServer logReadServer;
private final ProjectInfoCacheService projectInfoCacheService;
private final NodeScriptServer nodeScriptServer;
private final NodeStatService nodeStatService;
private final NodeScriptExecuteLogServer nodeScriptExecuteLogServer;
private final MachineNodeServer machineNodeServer;
public NodeEditController(OutGivingServer outGivingServer,
MonitorService monitorService,
@ -80,23 +79,24 @@ public class NodeEditController extends BaseServerController {
LogReadServer logReadServer,
ProjectInfoCacheService projectInfoCacheService,
NodeScriptServer nodeScriptServer,
NodeStatService nodeStatService,
NodeScriptExecuteLogServer nodeScriptExecuteLogServer) {
NodeScriptExecuteLogServer nodeScriptExecuteLogServer,
MachineNodeServer machineNodeServer) {
this.outGivingServer = outGivingServer;
this.monitorService = monitorService;
this.buildService = buildService;
this.logReadServer = logReadServer;
this.projectInfoCacheService = projectInfoCacheService;
this.nodeScriptServer = nodeScriptServer;
this.nodeStatService = nodeStatService;
this.nodeScriptExecuteLogServer = nodeScriptExecuteLogServer;
this.machineNodeServer = machineNodeServer;
}
@PostMapping(value = "list_data.json", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public JsonMessage<PageResultDto<NodeModel>> listJson() {
PageResultDto<NodeModel> nodeModelPageResultDto = nodeService.listPage(getRequest());
public JsonMessage<PageResultDto<NodeModel>> listJson(HttpServletRequest request) {
PageResultDto<NodeModel> nodeModelPageResultDto = nodeService.listPage(request);
nodeModelPageResultDto.each(nodeModel -> nodeModel.setMachineNodeData(machineNodeServer.getByKey(nodeModel.getMachineId())));
return JsonMessage.success("", nodeModelPageResultDto);
}
@ -131,24 +131,10 @@ public class NodeEditController extends BaseServerController {
return JsonMessage.success("", listGroup);
}
@RequestMapping(value = "node_status", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public JsonMessage<JSONArray> nodeStatus() {
long timeMillis = System.currentTimeMillis();
NodeModel node = getNode();
JSONObject jsonObject = NodeForward.requestData(node, NodeUrl.Status, getRequest(), JSONObject.class);
Assert.notNull(jsonObject, "获取信息失败");
JSONArray jsonArray = new JSONArray();
jsonObject.put("timeOut", System.currentTimeMillis() - timeMillis);
jsonObject.put("nodeId", node.getId());
jsonArray.add(jsonObject);
return JsonMessage.success("", jsonArray);
}
@PostMapping(value = "save.json", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public JsonMessage<String> save() {
nodeService.update(getRequest());
public JsonMessage<String> save(HttpServletRequest request) {
nodeService.update(request);
return JsonMessage.success("操作成功");
}
@ -202,8 +188,6 @@ public class NodeEditController extends BaseServerController {
//
int i = nodeService.delByKey(id, request);
if (i > 0) {
// 删除节点统计数据
nodeStatService.delByKey(id);
//
nodeScriptExecuteLogServer.delCache(id, request);
}
@ -227,21 +211,6 @@ public class NodeEditController extends BaseServerController {
return JsonMessage.success("操作成功");
}
/**
* 解锁节点通过插件端自动注册的节点默认未分配工作空间
*
* @param id 节点ID
* @param toWorkspaceId 分配到到工作空间ID
* @return msg
*/
@GetMapping(value = "un_lock_workspace", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
@SystemPermission()
public JsonMessage<String> unLockWorkspace(@ValidatorItem String id, @ValidatorItem String toWorkspaceId) {
nodeService.checkUserWorkspace(toWorkspaceId);
nodeService.unLock(id, toWorkspaceId);
return JsonMessage.success("操作成功");
}
/**
* 同步到指定工作空间

View File

@ -20,75 +20,97 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.controller.node;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.db.Entity;
import com.alibaba.fastjson2.JSONObject;
import io.jpom.common.BaseServerController;
import io.jpom.common.JsonMessage;
import io.jpom.model.stat.NodeStatModel;
import io.jpom.permission.ClassFeature;
import io.jpom.permission.Feature;
import io.jpom.permission.MethodFeature;
import io.jpom.service.stat.NodeStatService;
import io.jpom.system.ServerConfig;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.jpom.model.PageResultDto;
import java.util.List;
import java.util.Map;
/**
* @author bwcx_jzy
* @since 2022/1/22
*/
@RestController
@RequestMapping(value = "/node/stat")
@Feature(cls = ClassFeature.NODE_STAT)
public class NodeStatController extends BaseServerController {
private final NodeStatService nodeStatService;
private final ServerConfig.NodeConfig nodeConfig;
public NodeStatController(NodeStatService nodeStatService,
ServerConfig serverConfig) {
this.nodeStatService = nodeStatService;
this.nodeConfig = serverConfig.getNode();
}
@PostMapping(value = "list_data.json", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public JsonMessage<PageResultDto<NodeStatModel>> listJson() {
PageResultDto<NodeStatModel> nodeModelPageResultDto = nodeStatService.listPage(getRequest());
return JsonMessage.success("", nodeModelPageResultDto);
}
@GetMapping(value = "status_stat.json", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public JsonMessage<JSONObject> statusStat() {
String workspaceId = nodeStatService.getCheckUserWorkspace(getRequest());
//
JSONObject jsonObject = new JSONObject();
jsonObject.put("heartSecond", nodeConfig.getHeartSecond());
{
// 节点状态
String sql = "select `status`,count(1) as cunt from " + nodeStatService.getTableName() + " where workspaceId=? group by `status`";
List<Entity> list = nodeStatService.query(sql, workspaceId);
Map<String, Integer> map = CollStreamUtil.toMap(list, entity -> entity.getStr("status"), entity -> entity.getInt("cunt"));
jsonObject.put("status", map);
}
// {
// // 启用状态
// String sql = "select `openStatus`,count(1) as cunt from " + nodeService.getTableName() + " where workspaceId=? group by `openStatus`";
// List<Entity> list = nodeStatService.query(sql, workspaceId);
// Map<String, Integer> map = CollStreamUtil.toMap(list, entity -> entity.getStr("openStatus"), entity -> entity.getInt("cunt"));
// jsonObject.put("openStatus", map);
// }
return JsonMessage.success("", jsonObject);
}
}
///*
// * The MIT License (MIT)
// *
// * Copyright (c) 2019 Code Technology Studio
// *
// * Permission is hereby granted, free of charge, to any person obtaining a copy of
// * this software and associated documentation files (the "Software"), to deal in
// * the Software without restriction, including without limitation the rights to
// * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// * the Software, and to permit persons to whom the Software is furnished to do so,
// * subject to the following conditions:
// *
// * The above copyright notice and this permission notice shall be included in all
// * copies or substantial portions of the Software.
// *
// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// */
//package io.jpom.controller.node;
//
//import cn.hutool.core.collection.CollStreamUtil;
//import cn.hutool.db.Entity;
//import com.alibaba.fastjson2.JSONObject;
//import io.jpom.common.BaseServerController;
//import io.jpom.common.JsonMessage;
//import io.jpom.model.stat.NodeStatModel;
//import io.jpom.permission.ClassFeature;
//import io.jpom.permission.Feature;
//import io.jpom.permission.MethodFeature;
//import io.jpom.service.stat.NodeStatService;
//import io.jpom.system.ServerConfig;
//import org.springframework.http.MediaType;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.PostMapping;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//import top.jpom.model.PageResultDto;
//
//import java.util.List;
//import java.util.Map;
//
///**
// * @author bwcx_jzy
// * @since 2022/1/22
// */
//@RestController
//@RequestMapping(value = "/node/stat")
//@Feature(cls = ClassFeature.NODE_STAT)
//public class NodeStatController extends BaseServerController {
//
// private final NodeStatService nodeStatService;
// private final ServerConfig.NodeConfig nodeConfig;
//
// public NodeStatController(NodeStatService nodeStatService,
// ServerConfig serverConfig) {
// this.nodeStatService = nodeStatService;
// this.nodeConfig = serverConfig.getNode();
// }
//
// @PostMapping(value = "list_data.json", produces = MediaType.APPLICATION_JSON_VALUE)
// @Feature(method = MethodFeature.LIST)
// public JsonMessage<PageResultDto<NodeStatModel>> listJson() {
// PageResultDto<NodeStatModel> nodeModelPageResultDto = nodeStatService.listPage(getRequest());
// return JsonMessage.success("", nodeModelPageResultDto);
// }
//
// @GetMapping(value = "status_stat.json", produces = MediaType.APPLICATION_JSON_VALUE)
// @Feature(method = MethodFeature.LIST)
// public JsonMessage<JSONObject> statusStat() {
// String workspaceId = nodeStatService.getCheckUserWorkspace(getRequest());
// //
// JSONObject jsonObject = new JSONObject();
// jsonObject.put("heartSecond", nodeConfig.getHeartSecond());
// {
// // 节点状态
// String sql = "select `status`,count(1) as cunt from " + nodeStatService.getTableName() + " where workspaceId=? group by `status`";
// List<Entity> list = nodeStatService.query(sql, workspaceId);
// Map<String, Integer> map = CollStreamUtil.toMap(list, entity -> entity.getStr("status"), entity -> entity.getInt("cunt"));
// jsonObject.put("status", map);
// }
//// {
//// // 启用状态
//// String sql = "select `openStatus`,count(1) as cunt from " + nodeService.getTableName() + " where workspaceId=? group by `openStatus`";
//// List<Entity> list = nodeStatService.query(sql, workspaceId);
//// Map<String, Integer> map = CollStreamUtil.toMap(list, entity -> entity.getStr("openStatus"), entity -> entity.getInt("cunt"));
//// jsonObject.put("openStatus", map);
//// }
// return JsonMessage.success("", jsonObject);
// }
//}

View File

@ -32,8 +32,9 @@ import com.alibaba.fastjson2.JSONObject;
import io.jpom.common.*;
import io.jpom.common.validator.ValidatorItem;
import io.jpom.controller.openapi.NodeInfoController;
import io.jpom.func.assets.model.MachineNodeModel;
import io.jpom.func.assets.server.MachineNodeServer;
import io.jpom.model.AgentFileModel;
import io.jpom.model.data.NodeModel;
import io.jpom.permission.ClassFeature;
import io.jpom.permission.Feature;
import io.jpom.permission.MethodFeature;
@ -71,11 +72,14 @@ public class NodeUpdateController extends BaseServerController {
private final SystemParametersServer systemParametersServer;
private final ServerConfig serverConfig;
private final MachineNodeServer machineNodeServer;
public NodeUpdateController(SystemParametersServer systemParametersServer,
ServerConfig serverConfig) {
ServerConfig serverConfig,
MachineNodeServer machineNodeServer) {
this.systemParametersServer = systemParametersServer;
this.serverConfig = serverConfig;
this.machineNodeServer = machineNodeServer;
}
/**
@ -220,25 +224,26 @@ public class NodeUpdateController extends BaseServerController {
Assert.notNull(receiveCache, "没有对应的缓存信息");
JSONArray jsonArray = receiveCache.getJSONArray("canUseNode");
Assert.notEmpty(jsonArray, "没有对应的缓存信息:-1");
Optional<NodeModel> any = jsonArray.stream().map(o -> {
if (o instanceof NodeModel) {
return (NodeModel) o;
Optional<MachineNodeModel> any = jsonArray.stream().map(o -> {
if (o instanceof MachineNodeModel) {
return (MachineNodeModel) o;
}
JSONObject jsonObject = (JSONObject) o;
return jsonObject.toJavaObject(NodeModel.class);
}).filter(nodeModel -> StrUtil.equals(nodeModel.getUrl(), StrUtil.format("{}:{}", ip, port))).findAny();
return jsonObject.toJavaObject(MachineNodeModel.class);
}).filter(nodeModel -> StrUtil.equals(nodeModel.getJpomUrl(), StrUtil.format("{}:{}", ip, port))).findAny();
Assert.state(any.isPresent(), "ip 地址信息不正确");
NodeModel nodeModel = any.get();
MachineNodeModel machineNodeModel = any.get();
try {
nodeService.testNode(nodeModel);
machineNodeServer.testNode(machineNodeModel);
} catch (Exception e) {
log.warn("测试结果:{} {}", nodeModel.getUrl(), e.getMessage());
log.warn("测试结果:{} {}", machineNodeModel.getJpomUrl(), e.getMessage());
return new JsonMessage<>(500, "节点连接失败:" + e.getMessage());
}
// 插入
boolean exists = nodeService.existsByUrl(nodeModel.getUrl(), nodeModel.getWorkspaceId(), null);
Assert.state(!exists, "对应的节点已经存在拉:" + nodeModel.getUrl());
nodeService.insert(nodeModel);
boolean exists = machineNodeServer.existsByUrl(machineNodeModel.getJpomUrl(), null);
Assert.state(!exists, "对应的节点已经存在拉:" + machineNodeModel.getJpomUrl());
String workspaceId = nodeService.getCheckUserWorkspace(getRequest());
machineNodeServer.insertAndNode(machineNodeModel, workspaceId);
// 更新结果
receiveCache.put("type", "success");
return JsonMessage.success("安装成功", NodeInfoController.listReceiveCache(null));

View File

@ -33,10 +33,10 @@ import io.jpom.common.BaseServerController;
import io.jpom.common.JsonMessage;
import io.jpom.common.forward.NodeForward;
import io.jpom.common.forward.NodeUrl;
import io.jpom.func.assets.model.MachineNodeStatLogModel;
import io.jpom.func.assets.server.MachineNodeStatLogServer;
import io.jpom.model.data.NodeModel;
import io.jpom.model.log.SystemMonitorLog;
import io.jpom.permission.SystemPermission;
import io.jpom.service.dblog.DbSystemMonitorLogService;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.PostMapping;
@ -47,35 +47,36 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 欢迎页
* 节点统计信息
*
* @author Administrator
* @author bwcx_jzy
*/
@RestController
@RequestMapping(value = "/node")
public class NodeWelcomeController extends BaseServerController {
private final DbSystemMonitorLogService dbSystemMonitorLogService;
private final MachineNodeStatLogServer machineNodeStatLogServer;
public NodeWelcomeController(DbSystemMonitorLogService dbSystemMonitorLogService) {
this.dbSystemMonitorLogService = dbSystemMonitorLogService;
public NodeWelcomeController(MachineNodeStatLogServer machineNodeStatLogServer) {
this.machineNodeStatLogServer = machineNodeStatLogServer;
}
@PostMapping(value = "node_monitor_data.json", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonMessage<List<SystemMonitorLog>> nodeMonitorJson() {
List<SystemMonitorLog> list = this.getList();
public JsonMessage<List<MachineNodeStatLogModel>> nodeMonitorJson() {
List<MachineNodeStatLogModel> list = this.getList();
Assert.notEmpty(list, "没有查询到任何数据");
return JsonMessage.success("ok", list);
}
private List<SystemMonitorLog> getList() {
private List<MachineNodeStatLogModel> getList() {
NodeModel node = getNode();
String machineId = node.getMachineId();
String startDateStr = getParameter("time[0]");
String endDateStr = getParameter("time[1]");
if (StrUtil.hasEmpty(startDateStr, endDateStr)) {
SystemMonitorLog systemMonitorLog = new SystemMonitorLog();
systemMonitorLog.setNodeId(node.getId());
return dbSystemMonitorLogService.queryList(systemMonitorLog, 500, new Order("monitorTime", Direction.DESC));
MachineNodeStatLogModel systemMonitorLog = new MachineNodeStatLogModel();
systemMonitorLog.setMachineId(machineId);
return machineNodeStatLogServer.queryList(systemMonitorLog, 500, new Order("monitorTime", Direction.DESC));
}
// 处理时间
DateTime startDate = DateUtil.parse(startDateStr);
@ -86,69 +87,16 @@ public class NodeWelcomeController extends BaseServerController {
endDate = DateUtil.endOfDay(endDate);
}
long endTime = endDate.getTime();
// 开启了节点信息采集
Page pageObj = new Page(1, 5000);
pageObj.addOrder(new Order("monitorTime", Direction.DESC));
Entity entity = Entity.create();
entity.set("nodeId", node.getId());
entity.set("machineId", machineId);
entity.set(" MONITORTIME", ">= " + startTime);
entity.set("MONITORTIME", "<= " + endTime);
return dbSystemMonitorLogService.listPageOnlyResult(entity, pageObj);
return machineNodeStatLogServer.listPageOnlyResult(entity, pageObj);
}
// private JSONObject getData() {
// List<SystemMonitorLog> list = getList();
// Assert.notEmpty(list, "没有查询到任何数据");
// List<JSONObject> series = new ArrayList<>();
// List<String> scale = new ArrayList<>();
// for (int i = list.size() - 1; i >= 0; i--) {
// SystemMonitorLog systemMonitorLog = list.get(i);
// scale.add(new DateTime(systemMonitorLog.getMonitorTime()).toString(DatePattern.NORM_DATETIME_PATTERN));
// JSONObject jsonObject = new JSONObject();
// jsonObject.put("cpu", systemMonitorLog.getOccupyCpu());
// jsonObject.put("memory", systemMonitorLog.getOccupyMemory());
// jsonObject.put("memoryUsed", systemMonitorLog.getOccupyMemoryUsed());
// jsonObject.put("disk", systemMonitorLog.getOccupyDisk());
// series.add(jsonObject);
// }
//
// JSONObject object = new JSONObject();
// object.put("scales", scale);
// object.put("series", series);
// return object;
// }
//
// @PostMapping(value = "getTop", produces = MediaType.APPLICATION_JSON_VALUE)
// public String getTop() {
// JSONObject object = getData();
// return JsonMessage.success( "ok", object);
// }
// @RequestMapping(value = "exportTop")
// public void exportTop(String time) throws UnsupportedEncodingException {
// List<SystemMonitorLog> result = getList();
// if (CollUtil.isEmpty(result)) {
// // NodeForward.requestDownload(node, getRequest(), getResponse(), NodeUrl.exportTop);
// } else {
// NodeModel node = getNode();
// StringBuilder buf = new StringBuilder();
// buf.append("监控时间").append(",占用cpu").append(",占用内存").append(",占用磁盘").append("\r\n");
// for (SystemMonitorLog log : result) {
// long monitorTime = log.getMonitorTime();
// buf.append(DateUtil.date(monitorTime)).append(StrUtil.COMMA)
// .append(log.getOccupyCpu()).append("%").append(StrUtil.COMMA)
// .append(log.getOccupyMemory()).append("%").append(StrUtil.COMMA)
// .append(log.getOccupyDisk()).append("%").append("\r\n");
// }
// String fileName = URLEncoder.encode("Jpom系统监控-" + node.getId(), "UTF-8");
// HttpServletResponse response = getResponse();
// response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(StandardCharsets.UTF_8), "GBK") + ".csv");
// response.setContentType("text/csv;charset=utf-8");
// ServletUtil.write(getResponse(), buf.toString(), CharsetUtil.UTF_8);
// }
// }
@RequestMapping(value = "processList", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String getProcessList() {
return NodeForward.request(getNode(), getRequest(), NodeUrl.ProcessList).toString();

View File

@ -35,8 +35,10 @@ import io.jpom.common.ServerOpenApi;
import io.jpom.common.interceptor.NotLogin;
import io.jpom.common.validator.ValidatorItem;
import io.jpom.common.validator.ValidatorRule;
import io.jpom.model.data.NodeModel;
import io.jpom.func.assets.model.MachineNodeModel;
import io.jpom.func.assets.server.MachineNodeServer;
import io.jpom.model.data.WorkspaceModel;
import io.jpom.model.user.UserModel;
import io.jpom.service.node.NodeService;
import io.jpom.service.system.WorkspaceService;
import lombok.extern.slf4j.Slf4j;
@ -63,11 +65,14 @@ public class NodeInfoController extends BaseServerController {
private final NodeService nodeService;
private final WorkspaceService workspaceService;
private final MachineNodeServer machineNodeServer;
public NodeInfoController(NodeService nodeService,
WorkspaceService workspaceService) {
WorkspaceService workspaceService,
MachineNodeServer machineNodeServer) {
this.nodeService = nodeService;
this.workspaceService = workspaceService;
this.machineNodeServer = machineNodeServer;
}
/**
@ -98,18 +103,18 @@ public class NodeInfoController extends BaseServerController {
ipsList.add(clientIp);
}
List<String> canUseIps = ipsList.stream().filter(s -> this.testIpPort(s, ping)).collect(Collectors.toList());
List<NodeModel> canUseNode = canUseIps.stream().map(s -> {
NodeModel model = NodeInfoController.this.createModel(s, loginName, loginPwd, port, workspaceId);
List<MachineNodeModel> canUseNode = canUseIps.stream().map(s -> {
MachineNodeModel model = NodeInfoController.this.createMachineNodeModel(s, loginName, loginPwd, port);
try {
nodeService.testNode(model);
machineNodeServer.testNode(model);
} catch (Exception e) {
log.warn("测试结果:{} {}", model.getUrl(), e.getMessage());
log.warn("测试结果:{} {}", model.getJpomUrl(), e.getMessage());
return null;
}
return model;
}).filter(Objects::nonNull).collect(Collectors.toList());
// 只返回能通的 IP
canUseIps = canUseNode.stream().map(NodeModel::getName).collect(Collectors.toList());
canUseIps = canUseNode.stream().map(MachineNodeModel::getName).collect(Collectors.toList());
int size1 = CollUtil.size(canUseNode);
//
JSONObject jsonObject = new JSONObject();
@ -120,8 +125,8 @@ public class NodeInfoController extends BaseServerController {
jsonObject.put("canUseNode", canUseNode);
//
exists = false;
for (NodeModel nodeModel : canUseNode) {
if (nodeService.existsByUrl(nodeModel.getUrl(), nodeModel.getWorkspaceId(), null)) {
for (MachineNodeModel nodeModel : canUseNode) {
if (machineNodeServer.existsByUrl(nodeModel.getJpomUrl(), null)) {
// 存在
jsonObject.put("type", "exists");
exists = true;
@ -130,10 +135,10 @@ public class NodeInfoController extends BaseServerController {
}
if (!exists) {
if (size1 == 1) {
// 只有一个 ip 可以使用
// 添加插件端
NodeModel first = CollUtil.getFirst(canUseNode);
nodeService.insertNotFill(first);
// 只有一个 ip 可以使用,添加插件端
BaseServerController.resetInfo(UserModel.EMPTY);
MachineNodeModel first = CollUtil.getFirst(canUseNode);
machineNodeServer.insertAndNode(first, workspaceId);
jsonObject.put("type", "success");
} else {
jsonObject.put("type", size1 == 0 ? "canUseIpEmpty" : "multiIp");
@ -174,15 +179,14 @@ public class NodeInfoController extends BaseServerController {
return NetUtil.ping(ip, pingTime * 1000);
}
private NodeModel createModel(String ip, String loginName, String loginPwd, int port, String workspaceId) {
NodeModel nodeModel = new NodeModel();
nodeModel.setWorkspaceId(workspaceId);
nodeModel.setName(ip);
nodeModel.setOpenStatus(1);
nodeModel.setLoginName(loginName);
nodeModel.setLoginPwd(loginPwd);
nodeModel.setUrl(ip + StrUtil.COLON + port);
nodeModel.setProtocol("http");
return nodeModel;
private MachineNodeModel createMachineNodeModel(String ip, String loginName, String loginPwd, int port) {
MachineNodeModel machineNodeModel = new MachineNodeModel();
machineNodeModel.setName(ip);
machineNodeModel.setStatus(1);
machineNodeModel.setJpomUsername(loginName);
machineNodeModel.setJpomPassword(loginPwd);
machineNodeModel.setJpomUrl(ip + StrUtil.COLON + port);
machineNodeModel.setJpomProtocol("http");
return machineNodeModel;
}
}

View File

@ -45,6 +45,7 @@ import io.jpom.common.JsonMessage;
import io.jpom.common.forward.NodeForward;
import io.jpom.common.forward.NodeUrl;
import io.jpom.common.validator.ValidatorItem;
import io.jpom.func.assets.server.MachineNodeServer;
import io.jpom.model.data.NodeModel;
import io.jpom.model.data.SystemIpConfigModel;
import io.jpom.model.node.NodeAgentWhitelist;
@ -55,7 +56,6 @@ import io.jpom.permission.SystemPermission;
import io.jpom.service.node.NodeService;
import io.jpom.service.system.SystemParametersServer;
import io.jpom.system.ExtConfigBean;
import io.jpom.system.db.InitDb;
import io.jpom.system.init.ProxySelectorConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.YamlMapFactoryBean;
@ -92,21 +92,21 @@ import java.util.Map;
public class SystemConfigController extends BaseServerController {
private final SystemParametersServer systemParametersServer;
private final InitDb initDb;
private final NodeService nodeService;
private final ProxySelectorConfig proxySelectorConfig;
private final DbExtConfig dbExtConfig;
private final MachineNodeServer machineNodeServer;
public SystemConfigController(SystemParametersServer systemParametersServer,
InitDb initDb,
NodeService nodeService,
ProxySelectorConfig proxySelectorConfig,
DbExtConfig dbExtConfig) {
DbExtConfig dbExtConfig,
MachineNodeServer machineNodeServer) {
this.systemParametersServer = systemParametersServer;
this.initDb = initDb;
this.nodeService = nodeService;
this.proxySelectorConfig = proxySelectorConfig;
this.dbExtConfig = dbExtConfig;
this.machineNodeServer = machineNodeServer;
}
/**
@ -420,7 +420,7 @@ public class SystemConfigController extends BaseServerController {
proxys = ObjectUtil.defaultIfNull(proxys, Collections.emptyList());
for (ProxySelectorConfig.ProxyConfigItem proxy : proxys) {
if (StrUtil.isNotEmpty(proxy.getProxyAddress())) {
nodeService.testHttpProxy(proxy.getProxyAddress());
machineNodeServer.testHttpProxy(proxy.getProxyAddress());
}
}
systemParametersServer.upsert(ProxySelectorConfig.KEY, proxys, ProxySelectorConfig.KEY);

View File

@ -0,0 +1,92 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.func.assets.controller;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import io.jpom.common.JsonMessage;
import io.jpom.func.assets.model.MachineNodeModel;
import io.jpom.func.assets.server.MachineNodeServer;
import io.jpom.permission.ClassFeature;
import io.jpom.permission.Feature;
import io.jpom.permission.MethodFeature;
import io.jpom.permission.SystemPermission;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.jpom.model.PageResultDto;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author bwcx_jzy
* @since 2023/2/18
*/
@RestController
@RequestMapping(value = "/system/assets/machine")
@Feature(cls = ClassFeature.SYSTEM_ASSETS_MACHINE)
@SystemPermission
public class MachineNodeController {
private final MachineNodeServer machineNodeServer;
public MachineNodeController(MachineNodeServer machineNodeServer) {
this.machineNodeServer = machineNodeServer;
}
@PostMapping(value = "list-data", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public JsonMessage<PageResultDto<MachineNodeModel>> listJson(HttpServletRequest request) {
PageResultDto<MachineNodeModel> pageResultDto = machineNodeServer.listPage(request);
return JsonMessage.success("", pageResultDto);
}
@GetMapping(value = "list-group", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.LIST)
public JsonMessage<List<String>> listGroup() {
String sql = "select `groupName` from " + machineNodeServer.getTableName() + " group by `groupName`";
List<Entity> list = machineNodeServer.query(sql);
// 筛选字段
List<String> collect = list.stream()
.map(entity -> {
Object obj = entity.get("groupName");
return StrUtil.toStringOrNull(obj);
})
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
return JsonMessage.success("", collect);
}
@PostMapping(value = "edit", produces = MediaType.APPLICATION_JSON_VALUE)
@Feature(method = MethodFeature.EDIT)
public JsonMessage<String> save(HttpServletRequest request) {
machineNodeServer.update(request);
return JsonMessage.success("操作成功");
}
}

View File

@ -0,0 +1,242 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.func.assets.model;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import io.jpom.model.BaseUserModifyDbModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import top.jpom.h2db.TableName;
import top.jpom.transport.INodeInfo;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
/**
* @author bwcx_jzy
* @since 2023/2/18
*/
@EqualsAndHashCode(callSuper = true)
@TableName(value = "MACHINE_NODE_INFO", name = "机器节点信息")
@Data
public class MachineNodeModel extends BaseUserModifyDbModel implements INodeInfo {
/**
* 机器名称
*/
private String name;
/**
* 分组名称
*/
private String groupName;
/**
* 机器主机名
*/
private String hostName;
/**
* 机器的 IP 多个
*/
private String hostIpv4s;
/**
* 负载
*/
private String osLoadAverage;
/**
* 系统运行时间自启动以来的时间
* 自启动以来的秒数
*/
private Long osSystemUptime;
/**
* 系统名称
*/
private String osName;
/**
* 系统版本
*/
private String osVersion;
/**
* 硬件版本
*/
private String osHardwareVersion;
/**
* CPU数
*/
private Integer osCpuCores;
/**
* 总内存
*/
private Long osMoneyTotal;
/**
* 硬盘总大小
*/
private Long osFileStoreTotal;
/**
* CPU 型号
*/
private String osCpuIdentifierName;
/**
* 占用cpu
*/
private Double osOccupyCpu;
/**
* 占用内存 总共
*/
private Double osOccupyMemory;
/**
* 占用磁盘
*/
private Double osOccupyDisk;
/**
* 节点连接状态
* <p>
* 状态{0无法连接1 正常, 2 授权信息错误, 3 状态码错误}
*/
private Integer status;
/**
* 状态消息
*/
private String statusMsg;
/**
* 传输方式0 服务器拉取1 节点机器推送
*/
private Integer transportMode;
/**
* jpom 通讯地址
*/
private String jpomUrl;
/**
* 节点协议
*/
private String jpomProtocol;
/**
* 通讯登录账号
*/
private String jpomUsername;
/**
* 通讯登录密码
*/
private String jpomPassword;
/**
* 超时时间
*/
private Integer jpomTimeout;
/**
* http 代理
*/
private String jpomHttpProxy;
/**
* http 代理 类型
*/
private String jpomHttpProxyType;
/**
* jpom 版本号
*/
private String jpomVersion;
/**
* jpom 启动时间
*/
private Long jpomUptime;
/**
* Jpom 打包时间
*/
private String jpomBuildTime;
/**
* 网络耗时延迟
*/
private Integer networkDelay;
/**
* jpom 项目数
*/
private Integer jpomProjectCount;
/**
* jpom 脚本数据
*/
private Integer jpomScriptCount;
/**
* java 版本
*/
private String javaVersion;
/**
* jvm 总内存
*/
private Long jvmTotalMemory;
/**
* jvm 剩余内存
*/
private Long jvmFreeMemory;
@Override
public String name() {
return this.getName();
}
@Override
public String url() {
return this.getJpomUrl();
}
@Override
public String scheme() {
return getJpomProtocol();
}
/**
* 获取 授权的信息
*
* @return sha1
*/
@Override
public String authorize() {
return SecureUtil.sha1(this.jpomUsername + "@" + this.jpomPassword);
}
/**
* 获取节点的代理
*
* @return proxy
*/
@Override
public Proxy proxy() {
String httpProxy = this.getJpomHttpProxy();
if (StrUtil.isNotEmpty(httpProxy)) {
List<String> split = StrUtil.splitTrim(httpProxy, StrUtil.COLON);
String host = CollUtil.getFirst(split);
int port = Convert.toInt(CollUtil.getLast(split), 0);
String type = this.getJpomHttpProxyType();
Proxy.Type type1 = EnumUtil.fromString(Proxy.Type.class, type, Proxy.Type.HTTP);
return new Proxy(type1, new InetSocketAddress(host, port));
}
return null;
}
@Override
public Integer timeout() {
return this.getJpomTimeout();
}
}

View File

@ -0,0 +1,75 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.func.assets.model;
import io.jpom.model.log.SystemMonitorLog;
import lombok.Data;
import lombok.EqualsAndHashCode;
import top.jpom.h2db.TableName;
import top.jpom.model.BaseDbModel;
/**
* @author bwcx_jzy
* @see SystemMonitorLog
* @see io.jpom.model.data.NodeModel
* @since 2023/02/18
*/
@EqualsAndHashCode(callSuper = true)
@TableName(value = "MACHINE_NODE_STAT_LOG", name = "资产机器节点统计")
@Data
public class MachineNodeStatLogModel extends BaseDbModel {
/**
* 机器id
*/
private String machineId;
/**
* 占用cpu
*/
private Double occupyCpu;
/**
* 占用内存 总共
*/
private Double occupyMemory;
/**
* 占用磁盘
*/
private Double occupyDisk;
/**
* 监控时间
* 插件端返回的时间
*/
private Long monitorTime;
/**
* 网络耗时延迟
*/
private Integer networkDelay;
/**
* 每秒发送的KB数,rxkB/s
*/
private Long netTxBytes;
/**
* 每秒接收的KB数,rxkB/s
*/
private Long netRxBytes;
}

View File

@ -0,0 +1,402 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.func.assets.server;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson2.JSONObject;
import io.jpom.common.*;
import io.jpom.common.forward.NodeForward;
import io.jpom.common.forward.NodeUrl;
import io.jpom.cron.IAsyncLoad;
import io.jpom.func.assets.model.MachineNodeModel;
import io.jpom.func.assets.model.MachineNodeStatLogModel;
import io.jpom.model.data.NodeModel;
import io.jpom.model.user.UserModel;
import io.jpom.service.h2db.BaseDbService;
import io.jpom.service.node.NodeService;
import io.jpom.system.AgentException;
import io.jpom.system.AuthorizeException;
import io.jpom.system.ServerConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author bwcx_jzy
* @since 2023/2/18
*/
@Service
@Slf4j
public class MachineNodeServer extends BaseDbService<MachineNodeModel> implements ILoadEvent, IAsyncLoad, Runnable {
private final NodeService nodeService;
private final ServerConfig.NodeConfig nodeConfig;
private final MachineNodeStatLogServer machineNodeStatLogServer;
public MachineNodeServer(NodeService nodeService,
ServerConfig serverConfig,
MachineNodeStatLogServer machineNodeStatLogServer) {
this.nodeService = nodeService;
this.nodeConfig = serverConfig.getNode();
this.machineNodeStatLogServer = machineNodeStatLogServer;
}
@Override
protected void fillSelectResult(MachineNodeModel data) {
Optional.ofNullable(data).ifPresent(machineNodeModel -> machineNodeModel.setJpomPassword(null));
}
@Override
protected void fillInsert(MachineNodeModel machineNodeModel) {
super.fillInsert(machineNodeModel);
machineNodeModel.setGroupName(StrUtil.emptyToDefault(machineNodeModel.getGroupName(), Const.DEFAULT_GROUP_NAME));
}
@Override
public void afterPropertiesSet(ApplicationContext applicationContext) throws Exception {
long count = this.count();
if (count != 0) {
log.debug("节点机器表已经存在 {} 条数据,不需要修复机器数据", count);
return;
}
List<NodeModel> list = nodeService.list(false);
if (CollUtil.isEmpty(list)) {
log.debug("没有任何节点信息,不需要修复机器数据");
return;
}
// delete from MACHINE_NODE_INFO;
// drop table MACHINE_NODE_INFO;
Map<String, List<NodeModel>> nodeUrlMap = CollStreamUtil.groupByKey(list, NodeModel::getUrl);
List<MachineNodeModel> machineNodeModels = new ArrayList<>(nodeUrlMap.size());
for (Map.Entry<String, List<NodeModel>> entry : nodeUrlMap.entrySet()) {
List<NodeModel> value = entry.getValue();
NodeModel first = CollUtil.getFirst(value);
if (value.size() > 1) {
log.warn("节点地址 {} 存在多个数据,将自动合并使用 {} 节点的配置信息", entry.getKey(), first.getName());
}
machineNodeModels.add(this.nodeInfoToMachineNode(first));
}
this.insert(machineNodeModels);
log.info("成功修复 {} 条机器节点数据", machineNodeModels.size());
// 更新节点的机器id
for (MachineNodeModel value : machineNodeModels) {
Entity entity = Entity.create();
entity.set("machineId", value.getId());
Entity where = Entity.create();
where.set("url", value.getJpomUrl());
nodeService.update(entity, where);
}
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE + 1;
}
/**
* 节点对象转机器对象
*
* @param nodeModel 节点
* @return 机器对象
*/
private MachineNodeModel nodeInfoToMachineNode(NodeModel nodeModel) {
MachineNodeModel machineNodeModel = new MachineNodeModel();
machineNodeModel.setName(nodeModel.getName());
machineNodeModel.setGroupName(nodeModel.getGroup());
machineNodeModel.setTransportMode(0);
machineNodeModel.setStatus(0);
machineNodeModel.setJpomTimeout(nodeModel.getTimeOut());
machineNodeModel.setJpomUrl(nodeModel.getUrl());
machineNodeModel.setJpomUsername(nodeModel.getLoginName());
machineNodeModel.setJpomPassword(nodeModel.getLoginPwd());
machineNodeModel.setJpomProtocol(nodeModel.getProtocol());
machineNodeModel.setJpomHttpProxy(nodeModel.getHttpProxy());
machineNodeModel.setJpomHttpProxyType(nodeModel.getHttpProxyType());
machineNodeModel.setModifyUser(nodeModel.getModifyUser());
machineNodeModel.setCreateTimeMillis(nodeModel.getCreateTimeMillis());
machineNodeModel.setModifyTimeMillis(nodeModel.getModifyTimeMillis());
return machineNodeModel;
}
@Override
public void startLoad() {
// 启动心跳检测
int heartSecond = nodeConfig.getHeartSecond();
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, "Jpom Node Monitor"));
scheduler.scheduleAtFixedRate(this, 10, heartSecond, TimeUnit.SECONDS);
}
@Override
public void run() {
MachineNodeModel machineNodeModel = new MachineNodeModel();
machineNodeModel.setTransportMode(0);
List<MachineNodeModel> machineNodeModels = this.listByBean(machineNodeModel);
this.checkList(machineNodeModels);
}
private void checkList(List<MachineNodeModel> machineNodeModels) {
if (CollUtil.isEmpty(machineNodeModels)) {
return;
}
machineNodeModels.forEach(machineNodeModel -> {
// 超时时间统一避免长时间无响应
machineNodeModel.setJpomTimeout(10);
//
ThreadUtil.execute(() -> {
try {
BaseServerController.resetInfo(UserModel.EMPTY);
long timeMillis = SystemClock.now();
JsonMessage<JSONObject> message = NodeForward.request(machineNodeModel, NodeUrl.GetStatInfo, new JSONObject());
int networkTime = (int) (System.currentTimeMillis() - timeMillis);
JSONObject jsonObject;
if (message.success()) {
jsonObject = message.getData(JSONObject.class);
} else {
// 状态码错
this.updateStatus(machineNodeModel, 3, message.toString());
return;
}
jsonObject.put("networkDelay", networkTime);
this.saveStatInfo(machineNodeModel, jsonObject);
} catch (AuthorizeException agentException) {
this.updateStatus(machineNodeModel, 2, agentException.getMessage());
} catch (AgentException e) {
this.updateStatus(machineNodeModel, 0, e.getMessage());
} catch (Exception e) {
this.updateStatus(machineNodeModel, 0, e.getMessage());
log.error("获取节点监控信息失败", e);
} finally {
BaseServerController.removeEmpty();
}
});
});
}
/**
* 更新统计信息
*
* @param machineNode 机器数据
* @param data 统计数据
*/
private void saveStatInfo(MachineNodeModel machineNode, JSONObject data) {
MachineNodeModel machineNodeModel = new MachineNodeModel();
machineNodeModel.setId(machineNode.getId());
machineNodeModel.setStatus(1);
machineNodeModel.setStatusMsg("ok");
Integer networkDelay = data.getInteger("networkDelay");
machineNodeModel.setNetworkDelay(networkDelay);
// jpom 相关信息
Optional.ofNullable(data.getJSONObject("jpomInfo")).ifPresent(jsonObject -> {
JSONObject jpomManifest = jsonObject.getJSONObject("jpomManifest");
Optional.ofNullable(jpomManifest)
.ifPresent(jsonObject1 -> {
machineNodeModel.setJpomVersion(jsonObject1.getString("version"));
machineNodeModel.setJpomBuildTime(jsonObject1.getString("timeStamp"));
machineNodeModel.setOsName(jsonObject1.getString("osName"));
machineNodeModel.setJpomUptime(jsonObject1.getLong("upTime"));
});
machineNodeModel.setJpomProjectCount(jsonObject.getIntValue("projectCount"));
machineNodeModel.setJpomScriptCount(jsonObject.getIntValue("scriptCount"));
//
machineNodeModel.setJvmFreeMemory(jsonObject.getLongValue("freeMemory"));
machineNodeModel.setJvmTotalMemory(jsonObject.getLongValue("totalMemory"));
machineNodeModel.setJavaVersion(jsonObject.getString("javaVersion"));
});
// 基础状态信息
MachineNodeStatLogModel machineNodeStatLogModel = new MachineNodeStatLogModel();
machineNodeStatLogModel.setMachineId(machineNodeModel.getId());
machineNodeStatLogModel.setNetworkDelay(networkDelay);
Optional.ofNullable(data.getJSONObject("simpleStatus")).ifPresent(jsonObject -> {
machineNodeModel.setOsOccupyMemory(ObjectUtil.defaultIfNull(jsonObject.getDouble("memory"), -1D));
machineNodeModel.setOsOccupyDisk(ObjectUtil.defaultIfNull(jsonObject.getDouble("disk"), -1D));
machineNodeModel.setOsOccupyCpu(ObjectUtil.defaultIfNull(jsonObject.getDouble("cpu"), -1D));
//
machineNodeStatLogModel.setOccupyCpu(machineNodeModel.getOsOccupyCpu());
machineNodeStatLogModel.setOccupyMemory(machineNodeModel.getOsOccupyMemory());
machineNodeStatLogModel.setOccupyDisk(machineNodeModel.getOsOccupyDisk());
machineNodeStatLogModel.setNetTxBytes(jsonObject.getLong("netTxBytes"));
machineNodeStatLogModel.setNetRxBytes(jsonObject.getLong("netRxBytes"));
machineNodeStatLogModel.setMonitorTime(jsonObject.getLongValue("time"));
});
// 系统信息
Optional.ofNullable(data.getJSONObject("systemInfo")).ifPresent(jsonObject -> {
machineNodeModel.setOsSystemUptime(jsonObject.getLong("systemUptime"));
machineNodeModel.setOsVersion(jsonObject.getString("osVersion"));
machineNodeModel.setHostName(jsonObject.getString("hostName"));
machineNodeModel.setOsHardwareVersion(jsonObject.getString("hardwareVersion"));
machineNodeModel.setHostIpv4s(CollUtil.join(jsonObject.getList("hostIpv4s", String.class), StrUtil.COMMA));
machineNodeModel.setOsCpuIdentifierName(jsonObject.getString("osCpuIdentifierName"));
machineNodeModel.setOsCpuCores(jsonObject.getInteger("osCpuCores"));
machineNodeModel.setOsMoneyTotal(jsonObject.getLong("osMoneyTotal"));
machineNodeModel.setOsLoadAverage(CollUtil.join(jsonObject.getList("osLoadAverage", Double.class), StrUtil.COMMA));
machineNodeModel.setOsFileStoreTotal(jsonObject.getLong("osFileStoreTotal"));
});
this.updateById(machineNodeModel);
machineNodeStatLogServer.insert(machineNodeStatLogModel);
}
/**
* 更新机器状态
*
* @param machineNode 机器信息
* @param status 状态
* @param msg 状态消息
*/
private void updateStatus(MachineNodeModel machineNode, int status, String msg) {
MachineNodeModel machineNodeModel = new MachineNodeModel();
machineNodeModel.setId(machineNode.getId());
machineNodeModel.setStatus(status);
machineNodeModel.setStatusMsg(msg);
this.updateById(machineNodeModel);
}
private MachineNodeModel resolveMachineData(HttpServletRequest request) {
// 创建对象
MachineNodeModel machineNodeModel = ServletUtil.toBean(request, MachineNodeModel.class, true);
Assert.hasText(machineNodeModel.getName(), "请填写机器名称");
Assert.hasText(machineNodeModel.getJpomUrl(), "请填写 节点地址");
Assert.hasText(machineNodeModel.getJpomUsername(), "请填写节点密码");
//
MachineNodeModel update = new MachineNodeModel();
update.setId(machineNodeModel.getId());
update.setName(machineNodeModel.getName());
update.setJpomHttpProxy(machineNodeModel.getJpomHttpProxy());
update.setJpomHttpProxyType(machineNodeModel.getJpomHttpProxyType());
update.setJpomUrl(machineNodeModel.getJpomUrl());
update.setJpomProtocol(machineNodeModel.getJpomProtocol());
update.setJpomUsername(machineNodeModel.getJpomUsername());
update.setJpomPassword(machineNodeModel.getJpomPassword());
update.setJpomTimeout(machineNodeModel.getJpomTimeout());
return update;
}
public boolean existsByUrl(String jpomUrl, String id) {
Assert.hasText(jpomUrl, "节点地址不能为空");
//
Entity entity = Entity.create();
entity.set("jpomUrl", jpomUrl);
if (StrUtil.isNotEmpty(id)) {
entity.set("id", StrUtil.format(" <> {}", id));
}
return this.exists(entity);
}
public void update(HttpServletRequest request) {
MachineNodeModel machineNodeModel = this.resolveMachineData(request);
boolean exists = this.existsByUrl(machineNodeModel.getJpomUrl(), machineNodeModel.getId());
Assert.state(!exists, "对应的节点已经存在啦");
this.testNode(machineNodeModel);
// 更新状态
machineNodeModel.setStatus(1);
//
this.testHttpProxy(machineNodeModel.getJpomHttpProxy());
//
if (StrUtil.isNotEmpty(machineNodeModel.getId())) {
this.updateById(machineNodeModel);
} else {
// 目前默认为此
machineNodeModel.setTransportMode(0);
this.insert(machineNodeModel);
}
}
/**
* 测试节点是否可以访问
*
* @param nodeModel 节点信息
*/
public void testNode(MachineNodeModel nodeModel) {
//
int timeout = ObjectUtil.defaultIfNull(nodeModel.getJpomTimeout(), 0);
// 检查是否可用默认为5秒避免太长时间无法连接一直等待
nodeModel.setJpomTimeout(5);
//
JsonMessage<JpomManifest> objectJsonMessage = NodeForward.request(nodeModel, StrUtil.EMPTY, NodeUrl.Info, "nodeId", nodeModel.getId());
try {
JpomManifest jpomManifest = objectJsonMessage.getData(JpomManifest.class);
Assert.notNull(jpomManifest, "节点连接失败,请检查节点是否在线");
} catch (Exception e) {
log.error("节点连接失败,请检查节点是否在线", e);
throw new IllegalStateException("节点返回信息异常,请检查节点地址是否配置正确或者代理配置是否正确");
}
//
nodeModel.setJpomTimeout(timeout);
}
/**
* 探测 http proxy 是否可用
*
* @param httpProxy http proxy
*/
public void testHttpProxy(String httpProxy) {
if (StrUtil.isNotEmpty(httpProxy)) {
List<String> split = StrUtil.splitTrim(httpProxy, StrUtil.COLON);
Assert.isTrue(CollUtil.size(split) == 2, "HTTP代理地址格式不正确");
String host = split.get(0);
int port = Convert.toInt(split.get(1), 0);
Assert.isTrue(StrUtil.isNotEmpty(host) && NetUtil.isValidPort(port), "HTTP代理地址格式不正确");
//
try {
NetUtil.netCat(host, port, StrUtil.EMPTY.getBytes());
} catch (Exception e) {
log.warn("HTTP代理地址不可用:" + httpProxy, e);
throw new IllegalArgumentException("HTTP代理地址不可用:" + e.getMessage());
}
}
}
public void insertAndNode(MachineNodeModel machineNodeModel, String workspaceId) {
this.insert(machineNodeModel);
NodeModel nodeModel = this.createModel(machineNodeModel, workspaceId);
nodeService.insert(nodeModel);
}
private NodeModel createModel(MachineNodeModel machineNodeModel, String workspaceId) {
NodeModel nodeModel = new NodeModel();
nodeModel.setWorkspaceId(workspaceId);
nodeModel.setName(machineNodeModel.getName());
nodeModel.setOpenStatus(1);
nodeModel.setGroup(Const.DEFAULT_GROUP_NAME);
return nodeModel;
}
}

View File

@ -0,0 +1,35 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.func.assets.server;
import io.jpom.func.assets.model.MachineNodeStatLogModel;
import io.jpom.service.h2db.BaseDbService;
import org.springframework.stereotype.Service;
/**
* @author bwcx_jzy
* @since 2023/2/18
*/
@Service
public class MachineNodeStatLogServer extends BaseDbService<MachineNodeStatLogModel> {
}

View File

@ -0,0 +1,40 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author bwcx_jzy
* @since 2023/2/18
*/
@EqualsAndHashCode(callSuper = true)
@Data
public abstract class BaseMachineModel extends BaseGroupModel {
/**
* 机器id
*/
private String machineId;
}

View File

@ -22,22 +22,13 @@
*/
package io.jpom.model.data;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import io.jpom.common.forward.NodeUrl;
import io.jpom.model.BaseGroupModel;
import cn.hutool.core.annotation.PropIgnore;
import io.jpom.func.assets.model.MachineNodeModel;
import io.jpom.model.BaseMachineModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import top.jpom.h2db.TableName;
import top.jpom.transport.INodeInfo;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
/**
* 节点实体
@ -49,16 +40,20 @@ import java.util.List;
@TableName(value = "NODE_INFO", name = "节点信息")
@Data
@NoArgsConstructor
public class NodeModel extends BaseGroupModel implements INodeInfo {
public class NodeModel extends BaseMachineModel {
@Deprecated
private String url;
@Deprecated
private String loginName;
@Deprecated
private String loginPwd;
private String name;
/**
* 节点协议
*/
@Deprecated
private String protocol;
/**
* 开启状态如果关闭状态就暂停使用节点 1 启用
@ -67,29 +62,31 @@ public class NodeModel extends BaseGroupModel implements INodeInfo {
/**
* 节点超时时间
*/
@Deprecated
private Integer timeOut;
/**
* 绑定的sshId
*/
private String sshId;
/**
* 锁定类型
*/
private String unLockType;
/**
* http 代理
*/
@Deprecated
private String httpProxy;
/**
* https 代理 类型
*/
@Deprecated
private String httpProxyType;
/**
* 排序
*/
private Float sortValue;
@PropIgnore
private MachineNodeModel machineNodeData;
public boolean isOpenStatus() {
return openStatus != null && openStatus == 1;
}
@ -103,73 +100,8 @@ public class NodeModel extends BaseGroupModel implements INodeInfo {
this.setWorkspaceId(workspaceId);
}
/**
* 获取 授权的信息
*
* @return sha1
*/
public String toAuthorize() {
return SecureUtil.sha1(loginName + "@" + loginPwd);
}
// public String getRealUrl(NodeUrl nodeUrl) {
// return StrUtil.format("{}://{}{}", getProtocol().toLowerCase(), getUrl(), nodeUrl.getUrl());
// }
public String getRealUrl(NodeUrl nodeUrl) {
return StrUtil.format("{}://{}{}", getProtocol().toLowerCase(), getUrl(), nodeUrl.getUrl());
}
@Override
public String name() {
return this.getName();
}
@Override
public String url() {
return this.getUrl();
}
@Override
public String scheme() {
return getProtocol();
}
@Override
public String authorize() {
return this.toAuthorize();
}
/**
* 获取节点的代理
*
* @return proxy
*/
@Override
public Proxy proxy() {
String httpProxy = this.getHttpProxy();
if (StrUtil.isNotEmpty(httpProxy)) {
List<String> split = StrUtil.splitTrim(httpProxy, StrUtil.COLON);
String host = CollUtil.getFirst(split);
int port = Convert.toInt(CollUtil.getLast(split), 0);
String type = this.getHttpProxyType();
Proxy.Type type1 = EnumUtil.fromString(Proxy.Type.class, type, Proxy.Type.HTTP);
return new Proxy(type1, new InetSocketAddress(host, port));
}
return null;
}
/**
* 创建代理
*
* @param type 代理类型
* @param httpProxy 代理地址
* @return proxy
*/
public static Proxy crateProxy(String type, String httpProxy) {
if (StrUtil.isNotEmpty(httpProxy)) {
List<String> split = StrUtil.splitTrim(httpProxy, StrUtil.COLON);
String host = CollUtil.getFirst(split);
int port = Convert.toInt(CollUtil.getLast(split), 0);
Proxy.Type type1 = EnumUtil.fromString(Proxy.Type.class, type, Proxy.Type.HTTP);
return new Proxy(type1, new InetSocketAddress(host, port));
}
return null;
}
}

View File

@ -89,6 +89,7 @@ public enum ClassFeature {
SYSTEM_CACHE("系统缓存"),
SYSTEM_LOG("系统日志"),
SYSTEM_UPGRADE("在线升级"),
SYSTEM_ASSETS_MACHINE("机器资产管理"),
SYSTEM_CONFIG("服务端系统配置"),
SYSTEM_EXT_CONFIG("系统配置目录"),
SYSTEM_CONFIG_IP("系统配置IP白名单"),

View File

@ -31,6 +31,7 @@ import org.springframework.stereotype.Service;
* @since 2019/9/13
*/
@Service
@Deprecated
public class DbSystemMonitorLogService extends BaseDbService<SystemMonitorLog> {
@Override

View File

@ -110,17 +110,17 @@ public abstract class BaseDbService<T extends BaseDbModel> extends BaseDbCommonS
}
}
/**
* 不填充 插入
*
* @param t 数据
*/
public void insertNotFill(T t) {
// def create time
t.setCreateTimeMillis(ObjectUtil.defaultIfNull(t.getCreateTimeMillis(), SystemClock.now()));
t.setId(ObjectUtil.defaultIfNull(t.getId(), IdUtil.fastSimpleUUID()));
super.insert(t);
}
// /**
// * 不填充 插入
// *
// * @param t 数据
// */
// public void insertNotFill(T t) {
// // def create time
// t.setCreateTimeMillis(ObjectUtil.defaultIfNull(t.getCreateTimeMillis(), SystemClock.now()));
// t.setId(ObjectUtil.defaultIfNull(t.getId(), IdUtil.fastSimpleUUID()));
// super.insert(t);
// }
@Override
public void insert(Collection<T> t) {
@ -208,7 +208,11 @@ public abstract class BaseDbService<T extends BaseDbModel> extends BaseDbCommonS
}
public List<T> list() {
return super.listByBean(ReflectUtil.newInstance(this.tClass));
return this.list(true);
}
public List<T> list(boolean fill) {
return this.listByBean(ReflectUtil.newInstance(this.tClass), fill);
}
public long count() {

View File

@ -23,6 +23,7 @@
package io.jpom.service.h2db;
import cn.hutool.db.Entity;
import io.jpom.common.Const;
import io.jpom.common.ServerConst;
import io.jpom.model.BaseGroupModel;
@ -66,6 +67,6 @@ public abstract class BaseGroupService<T extends BaseGroupModel> extends BaseWor
*/
public void repairGroupFiled() {
String sql = "update " + getTableName() + " set `GROUP`=? where `GROUP` is null or `GROUP`=''";
super.execute(sql, "默认");
super.execute(sql, Const.DEFAULT_GROUP_NAME);
}
}

View File

@ -23,6 +23,7 @@
package io.jpom.service.h2db;
import cn.hutool.db.Entity;
import io.jpom.common.Const;
import io.jpom.common.ServerConst;
import io.jpom.model.BaseNodeGroupModel;
import io.jpom.service.node.NodeService;
@ -71,6 +72,6 @@ public abstract class BaseNodeGroupService<T extends BaseNodeGroupModel> extends
*/
public void repairGroupFiled() {
String sql = "update " + getTableName() + " set `GROUP`=? where `GROUP` is null or `GROUP`=''";
super.execute(sql, "默认");
super.execute(sql, Const.DEFAULT_GROUP_NAME);
}
}

View File

@ -22,26 +22,16 @@
*/
package io.jpom.service.node;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.extra.servlet.ServletUtil;
import io.jpom.common.Const;
import io.jpom.common.JpomManifest;
import io.jpom.common.JsonMessage;
import io.jpom.common.forward.NodeForward;
import io.jpom.common.forward.NodeUrl;
import io.jpom.model.data.NodeModel;
import io.jpom.model.data.SshModel;
import io.jpom.model.data.WorkspaceModel;
import io.jpom.service.h2db.BaseGroupService;
import io.jpom.service.node.ssh.SshService;
import io.jpom.service.system.WorkspaceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -49,10 +39,7 @@ import org.springframework.util.Assert;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* @author bwcx_jzy
@ -63,9 +50,6 @@ import java.util.stream.Collectors;
public class NodeService extends BaseGroupService<NodeModel> {
private final SshService sshService;
@Resource
@Lazy
private WorkspaceService workspaceService;
@Resource
@Lazy
@ -82,45 +66,53 @@ public class NodeService extends BaseGroupService<NodeModel> {
}
}
/**
* 测试节点是否可以访问
*
* @param nodeModel 节点信息
*/
public void testNode(NodeModel nodeModel) {
//
int timeOut = ObjectUtil.defaultIfNull(nodeModel.getTimeOut(), 0);
// 检查是否可用默认为5秒避免太长时间无法连接一直等待
nodeModel.setTimeOut(5);
//
JsonMessage<JpomManifest> objectJsonMessage = NodeForward.request(nodeModel, NodeUrl.Info, "nodeId", nodeModel.getId());
try {
JpomManifest jpomManifest = objectJsonMessage.getData(JpomManifest.class);
Assert.notNull(jpomManifest, "节点连接失败,请检查节点是否在线");
} catch (Exception e) {
log.error("节点连接失败,请检查节点是否在线", e);
throw new IllegalStateException("节点返回信息异常,请检查节点地址是否配置正确或者代理配置是否正确");
}
//
nodeModel.setTimeOut(timeOut);
}
public boolean existsByUrl(String url, String workspaceId, String id) {
// Entity entity = Entity.create();
// entity.set("url", nodeModel.getUrl());
// entity.set("workspaceId", workspaceId);
// if (StrUtil.isNotEmpty(id)) {
// entity.set("id", StrUtil.format(" <> {}", id));
// }
// boolean exists = super.exists(entity);
// Assert.state(!exists, "对应的节点已经存在啦");
// 可能出现错误
NodeModel nodeModel1 = new NodeModel();
nodeModel1.setUrl(url);
nodeModel1.setWorkspaceId(workspaceId);
List<NodeModel> nodeModels = ObjectUtil.defaultIfNull(super.listByBean(nodeModel1), Collections.EMPTY_LIST);
Optional<NodeModel> any = nodeModels.stream().filter(nodeModel2 -> !StrUtil.equals(id, nodeModel2.getId())).findAny();
return any.isPresent();
// public boolean existsByUrl(String url, String workspaceId, String id) {
//
// // 可能出现错误
// NodeModel nodeModel1 = new NodeModel();
// nodeModel1.setUrl(url);
// nodeModel1.setWorkspaceId(workspaceId);
// List<NodeModel> nodeModels = ObjectUtil.defaultIfNull(super.listByBean(nodeModel1), Collections.EMPTY_LIST);
// Optional<NodeModel> any = nodeModels.stream().filter(nodeModel2 -> !StrUtil.equals(id, nodeModel2.getId())).findAny();
// return any.isPresent();
// }
private NodeModel resolveNode(HttpServletRequest request) {
// 创建对象
NodeModel nodeModel = ServletUtil.toBean(request, NodeModel.class, true);
String id = nodeModel.getId();
Assert.hasText(id, "没有节点id");
Assert.hasText(nodeModel.getName(), "请填写节点名称");
// 兼容就数据判断
String checkId = StrUtil.replace(id, StrUtil.DASHED, StrUtil.UNDERLINE);
Validator.validateGeneral(checkId, 2, Const.ID_MAX_LEN, "节点id不能为空并且2-50英文字母 、数字和下划线)");
Assert.hasText(nodeModel.getName(), "节点名称 不能为空");
String workspaceId = this.getCheckUserWorkspace(request);
nodeModel.setWorkspaceId(workspaceId);
// 判断 ssh
String sshId = nodeModel.getSshId();
if (StrUtil.isNotEmpty(sshId)) {
SshModel byKey = sshService.getByKey(sshId, request);
Assert.notNull(byKey, "对应的 SSH 不存在");
Entity entity = Entity.create();
entity.set("sshId", sshId);
entity.set("workspaceId", workspaceId);
if (StrUtil.isNotEmpty(id)) {
entity.set("id", StrUtil.format(" <> {}", id));
}
boolean exists = super.exists(entity);
Assert.state(!exists, "对应的SSH已经被其他节点绑定啦");
}
NodeModel update = new NodeModel();
update.setId(id);
update.setName(nodeModel.getName());
update.setGroup(nodeModel.getGroup());
update.setSshId(nodeModel.getSshId());
update.setOpenStatus(nodeModel.getOpenStatus());
return update;
}
/**
@ -129,95 +121,12 @@ public class NodeService extends BaseGroupService<NodeModel> {
* @param request 请求对象
*/
public void update(HttpServletRequest request) {
String type = request.getParameter("type");
boolean create = "add".equalsIgnoreCase(type);
// 创建对象
NodeModel nodeModel = ServletUtil.toBean(request, NodeModel.class, true);
String id = nodeModel.getId();
if (StrUtil.isNotEmpty(id)) {
String checkId = StrUtil.replace(id, StrUtil.DASHED, StrUtil.UNDERLINE);
Validator.validateGeneral(checkId, 2, Const.ID_MAX_LEN, "节点id不能为空并且2-50英文字母 、数字和下划线)");
}
Assert.hasText(nodeModel.getName(), "节点名称 不能为空");
//
this.testHttpProxy(nodeModel.getHttpProxy());
NodeModel existsNode = super.getByKey(id);
String workspaceId = this.getCheckUserWorkspace(request);
nodeModel.setWorkspaceId(workspaceId);
//nodeModel.setProtocol(StrUtil.emptyToDefault(nodeModel.getProtocol(), "http"));
{// 节点地址 重复
boolean exists = this.existsByUrl(nodeModel.getUrl(), nodeModel.getWorkspaceId(), id);
Assert.state(!exists, "对应的节点已经存在啦");
}
// 判断 ssh
String sshId = nodeModel.getSshId();
if (StrUtil.isNotEmpty(sshId)) {
SshModel byKey = sshService.getByKey(sshId, request);
Assert.notNull(byKey, "对应的 SSH 不存在");
List<NodeModel> nodeBySshId = this.getNodeBySshId(sshId);
nodeBySshId = ObjectUtil.defaultIfNull(nodeBySshId, Collections.emptyList());
boolean anyMatch = nodeBySshId.stream().anyMatch(nodeModel2 -> !StrUtil.equals(id, nodeModel2.getId()));
// Optional<NodeModel> any = anyMatch.findAny();
Assert.state(!anyMatch, "对应的SSH已经被其他节点绑定啦");
}
if (nodeModel.isOpenStatus()) {
//
this.checkLockType(existsNode);
this.testNode(nodeModel);
}
if (create) {
this.insert(nodeModel);
// 同步项目
projectInfoCacheService.syncNode(nodeModel);
} else {
this.update(nodeModel);
}
NodeModel nodeModel = this.resolveNode(request);
this.update(nodeModel);
// 同步项目
projectInfoCacheService.syncNode(nodeModel);
}
/**
* 探测 http proxy 是否可用
*
* @param httpProxy http proxy
*/
public void testHttpProxy(String httpProxy) {
if (StrUtil.isNotEmpty(httpProxy)) {
List<String> split = StrUtil.splitTrim(httpProxy, StrUtil.COLON);
Assert.isTrue(CollUtil.size(split) == 2, "HTTP代理地址格式不正确");
String host = split.get(0);
int port = Convert.toInt(split.get(1), 0);
Assert.isTrue(StrUtil.isNotEmpty(host) && NetUtil.isValidPort(port), "HTTP代理地址格式不正确");
//
try {
NetUtil.netCat(host, port, StrUtil.EMPTY.getBytes());
} catch (Exception e) {
log.warn("HTTP代理地址不可用:" + httpProxy, e);
throw new IllegalArgumentException("HTTP代理地址不可用:" + e.getMessage());
}
}
}
/**
* 解锁分配工作空间
*
* @param id 节点ID
* @param workspaceId 工作空间
*/
public void unLock(String id, String workspaceId) {
NodeModel nodeModel = super.getByKey(id);
Assert.notNull(nodeModel, "没有对应对节点");
//
WorkspaceModel workspaceModel = workspaceService.getByKey(workspaceId);
Assert.notNull(workspaceModel, "没有对应对工作空间");
NodeModel nodeModel1 = new NodeModel();
nodeModel1.setId(id);
nodeModel1.setWorkspaceId(workspaceId);
nodeModel1.setUnLockType(StrUtil.EMPTY);
nodeModel1.setOpenStatus(1);
super.update(nodeModel1);
}
/**
* 将节点信息同步到其他工作空间
@ -228,144 +137,59 @@ public class NodeService extends BaseGroupService<NodeModel> {
*/
public void syncToWorkspace(String ids, String nowWorkspaceId, String workspaceId) {
StrUtil.splitTrim(ids, StrUtil.COMMA).forEach(id -> {
NodeModel data = super.getByKey(id, false, entity -> entity.set("workspaceId", nowWorkspaceId));
NodeModel data = this.getByKey(id, false, entity -> entity.set("workspaceId", nowWorkspaceId));
Assert.notNull(data, "没有对应到节点信息");
//
NodeModel where = new NodeModel();
where.setWorkspaceId(workspaceId);
where.setUrl(data.getUrl());
NodeModel nodeModel = NodeService.super.queryByBean(where);
if (nodeModel == null) {
// 不存在则添加节点
data.setId(null);
data.setWorkspaceId(workspaceId);
data.setCreateTimeMillis(null);
data.setModifyTimeMillis(null);
data.setModifyUser(null);
// ssh 不同步
data.setSshId(null);
NodeService.super.insert(data);
} else {
// 修改信息
NodeModel update = new NodeModel(nodeModel.getId());
update.setLoginName(data.getLoginName());
update.setLoginPwd(data.getLoginPwd());
update.setProtocol(data.getProtocol());
update.setHttpProxy(data.getHttpProxy());
update.setHttpProxyType(data.getHttpProxyType());
NodeService.super.updateById(update);
}
where.setMachineId(data.getMachineId());
NodeModel nodeModel = this.queryByBean(where);
Assert.isNull(nodeModel, () -> "对应工作空间已经存在改节点啦:" + nodeModel.getName());
// 不存在则添加节点
data.setId(null);
data.setWorkspaceId(workspaceId);
data.setCreateTimeMillis(null);
data.setModifyTimeMillis(null);
data.setModifyUser(null);
data.setLoginName(null);
data.setUrl(null);
data.setLoginPwd(null);
data.setProtocol(null);
data.setHttpProxy(null);
data.setHttpProxyType(null);
// ssh 不同步
data.setSshId(null);
this.insert(data);
});
}
/**
* 判断节点是否锁定中
*
* @param nodeModel 节点
*/
private void checkLockType(NodeModel nodeModel) {
if (nodeModel == null) {
return;
}
// 判断锁定类型
if (StrUtil.isNotEmpty(nodeModel.getUnLockType())) {
//
Assert.state(!StrUtil.equals(nodeModel.getUnLockType(), "unassignedWorkspace"), "当前节点还未分配工作空间,请分配");
}
}
@Override
public void insert(NodeModel nodeModel) {
this.fillNodeInfo(nodeModel);
super.insert(nodeModel);
this.updateDuplicateNode(nodeModel);
protected void fillInsert(NodeModel nodeModel) {
super.fillInsert(nodeModel);
// 表中字段不能为空设置为空字符串
nodeModel.setLoginName(StrUtil.EMPTY);
nodeModel.setLoginPwd(StrUtil.EMPTY);
nodeModel.setProtocol(StrUtil.EMPTY);
nodeModel.setUrl(StrUtil.EMPTY);
}
@Override
public void insertNotFill(NodeModel nodeModel) {
nodeModel.setWorkspaceId(StrUtil.emptyToDefault(nodeModel.getWorkspaceId(), Const.WORKSPACE_DEFAULT_ID));
this.fillNodeInfo(nodeModel);
super.insertNotFill(nodeModel);
this.updateDuplicateNode(nodeModel);
}
// @Override
// public void insertNotFill(NodeModel nodeModel) {
// nodeModel.setWorkspaceId(StrUtil.emptyToDefault(nodeModel.getWorkspaceId(), Const.WORKSPACE_DEFAULT_ID));
// this.fillNodeInfo(nodeModel);
// super.insertNotFill(nodeModel);
// }
/**
* 填充默认字段
*
* @param nodeModel 节点
*/
private void fillNodeInfo(NodeModel nodeModel) {
nodeModel.setProtocol(StrUtil.emptyToDefault(nodeModel.getProtocol(), "http"));
nodeModel.setOpenStatus(ObjectUtil.defaultIfNull(nodeModel.getOpenStatus(), 0));
}
// /**
// * 填充默认字段
// *
// * @param nodeModel 节点
// */
// private void fillNodeInfo(NodeModel nodeModel) {
// nodeModel.setProtocol(StrUtil.emptyToDefault(nodeModel.getProtocol(), "http"));
// nodeModel.setOpenStatus(ObjectUtil.defaultIfNull(nodeModel.getOpenStatus(), 0));
// }
@Override
public int delByKey(String keyValue) {
return super.delByKey(keyValue);
}
@Override
public int del(Entity where) {
return super.del(where);
}
@Override
public int updateById(NodeModel info) {
int updateById = super.updateById(info);
if (updateById > 0) {
this.updateDuplicateNode(info);
}
return updateById;
}
/**
* 更新相同节点对 授权信息
*
* @param info 节点信息
*/
private void updateDuplicateNode(NodeModel info) {
// if (StrUtil.hasEmpty(info.getUrl(), info.getLoginName(), info.getLoginPwd())) {
// return;
// }
// NodeModel update = new NodeModel();
// update.setLoginName(info.getLoginName());
// update.setLoginPwd(info.getLoginPwd());
// //
// NodeModel where = new NodeModel();
// where.setUrl(info.getUrl());
// int updateCount = super.update(super.dataBeanToEntity(update), super.dataBeanToEntity(where));
// if (updateCount > 1) {
// log.debug("update duplicate node {} {}", info.getUrl(), updateCount);
// }
}
/**
* 根据 url 去重
*
* @return list
*/
public List<NodeModel> listDeDuplicationByUrl() {
String sql = "select url,max(loginName) as loginName,max(loginPwd) as loginPwd,max(protocol) as protocol from " + super.getTableName() + " group by url";
List<Entity> query = this.query(sql);
if (query != null) {
return query.stream().map((entity -> this.entityToBean(entity, this.tClass))).collect(Collectors.toList());
}
return null;
}
/**
* 根据 url 去重
*
* @return list
*/
public List<String> getDeDuplicationByUrl() {
String sql = "select url from " + super.getTableName() + " group by url";
List<Entity> query = this.query(sql);
if (query != null) {
return query.stream().map((entity -> entity.getStr("url"))).collect(Collectors.toList());
}
return null;
}
public List<NodeModel> getNodeBySshId(String sshId) {
NodeModel nodeModel = new NodeModel();

View File

@ -20,275 +20,280 @@
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.service.stat;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import com.alibaba.fastjson2.JSONObject;
import io.jpom.common.BaseServerController;
import io.jpom.common.JsonMessage;
import io.jpom.common.forward.NodeForward;
import io.jpom.common.forward.NodeUrl;
import io.jpom.cron.IAsyncLoad;
import io.jpom.model.data.NodeModel;
import io.jpom.model.log.SystemMonitorLog;
import io.jpom.model.stat.NodeStatModel;
import io.jpom.model.user.UserModel;
import io.jpom.service.dblog.DbSystemMonitorLogService;
import io.jpom.service.h2db.BaseWorkspaceService;
import io.jpom.service.node.NodeService;
import io.jpom.system.AgentException;
import io.jpom.system.AuthorizeException;
import io.jpom.system.ServerConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 节点统计 service
*
* @author bwcx_jzy
* @since 2022/1/22
*/
@Service
@Slf4j
public class NodeStatService extends BaseWorkspaceService<NodeStatModel> implements IAsyncLoad, Runnable {
private final DbSystemMonitorLogService dbSystemMonitorLogService;
private final NodeService nodeService;
private final ServerConfig.NodeConfig nodeConfig;
public NodeStatService(DbSystemMonitorLogService dbSystemMonitorLogService,
NodeService nodeService,
ServerConfig serverConfig) {
this.dbSystemMonitorLogService = dbSystemMonitorLogService;
this.nodeService = nodeService;
this.nodeConfig = serverConfig.getNode();
}
/**
* 根据 url 去重
*
* @return list
*/
public List<String> getDeDuplicationByUrl() {
String sql = "select url from " + super.getTableName() + " group by url";
List<Entity> query = this.query(sql);
if (query != null) {
return query.stream().map((entity -> entity.getStr("url"))).collect(Collectors.toList());
}
return null;
}
@Override
public void startLoad() {
// 启动心跳检测
int heartSecond = nodeConfig.getHeartSecond();
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, "Jpom Node Monitor"));
scheduler.scheduleAtFixedRate(this, 10, heartSecond, TimeUnit.SECONDS);
// 清理 错误的节点统计数据
List<String> duplicationByUrl = nodeService.getDeDuplicationByUrl();
List<String> statUrl = this.getDeDuplicationByUrl();
Collection<String> subtract = CollUtil.subtract(statUrl, duplicationByUrl);
//
for (String s : subtract) {
NodeStatModel statModel = new NodeStatModel();
statModel.setUrl(s);
this.del(this.dataBeanToEntity(statModel));
}
}
@Override
public void run() {
List<NodeModel> nodeModels = nodeService.listDeDuplicationByUrl();
//
this.checkList(nodeModels);
}
private List<NodeModel> getListByUrl(String url) {
NodeModel nodeModel = new NodeModel();
nodeModel.setUrl(url);
return nodeService.listByBean(nodeModel);
}
private void checkList(List<NodeModel> nodeModels) {
if (CollUtil.isEmpty(nodeModels)) {
return;
}
nodeModels.forEach(nodeModel -> {
//
nodeModel.setName(nodeModel.getUrl());
List<NodeModel> modelList = this.getListByUrl(nodeModel.getUrl());
boolean match = modelList.stream().allMatch(NodeModel::isOpenStatus);
if (!match) {
// 节点都关闭
try {
BaseServerController.resetInfo(UserModel.EMPTY);
this.save(modelList, 4, "节点禁用中");
} finally {
BaseServerController.removeEmpty();
}
return;
}
nodeModel.setOpenStatus(1);
nodeModel.setTimeOut(5);
//
ThreadUtil.execute(() -> {
try {
BaseServerController.resetInfo(UserModel.EMPTY);
JSONObject nodeTopInfo = this.getNodeTopInfo(nodeModel);
//
long timeMillis = SystemClock.now();
JsonMessage<Object> jsonMessage = NodeForward.request(nodeModel, NodeUrl.Status, "nodeId", nodeModel.getId());
int networkTime = (int) (System.currentTimeMillis() - timeMillis);
JSONObject jsonObject;
if (jsonMessage.success()) {
jsonObject = jsonMessage.getData(JSONObject.class);
} else {
// 状态码错
jsonObject = new JSONObject();
jsonObject.put("status", 3);
jsonObject.put("failureMsg", jsonMessage.toString());
}
jsonObject.put("networkTime", networkTime);
if (nodeTopInfo != null) {
nodeTopInfo.put("networkTime", networkTime);
}
this.save(modelList, nodeTopInfo, jsonObject);
} catch (AuthorizeException agentException) {
this.save(modelList, 2, agentException.getMessage());
} catch (AgentException e) {
this.save(modelList, 1, e.getMessage());
} catch (Exception e) {
this.save(modelList, 1, e.getMessage());
log.error("获取节点监控信息失败", e);
} finally {
BaseServerController.removeEmpty();
}
});
});
}
private void saveSystemMonitor(List<NodeModel> modelList, JSONObject systemMonitor) {
if (systemMonitor != null) {
List<SystemMonitorLog> monitorLogs = modelList.stream().map(nodeModel -> {
SystemMonitorLog log = new SystemMonitorLog();
log.setOccupyMemory(systemMonitor.getDouble("memory"));
log.setOccupyMemoryUsed(systemMonitor.getDouble("memoryUsed"));
log.setOccupyDisk(systemMonitor.getDouble("disk"));
log.setOccupyCpu(systemMonitor.getDouble("cpu"));
log.setMonitorTime(systemMonitor.getLongValue("time"));
log.setNetworkTime(systemMonitor.getIntValue("networkTime"));
log.setNodeId(nodeModel.getId());
return log;
}).collect(Collectors.toList());
//
dbSystemMonitorLogService.insert(monitorLogs);
}
}
/**
* 更新状态 和错误信息
*
* @param modelList 节点
* @param satus 状态
* @param msg 错误消息
*/
private void save(List<NodeModel> modelList, int satus, String msg) {
for (NodeModel nodeModel : modelList) {
NodeStatModel nodeStatModel = this.create(nodeModel);
nodeStatModel.setFailureMsg(msg);
nodeStatModel.setStatus(satus);
this.upsert(nodeStatModel);
}
}
/**
* 报错结果
*
* @param modelList 节点
* @param systemMonitor 系统监控
* @param statusData 状态数据
*/
private void save(List<NodeModel> modelList, JSONObject systemMonitor, JSONObject statusData) {
this.saveSystemMonitor(modelList, systemMonitor);
//
for (NodeModel nodeModel : modelList) {
NodeStatModel nodeStatModel = this.create(nodeModel);
if (nodeModel.isOpenStatus()) {
if (systemMonitor != null) {
nodeStatModel.setOccupyMemory(ObjectUtil.defaultIfNull(systemMonitor.getDouble("memory"), -1D));
nodeStatModel.setOccupyMemoryUsed(ObjectUtil.defaultIfNull(systemMonitor.getDouble("memoryUsed"), -1D));
nodeStatModel.setOccupyDisk(ObjectUtil.defaultIfNull(systemMonitor.getDouble("disk"), -1D));
nodeStatModel.setOccupyCpu(ObjectUtil.defaultIfNull(systemMonitor.getDouble("cpu"), -1D));
}
//
nodeStatModel.setNetworkTime(statusData.getIntValue("networkTime"));
nodeStatModel.setJpomVersion(statusData.getString("jpomVersion"));
nodeStatModel.setOsName(statusData.getString("osName"));
String runTime = statusData.getString("runTime");
String runTimeLong = statusData.getString("runTimeLong");
// 兼容数据
nodeStatModel.setUpTimeStr(StrUtil.emptyToDefault(runTimeLong, runTime));
nodeStatModel.setFailureMsg(StrUtil.emptyToDefault(statusData.getString("failureMsg"), StrUtil.EMPTY));
//
Integer statusInteger = statusData.getInteger("status");
if (statusInteger != null) {
nodeStatModel.setStatus(statusInteger);
} else {
nodeStatModel.setStatus(0);
}
} else {
nodeStatModel.setStatus(4);
nodeStatModel.setFailureMsg("节点禁用中");
}
this.upsert(nodeStatModel);
}
}
private NodeStatModel create(NodeModel model) {
NodeStatModel nodeStatModel = new NodeStatModel();
nodeStatModel.setId(model.getId());
nodeStatModel.setWorkspaceId(model.getWorkspaceId());
nodeStatModel.setName(model.getName());
nodeStatModel.setUrl(model.getUrl());
//
nodeStatModel.setOccupyMemory(-1D);
nodeStatModel.setOccupyMemoryUsed(-1D);
nodeStatModel.setOccupyDisk(-1D);
nodeStatModel.setOccupyCpu(-1D);
nodeStatModel.setGroup(model.getGroup());
nodeStatModel.setNetworkTime(-1);
nodeStatModel.setUpTimeStr(StrUtil.EMPTY);
return nodeStatModel;
}
/**
* 获取节点监控信息
*
* @param reqNode 真实节点
*/
private JSONObject getNodeTopInfo(NodeModel reqNode) {
JsonMessage<?> message = NodeForward.request(reqNode, null, NodeUrl.GetDirectTop);
Object data = message.getData();
if (data == null) {
return null;
}
if (data instanceof JSONObject) {
return (JSONObject) data;
}
log.warn("{} node response msg:{}", reqNode.getName(), message);
return null;
}
}
///*
// * The MIT License (MIT)
// *
// * Copyright (c) 2019 Code Technology Studio
// *
// * Permission is hereby granted, free of charge, to any person obtaining a copy of
// * this software and associated documentation files (the "Software"), to deal in
// * the Software without restriction, including without limitation the rights to
// * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// * the Software, and to permit persons to whom the Software is furnished to do so,
// * subject to the following conditions:
// *
// * The above copyright notice and this permission notice shall be included in all
// * copies or substantial portions of the Software.
// *
// * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// */
//package io.jpom.service.stat;
//
//import cn.hutool.core.collection.CollUtil;
//import cn.hutool.core.date.SystemClock;
//import cn.hutool.core.thread.ThreadUtil;
//import cn.hutool.core.util.ObjectUtil;
//import cn.hutool.core.util.StrUtil;
//import cn.hutool.db.Entity;
//import com.alibaba.fastjson2.JSONObject;
//import io.jpom.common.BaseServerController;
//import io.jpom.common.JsonMessage;
//import io.jpom.common.forward.NodeForward;
//import io.jpom.common.forward.NodeUrl;
//import io.jpom.cron.IAsyncLoad;
//import io.jpom.model.data.NodeModel;
//import io.jpom.model.log.SystemMonitorLog;
//import io.jpom.model.stat.NodeStatModel;
//import io.jpom.model.user.UserModel;
//import io.jpom.service.dblog.DbSystemMonitorLogService;
//import io.jpom.service.h2db.BaseWorkspaceService;
//import io.jpom.service.node.NodeService;
//import io.jpom.system.AgentException;
//import io.jpom.system.AuthorizeException;
//import io.jpom.system.ServerConfig;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.stereotype.Service;
//
//import java.util.Collection;
//import java.util.List;
//import java.util.concurrent.Executors;
//import java.util.concurrent.ScheduledExecutorService;
//import java.util.concurrent.TimeUnit;
//import java.util.stream.Collectors;
//
///**
// * 节点统计 service
// *
// * @author bwcx_jzy
// * @since 2022/1/22
// */
//@Service
//@Slf4j
//public class NodeStatService extends BaseWorkspaceService<NodeStatModel> implements IAsyncLoad, Runnable {
//
// private final DbSystemMonitorLogService dbSystemMonitorLogService;
// private final NodeService nodeService;
// private final ServerConfig.NodeConfig nodeConfig;
//
// public NodeStatService(DbSystemMonitorLogService dbSystemMonitorLogService,
// NodeService nodeService,
// ServerConfig serverConfig) {
// this.dbSystemMonitorLogService = dbSystemMonitorLogService;
// this.nodeService = nodeService;
// this.nodeConfig = serverConfig.getNode();
// }
//
// /**
// * 根据 url 去重
// *
// * @return list
// */
// public List<String> getDeDuplicationByUrl() {
// String sql = "select url from " + super.getTableName() + " group by url";
// List<Entity> query = this.query(sql);
// if (query != null) {
// return query.stream().map((entity -> entity.getStr("url"))).collect(Collectors.toList());
// }
// return null;
// }
//
// @Override
// public void startLoad() {
// // 启动心跳检测
// int heartSecond = nodeConfig.getHeartSecond();
// ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, "Jpom Node Monitor"));
// scheduler.scheduleAtFixedRate(this, 10, heartSecond, TimeUnit.SECONDS);
// // 清理 错误的节点统计数据
// List<String> duplicationByUrl = nodeService.getDeDuplicationByUrl();
// List<String> statUrl = this.getDeDuplicationByUrl();
// Collection<String> subtract = CollUtil.subtract(statUrl, duplicationByUrl);
// //
// for (String s : subtract) {
// NodeStatModel statModel = new NodeStatModel();
// statModel.setUrl(s);
// this.del(this.dataBeanToEntity(statModel));
// }
// }
//
// @Override
// public void run() {
// List<NodeModel> nodeModels = nodeService.listDeDuplicationByUrl();
// //
// this.checkList(nodeModels);
// }
//
//
// private List<NodeModel> getListByUrl(String url) {
// NodeModel nodeModel = new NodeModel();
// nodeModel.setUrl(url);
// return nodeService.listByBean(nodeModel);
// }
//
// private void checkList(List<NodeModel> nodeModels) {
// if (CollUtil.isEmpty(nodeModels)) {
// return;
// }
// nodeModels.forEach(nodeModel -> {
// //
// nodeModel.setName(nodeModel.getUrl());
// List<NodeModel> modelList = this.getListByUrl(nodeModel.getUrl());
// boolean match = modelList.stream().allMatch(NodeModel::isOpenStatus);
// if (!match) {
// // 节点都关闭
// try {
// BaseServerController.resetInfo(UserModel.EMPTY);
// this.save(modelList, 4, "节点禁用中");
// } finally {
// BaseServerController.removeEmpty();
// }
// return;
// }
// nodeModel.setOpenStatus(1);
// nodeModel.setTimeOut(5);
// //
// ThreadUtil.execute(() -> {
// try {
// BaseServerController.resetInfo(UserModel.EMPTY);
// JsonMessage<JSONObject> message = NodeForward.request(reqNode, null, NodeUrl.GetDirectTop);
// JSONObject nodeTopInfo = this.getNodeTopInfo(nodeModel);
// //
// long timeMillis = SystemClock.now();
// JsonMessage<Object> jsonMessage = NodeForward.request(nodeModel, NodeUrl.Status, "nodeId", nodeModel.getId());
// int networkTime = (int) (System.currentTimeMillis() - timeMillis);
// JSONObject jsonObject;
// if (jsonMessage.success()) {
// jsonObject = jsonMessage.getData(JSONObject.class);
// } else {
// // 状态码错
// jsonObject = new JSONObject();
// jsonObject.put("status", 3);
// jsonObject.put("failureMsg", jsonMessage.toString());
// }
// jsonObject.put("networkTime", networkTime);
// if (nodeTopInfo != null) {
// nodeTopInfo.put("networkTime", networkTime);
// }
// this.save(modelList, nodeTopInfo, jsonObject);
// } catch (AuthorizeException agentException) {
// this.save(modelList, 2, agentException.getMessage());
// } catch (AgentException e) {
// this.save(modelList, 1, e.getMessage());
// } catch (Exception e) {
// this.save(modelList, 1, e.getMessage());
// log.error("获取节点监控信息失败", e);
// } finally {
// BaseServerController.removeEmpty();
// }
// });
// });
// }
//
// private void saveSystemMonitor(List<NodeModel> modelList, JSONObject systemMonitor) {
// if (systemMonitor != null) {
// List<SystemMonitorLog> monitorLogs = modelList.stream().map(nodeModel -> {
// SystemMonitorLog log = new SystemMonitorLog();
// log.setOccupyMemory(systemMonitor.getDouble("memory"));
// log.setOccupyMemoryUsed(systemMonitor.getDouble("memoryUsed"));
// log.setOccupyDisk(systemMonitor.getDouble("disk"));
// log.setOccupyCpu(systemMonitor.getDouble("cpu"));
// log.setMonitorTime(systemMonitor.getLongValue("time"));
// log.setNetworkTime(systemMonitor.getIntValue("networkTime"));
// log.setNodeId(nodeModel.getId());
// return log;
// }).collect(Collectors.toList());
// //
// dbSystemMonitorLogService.insert(monitorLogs);
// }
// }
//
// /**
// * 更新状态 和错误信息
// *
// * @param modelList 节点
// * @param satus 状态
// * @param msg 错误消息
// */
// private void save(List<NodeModel> modelList, int satus, String msg) {
// for (NodeModel nodeModel : modelList) {
// NodeStatModel nodeStatModel = this.create(nodeModel);
// nodeStatModel.setFailureMsg(msg);
// nodeStatModel.setStatus(satus);
// this.upsert(nodeStatModel);
// }
// }
//
// /**
// * 报错结果
// *
// * @param modelList 节点
// * @param systemMonitor 系统监控
// * @param statusData 状态数据
// */
// private void save(List<NodeModel> modelList, JSONObject systemMonitor, JSONObject statusData) {
// this.saveSystemMonitor(modelList, systemMonitor);
// //
// for (NodeModel nodeModel : modelList) {
// NodeStatModel nodeStatModel = this.create(nodeModel);
// if (nodeModel.isOpenStatus()) {
// if (systemMonitor != null) {
// nodeStatModel.setOccupyMemory(ObjectUtil.defaultIfNull(systemMonitor.getDouble("memory"), -1D));
// nodeStatModel.setOccupyMemoryUsed(ObjectUtil.defaultIfNull(systemMonitor.getDouble("memoryUsed"), -1D));
// nodeStatModel.setOccupyDisk(ObjectUtil.defaultIfNull(systemMonitor.getDouble("disk"), -1D));
// nodeStatModel.setOccupyCpu(ObjectUtil.defaultIfNull(systemMonitor.getDouble("cpu"), -1D));
// }
// //
// nodeStatModel.setNetworkTime(statusData.getIntValue("networkTime"));
// nodeStatModel.setJpomVersion(statusData.getString("jpomVersion"));
// nodeStatModel.setOsName(statusData.getString("osName"));
// String runTime = statusData.getString("runTime");
// String runTimeLong = statusData.getString("runTimeLong");
// // 兼容数据
// nodeStatModel.setUpTimeStr(StrUtil.emptyToDefault(runTimeLong, runTime));
// nodeStatModel.setFailureMsg(StrUtil.emptyToDefault(statusData.getString("failureMsg"), StrUtil.EMPTY));
// //
// Integer statusInteger = statusData.getInteger("status");
// if (statusInteger != null) {
// nodeStatModel.setStatus(statusInteger);
// } else {
// nodeStatModel.setStatus(0);
// }
// } else {
// nodeStatModel.setStatus(4);
// nodeStatModel.setFailureMsg("节点禁用中");
// }
// this.upsert(nodeStatModel);
// }
// }
//
// private NodeStatModel create(NodeModel model) {
// NodeStatModel nodeStatModel = new NodeStatModel();
// nodeStatModel.setId(model.getId());
// nodeStatModel.setWorkspaceId(model.getWorkspaceId());
// nodeStatModel.setName(model.getName());
// nodeStatModel.setUrl(model.getUrl());
// //
// nodeStatModel.setOccupyMemory(-1D);
// nodeStatModel.setOccupyMemoryUsed(-1D);
// nodeStatModel.setOccupyDisk(-1D);
// nodeStatModel.setOccupyCpu(-1D);
// nodeStatModel.setGroup(model.getGroup());
// nodeStatModel.setNetworkTime(-1);
// nodeStatModel.setUpTimeStr(StrUtil.EMPTY);
// return nodeStatModel;
// }
//}

View File

@ -52,7 +52,7 @@ public class WorkspaceService extends BaseDbService<WorkspaceModel> implements I
if (workspaceModel == null) {
WorkspaceModel defaultWorkspace = new WorkspaceModel();
defaultWorkspace.setId(Const.WORKSPACE_DEFAULT_ID);
defaultWorkspace.setName("默认");
defaultWorkspace.setName(Const.DEFAULT_GROUP_NAME);
defaultWorkspace.setDescription("系统默认的工作空间,不能删除");
super.insert(defaultWorkspace);

View File

@ -31,10 +31,7 @@ import io.jpom.util.SocketSessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import top.jpom.transport.DataContentType;
import top.jpom.transport.IProxyWebSocket;
import top.jpom.transport.IUrlItem;
import top.jpom.transport.TransportServerFactory;
import top.jpom.transport.*;
import java.io.IOException;
import java.net.URISyntaxException;
@ -89,9 +86,10 @@ public abstract class BaseProxyHandler extends BaseHandler {
if (nodeModel != null) {
Object[] parameters = this.getParameters(attributes);
// 连接节点
IUrlItem urlItem = NodeForward.createUrlItem(nodeModel, this.nodeUrl, DataContentType.FORM_URLENCODED);
INodeInfo nodeInfo = NodeForward.parseNodeInfo(nodeModel);
IUrlItem urlItem = NodeForward.parseUrlItem(nodeInfo, nodeModel.getWorkspaceId(), this.nodeUrl, DataContentType.FORM_URLENCODED);
IProxyWebSocket proxySession = TransportServerFactory.get().websocket(nodeModel, urlItem, parameters);
IProxyWebSocket proxySession = TransportServerFactory.get().websocket(nodeInfo, urlItem, parameters);
proxySession.onMessage(s -> sendMsg(session, s));
if (!proxySession.connectBlocking()) {
this.sendMsg(session, "插件端连接失败");

View File

@ -22,7 +22,7 @@
*/
package io.jpom.socket;
import io.jpom.service.node.NodeService;
import io.jpom.func.assets.server.MachineNodeServer;
import io.jpom.service.system.SystemParametersServer;
import io.jpom.socket.handler.*;
import io.jpom.system.ServerConfig;
@ -41,17 +41,17 @@ import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry
public class ServerWebSocketConfig implements WebSocketConfigurer {
private final ServerWebSocketInterceptor serverWebSocketInterceptor;
private final SystemParametersServer systemParametersServer;
private final NodeService nodeService;
private final ServerConfig.NodeConfig nodeConfig;
private final MachineNodeServer machineNodeServer;
public ServerWebSocketConfig(ServerWebSocketInterceptor serverWebSocketInterceptor,
SystemParametersServer systemParametersServer,
NodeService nodeService,
ServerConfig serverConfig) {
ServerConfig serverConfig,
MachineNodeServer machineNodeServer) {
this.serverWebSocketInterceptor = serverWebSocketInterceptor;
this.systemParametersServer = systemParametersServer;
this.nodeService = nodeService;
this.nodeConfig = serverConfig.getNode();
this.machineNodeServer = machineNodeServer;
}
@Override
@ -69,7 +69,7 @@ public class ServerWebSocketConfig implements WebSocketConfigurer {
registry.addHandler(new SshHandler(), "/socket/ssh")
.addInterceptors(serverWebSocketInterceptor).setAllowedOrigins("*");
// 节点升级
registry.addHandler(new NodeUpdateHandler(nodeService, systemParametersServer, nodeConfig), "/socket/node_update")
registry.addHandler(new NodeUpdateHandler(machineNodeServer, systemParametersServer, nodeConfig), "/socket/node_update")
.addInterceptors(serverWebSocketInterceptor).setAllowedOrigins("*");
// 脚本模板
registry.addHandler(new ServerScriptHandler(), "/socket/script_run")

View File

@ -36,25 +36,22 @@ import io.jpom.common.JsonMessage;
import io.jpom.common.Type;
import io.jpom.common.forward.NodeForward;
import io.jpom.common.forward.NodeUrl;
import io.jpom.func.assets.model.MachineNodeModel;
import io.jpom.func.assets.server.MachineNodeServer;
import io.jpom.model.AgentFileModel;
import io.jpom.model.UploadFileModel;
import io.jpom.model.WebSocketMessageModel;
import io.jpom.model.data.NodeModel;
import io.jpom.permission.ClassFeature;
import io.jpom.permission.Feature;
import io.jpom.permission.MethodFeature;
import io.jpom.permission.SystemPermission;
import io.jpom.service.node.NodeService;
import io.jpom.service.system.SystemParametersServer;
import io.jpom.socket.BaseProxyHandler;
import io.jpom.socket.ConsoleCommandOp;
import io.jpom.system.ServerConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.WebSocketSession;
import top.jpom.transport.DataContentType;
import top.jpom.transport.IProxyWebSocket;
import top.jpom.transport.IUrlItem;
import top.jpom.transport.TransportServerFactory;
import top.jpom.transport.*;
import java.io.File;
import java.io.FileInputStream;
@ -85,14 +82,14 @@ public class NodeUpdateHandler extends BaseProxyHandler {
private static final int INIT_WAIT = 10 * 1000;
private final SystemParametersServer systemParametersServer;
private final NodeService nodeService;
private final MachineNodeServer machineNodeServer;
private final ServerConfig.NodeConfig nodeConfig;
public NodeUpdateHandler(NodeService nodeService,
public NodeUpdateHandler(MachineNodeServer machineNodeServer,
SystemParametersServer systemParametersServer,
ServerConfig.NodeConfig nodeConfig) {
super(null);
this.nodeService = nodeService;
this.machineNodeServer = machineNodeServer;
this.systemParametersServer = systemParametersServer;
this.nodeConfig = nodeConfig;
//systemParametersServer = SpringUtil.getBean(SystemParametersServer.class);
@ -117,16 +114,17 @@ public class NodeUpdateHandler extends BaseProxyHandler {
private void pullNodeList(WebSocketSession session, String ids) {
List<String> split = StrUtil.split(ids, StrUtil.COMMA);
List<NodeModel> nodeModelList = nodeService.listById(split);
List<MachineNodeModel> nodeModelList = machineNodeServer.listById(split);
if (nodeModelList == null) {
this.onError(session, "没有查询到节点信息:" + ids);
return;
}
for (NodeModel model : nodeModelList) {
for (MachineNodeModel model : nodeModelList) {
IProxyWebSocket nodeClient = clientMap.computeIfAbsent(model.getId(), s -> {
IUrlItem urlItem = NodeForward.createUrlItem(model, NodeUrl.NodeUpdate, DataContentType.FORM_URLENCODED);
INodeInfo nodeInfo = NodeForward.coverNodeInfo(model);
IUrlItem urlItem = NodeForward.parseUrlItem(model, StrUtil.EMPTY, NodeUrl.NodeUpdate, DataContentType.FORM_URLENCODED);
IProxyWebSocket proxySession = TransportServerFactory.get().websocket(model, urlItem);
IProxyWebSocket proxySession = TransportServerFactory.get().websocket(nodeInfo, urlItem);
proxySession.onMessage(msg -> sendMsg(session, msg));
return proxySession;
});
@ -241,7 +239,7 @@ public class NodeUpdateHandler extends BaseProxyHandler {
}
for (int i = 0; i < ids.size(); i++) {
String id = ids.getString(i);
NodeModel node = nodeService.getByKey(id);
MachineNodeModel node = machineNodeServer.getByKey(id);
if (node == null) {
this.onError(session, "没有对应的节点:" + id);
continue;
@ -254,24 +252,24 @@ public class NodeUpdateHandler extends BaseProxyHandler {
}
}
private boolean updateNodeItemHttp(NodeModel nodeModel, WebSocketSession session, AgentFileModel agentFileModel) throws IOException {
private boolean updateNodeItemHttp(MachineNodeModel machineNodeModel, WebSocketSession session, AgentFileModel agentFileModel) throws IOException {
File file = FileUtil.file(agentFileModel.getSavePath());
JsonMessage<String> message = NodeForward.requestSharding(nodeModel, NodeUrl.SystemUploadJar, new JSONObject(),
JsonMessage<String> message = NodeForward.requestSharding(machineNodeModel, NodeUrl.SystemUploadJar, new JSONObject(),
file,
jsonObject -> NodeForward.request(nodeModel, NodeUrl.SystemUploadJarMerge, jsonObject),
jsonObject -> NodeForward.request(machineNodeModel, NodeUrl.SystemUploadJarMerge, jsonObject),
(total, progressSize) -> {
UploadFileModel uploadFileModel = new UploadFileModel();
uploadFileModel.setSize(total);
uploadFileModel.setCompleteSize(progressSize);
uploadFileModel.setId(nodeModel.getId());
uploadFileModel.setId(machineNodeModel.getId());
uploadFileModel.setVersion(agentFileModel.getVersion());
// 更新进度
WebSocketMessageModel model = new WebSocketMessageModel("updateNode", nodeModel.getId());
WebSocketMessageModel model = new WebSocketMessageModel("updateNode", machineNodeModel.getId());
model.setData(uploadFileModel);
NodeUpdateHandler.this.sendMsg(model, session);
});
String id = nodeModel.getId();
String id = machineNodeModel.getId();
WebSocketMessageModel callbackRestartMessage = new WebSocketMessageModel("restart", id);
callbackRestartMessage.setData(message.getMsg());
this.sendMsg(callbackRestartMessage, session);
@ -286,7 +284,7 @@ public class NodeUpdateHandler extends BaseProxyHandler {
++retryCount;
try {
ThreadUtil.sleep(1000L);
JsonMessage<Object> jsonMessage = NodeForward.request(nodeModel, NodeUrl.Info, "nodeId", id);
JsonMessage<Object> jsonMessage = NodeForward.request(machineNodeModel, StrUtil.EMPTY, NodeUrl.Info, "nodeId", id);
if (jsonMessage.success()) {
this.sendMsg(callbackRestartMessage.setData("重启完成"), session);
return true;
@ -347,7 +345,7 @@ public class NodeUpdateHandler extends BaseProxyHandler {
return false;
}
private void updateNodeItem(String id, NodeModel node, WebSocketSession session, AgentFileModel agentFileModel, boolean http) {
private void updateNodeItem(String id, MachineNodeModel node, WebSocketSession session, AgentFileModel agentFileModel, boolean http) {
try {
IProxyWebSocket client = clientMap.get(node.getId());
if (client == null) {

View File

@ -0,0 +1,64 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Code Technology Studio
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.jpom.system.db;
import cn.hutool.extra.spring.SpringUtil;
import io.jpom.common.ILoadEvent;
import io.jpom.service.h2db.BaseGroupService;
import io.jpom.service.h2db.BaseNodeGroupService;
import io.jpom.service.h2db.BaseNodeService;
import org.springframework.context.ApplicationContext;
import java.util.Map;
/**
* 数据库初始化完成后
*
* @author bwcx_jzy
* @since 2023/2/18
*/
public class DataInitEvent implements ILoadEvent {
@Override
public void afterPropertiesSet(ApplicationContext applicationContext) throws Exception {
//
Map<String, BaseGroupService> groupServiceMap = SpringUtil.getApplicationContext().getBeansOfType(BaseGroupService.class);
for (BaseGroupService<?> value : groupServiceMap.values()) {
value.repairGroupFiled();
}
//
Map<String, BaseNodeGroupService> nodeGroupServiceMap = SpringUtil.getApplicationContext().getBeansOfType(BaseNodeGroupService.class);
for (BaseNodeGroupService<?> value : nodeGroupServiceMap.values()) {
value.repairGroupFiled();
}
// 同步项目
Map<String, BaseNodeService> beansOfType = SpringUtil.getApplicationContext().getBeansOfType(BaseNodeService.class);
for (BaseNodeService<?> value : beansOfType.values()) {
value.syncAllNode();
}
}
@Override
public int getOrder() {
return HIGHEST_PRECEDENCE + 2;
}
}

View File

@ -32,14 +32,10 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.db.Db;
import cn.hutool.db.ds.DSFactory;
import cn.hutool.extra.spring.SpringUtil;
import io.jpom.common.ILoadEvent;
import io.jpom.common.JpomApplicationEvent;
import io.jpom.model.data.BackupInfoModel;
import io.jpom.service.dblog.BackupInfoService;
import io.jpom.service.h2db.BaseGroupService;
import io.jpom.service.h2db.BaseNodeGroupService;
import io.jpom.service.h2db.BaseNodeService;
import io.jpom.system.JpomRuntimeException;
import lombok.Lombok;
import lombok.extern.slf4j.Slf4j;
@ -171,21 +167,6 @@ public class InitDb implements DisposableBean, ILoadEvent {
throw Lombok.sneakyThrow(e);
}
log.info("{} db Successfully loaded, url is 【{}】", storageService.mode(), storageService.dbUrl());
//
Map<String, BaseGroupService> groupServiceMap = SpringUtil.getApplicationContext().getBeansOfType(BaseGroupService.class);
for (BaseGroupService<?> value : groupServiceMap.values()) {
value.repairGroupFiled();
}
//
Map<String, BaseNodeGroupService> nodeGroupServiceMap = SpringUtil.getApplicationContext().getBeansOfType(BaseNodeGroupService.class);
for (BaseNodeGroupService<?> value : nodeGroupServiceMap.values()) {
value.repairGroupFiled();
}
// 同步项目
Map<String, BaseNodeService> beansOfType = SpringUtil.getApplicationContext().getBeansOfType(BaseNodeService.class);
for (BaseNodeService<?> value : beansOfType.values()) {
value.syncAllNode();
}
}

View File

@ -26,7 +26,7 @@ import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import io.jpom.common.ILoadEvent;
import io.jpom.model.data.NodeModel;
import io.jpom.common.forward.NodeForward;
import io.jpom.service.system.SystemParametersServer;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@ -79,7 +79,7 @@ public class ProxySelectorConfig extends ProxySelector implements ILoadEvent {
}
return StrUtil.containsIgnoreCase(url, proxyConfigItem.getPattern());
})
.map(proxyConfigItem -> NodeModel.crateProxy(proxyConfigItem.getProxyType(), proxyConfigItem.getProxyAddress()))
.map(proxyConfigItem -> NodeForward.crateProxy(proxyConfigItem.getProxyType(), proxyConfigItem.getProxyAddress()))
.filter(Objects::nonNull)
.findFirst()
.map(Collections::singletonList)

View File

@ -1,131 +1,131 @@
[
{
"title": "节点管理",
"icon_v3": "block",
"id": "nodeManager",
"childs": [
{
"title": "节点列表",
"id": "nodeList"
},
{
"title": "节点统计",
"id": "nodeStat"
}
]
},
{
"title": "项目管理",
"icon_v3": "apartment",
"id": "outgiving",
"childs": [
{
"title": "项目列表",
"id": "projectSearch"
},
{
"id": "outgivingList",
"title": "分发列表"
},
{
"id": "outgivingLog",
"title": "分发日志"
},
{
"id": "logRead",
"title": "日志阅读"
}
]
},
{
"title": "在线构建",
"icon_v3": "build",
"id": "build",
"childs": [
{
"id": "repository",
"title": "仓库信息"
},
{
"id": "buildList",
"title": "构建列表"
},
{
"id": "buildHistory",
"title": "构建历史"
}
]
},
{
"title": "SSH管理",
"icon_v3": "code",
"id": "sshManager",
"childs": [
{
"title": "SSH列表",
"id": "sshList"
},
{
"title": "命令管理",
"id": "commandList"
},
{
"title": "命令执行记录",
"id": "commandLogList"
}
]
},
{
"title": "脚本管理",
"icon_v3": "file-text",
"id": "scriptManager",
"childs": [
{
"title": "节点脚本列表",
"id": "scriptAllList"
},
{
"title": "脚本列表",
"id": "serverScriptList"
},
{
"title": "脚本执行记录",
"id": "serverScriptLogList"
}
]
},
{
"title": "Docker管理",
"icon_v3": "cloud-server",
"id": "dockerManager",
"childs": [
{
"title": "Docker列表",
"id": "dockerList"
},
{
"title": "集群列表",
"id": "dockerSwarm"
}
]
},
{
"title": "监控管理",
"icon_v3": "monitor",
"id": "monitor",
"childs": [
{
"id": "monitorList",
"title": "监控列表"
},
{
"id": "monitorLog",
"title": "监控日志"
},
{
"id": "userOptLog",
"title": "操作监控"
}
]
}
{
"title": "节点管理",
"icon_v3": "block",
"id": "nodeManager",
"childs": [
{
"title": "逻辑节点",
"id": "nodeList"
},
{
"title": "节点统计",
"id": "nodeStat"
}
]
},
{
"title": "项目管理",
"icon_v3": "apartment",
"id": "outgiving",
"childs": [
{
"title": "项目列表",
"id": "projectSearch"
},
{
"id": "outgivingList",
"title": "分发列表"
},
{
"id": "outgivingLog",
"title": "分发日志"
},
{
"id": "logRead",
"title": "日志阅读"
}
]
},
{
"title": "在线构建",
"icon_v3": "build",
"id": "build",
"childs": [
{
"id": "repository",
"title": "仓库信息"
},
{
"id": "buildList",
"title": "构建列表"
},
{
"id": "buildHistory",
"title": "构建历史"
}
]
},
{
"title": "SSH管理",
"icon_v3": "code",
"id": "sshManager",
"childs": [
{
"title": "SSH列表",
"id": "sshList"
},
{
"title": "命令管理",
"id": "commandList"
},
{
"title": "命令执行记录",
"id": "commandLogList"
}
]
},
{
"title": "脚本管理",
"icon_v3": "file-text",
"id": "scriptManager",
"childs": [
{
"title": "节点脚本列表",
"id": "scriptAllList"
},
{
"title": "脚本列表",
"id": "serverScriptList"
},
{
"title": "脚本执行记录",
"id": "serverScriptLogList"
}
]
},
{
"title": "Docker管理",
"icon_v3": "cloud-server",
"id": "dockerManager",
"childs": [
{
"title": "Docker列表",
"id": "dockerList"
},
{
"title": "集群列表",
"id": "dockerSwarm"
}
]
},
{
"title": "监控管理",
"icon_v3": "monitor",
"id": "monitor",
"childs": [
{
"id": "monitorList",
"title": "监控列表"
},
{
"id": "monitorLog",
"title": "监控日志"
},
{
"id": "userOptLog",
"title": "操作监控"
}
]
}
]

View File

@ -1,89 +1,101 @@
[
{
"title": "配置管理",
"icon_v3": "save",
"id": "config",
"childs": [
{
"id": "monitorConfigEmail",
"title": "邮箱配置",
"role": "system"
},
{
"id": "outgivingWhitelistDirectory",
"title": "白名单配置",
"role": "system"
},
{
"id": "systemExtConfig",
"title": "系统配置目录",
"role": "system"
}
]
},
{
"title": "用户管理",
"icon_v3": "user",
"id": "user",
"childs": [
{
"id": "userList",
"title": "用户列表",
"role": "system"
},
{
"id": "permission_group",
"title": "权限组",
"role": "system"
},
{
"id": "user_log",
"title": "操作日志",
"role": "system"
}
]
},
{
"title": "系统管理",
"icon_v3": "setting",
"id": "setting",
"childs": [
{
"id": "workspace",
"title": "工作空间",
"role": "system"
},
{
"id": "globalEnv",
"title": "全局变量",
"role": "system"
},
{
"id": "cacheManage",
"title": "缓存管理",
"role": "system"
},
{
"id": "logManage",
"title": "系统日志",
"role": "system"
},
{
"id": "update",
"title": "在线升级",
"role": "sys"
},
{
"id": "sysConfig",
"title": "系统配置",
"role": "system"
},
{
"id": "backup",
"title": "数据库备份",
"role": "system",
"storageMode": "H2"
}
]
}
{
"title": "资产管理",
"icon_v3": "hdd",
"id": "assets-manager",
"childs": [
{
"id": "machine_node_info",
"title": "机器管理",
"role": "system"
}
]
},
{
"title": "配置管理",
"icon_v3": "save",
"id": "config",
"childs": [
{
"id": "monitorConfigEmail",
"title": "邮箱配置",
"role": "system"
},
{
"id": "outgivingWhitelistDirectory",
"title": "白名单配置",
"role": "system"
},
{
"id": "systemExtConfig",
"title": "系统配置目录",
"role": "system"
}
]
},
{
"title": "用户管理",
"icon_v3": "user",
"id": "user",
"childs": [
{
"id": "userList",
"title": "用户列表",
"role": "system"
},
{
"id": "permission_group",
"title": "权限组",
"role": "system"
},
{
"id": "user_log",
"title": "操作日志",
"role": "system"
}
]
},
{
"title": "系统管理",
"icon_v3": "setting",
"id": "setting",
"childs": [
{
"id": "workspace",
"title": "工作空间",
"role": "system"
},
{
"id": "globalEnv",
"title": "全局变量",
"role": "system"
},
{
"id": "cacheManage",
"title": "缓存管理",
"role": "system"
},
{
"id": "logManage",
"title": "系统日志",
"role": "system"
},
{
"id": "update",
"title": "在线升级",
"role": "sys"
},
{
"id": "sysConfig",
"title": "系统配置",
"role": "system"
},
{
"id": "backup",
"title": "数据库备份",
"role": "system",
"storageMode": "H2"
}
]
}
]

View File

@ -557,18 +557,34 @@ public abstract class BaseDbCommonService<T> {
* @return data
*/
public List<T> listByBean(T data) {
return this.listByBean(data, true);
}
/**
* 查询实体对象
*
* @param data 实体
* @return data
*/
public List<T> listByBean(T data, boolean fill) {
Entity where = this.dataBeanToEntity(data);
List<Entity> entitys = this.queryList(where);
return this.entityToBeanList(entitys);
return this.entityToBeanList(entitys, fill);
}
public List<T> entityToBeanList(List<Entity> entitys) {
return this.entityToBeanList(entitys, true);
}
public List<T> entityToBeanList(List<Entity> entitys, boolean fill) {
if (entitys == null) {
return null;
}
return entitys.stream().map((entity -> {
T entityToBean = this.entityToBean(entity, this.tClass);
this.fillSelectResult(entityToBean);
if (fill) {
this.fillSelectResult(entityToBean);
}
return entityToBean;
})).collect(Collectors.toList());
}

View File

@ -4,3 +4,5 @@ ADD,OUT_GIVING,group,String,50,,项目分组
ADD,BUILD_INFO,buildEnvParameter,TEXT,,,构建环境变量
ADD,BUILDHISTORYLOG,buildEnvCache,TEXT,,,构建环境变量
ADD,OUT_GIVING,webhook,String,255,,webhook
DROP,NODE_INFO,unLockType
ADD,NODE_INFO,machineId,String,50,,机器id

1 alterType,tableName,name,type,len,defaultValue,comment,notNull
4 ADD,BUILD_INFO,buildEnvParameter,TEXT,,,构建环境变量
5 ADD,BUILDHISTORYLOG,buildEnvCache,TEXT,,,构建环境变量
6 ADD,OUT_GIVING,webhook,String,255,,webhook
7 DROP,NODE_INFO,unLockType
8 ADD,NODE_INFO,machineId,String,50,,机器id

View File

@ -0,0 +1,52 @@
tableName,name,type,len,defaultValue,notNull,primaryKey,comment,tableComment
MACHINE_NODE_INFO,id,String,50,,true,true,id,机器节点信息
MACHINE_NODE_INFO,createTimeMillis,Long,,,false,false,数据创建时间,
MACHINE_NODE_INFO,modifyTimeMillis,Long,,,false,false,数据修改时间,
MACHINE_NODE_INFO,modifyUser,String,50,,false,false,修改人,
MACHINE_NODE_INFO,strike,Integer,,0,false,false,逻辑删除{1删除0 未删除(默认)},
MACHINE_NODE_INFO,name,String,50,,true,false,机器名称,
MACHINE_NODE_INFO,groupName,String,50,,true,false,分组名称,
MACHINE_NODE_INFO,hostName,String,255,,false,false,机器主机名,
MACHINE_NODE_INFO,hostIpv4s,TEXT,,,false,false,机器所有 IP,
MACHINE_NODE_INFO,osLoadAverage,String,100,,false,false,系统负载,
MACHINE_NODE_INFO,osSystemUptime,Long,,,false,false,系统运行时间(自启动以来的时间),
MACHINE_NODE_INFO,osVersion,String,255,,false,false,系统版本,
MACHINE_NODE_INFO,osHardwareVersion,String,255,,false,false,硬件版本,
MACHINE_NODE_INFO,osCpuCores,Integer,,,false,false,CPU数,
MACHINE_NODE_INFO,osMoneyTotal,Long,,,false,false,总内存,
MACHINE_NODE_INFO,osFileStoreTotal,Long,,,false,false,硬盘大小,
MACHINE_NODE_INFO,osCpuIdentifierName,String,255,,false,false,CPU 型号,
MACHINE_NODE_INFO,osName,String,50,,false,false,系统名称,
MACHINE_NODE_INFO,status,TINYINT,,,true,false,节点连接状态0 未连接1 连接中,
MACHINE_NODE_INFO,statusMsg,TEXT,,,false,false,状态消息,
MACHINE_NODE_INFO,transportMode,TINYINT,,,true,false,传输方式。0 服务器拉取1 节点机器推送,
MACHINE_NODE_INFO,jpomUrl,String,100,,false,false,节点 url IP:PORT,
MACHINE_NODE_INFO,jpomUsername,String,100,,false,false,节点登录名,
MACHINE_NODE_INFO,jpomPassword,String,100,,false,false,节点密码,
MACHINE_NODE_INFO,jpomProtocol,String,10,,false,false,协议 http https,
MACHINE_NODE_INFO,jpomTimeout,Integer,,,false,false,节点超时时间,
MACHINE_NODE_INFO,jpomHttpProxy,String,200,,false,false,http 代理,
MACHINE_NODE_INFO,jpomHttpProxyType,String,20,,false,false,http 代理类型,
MACHINE_NODE_INFO,jpomVersion,String,50,,false,false,jpom版本号,
MACHINE_NODE_INFO,jpomUptime,Long,,,false,false,jpom运行时间,
MACHINE_NODE_INFO,jpomBuildTime,String,50,,false,false,Jpom 打包时间,
MACHINE_NODE_INFO,jpomProjectCount,Integer,,,false,false,jpom项目数,
MACHINE_NODE_INFO,jpomScriptCount,Integer,,,false,false,jpom脚本数,
MACHINE_NODE_INFO,networkDelay,Integer,,,false,false,网络耗时(延迟),
MACHINE_NODE_INFO,javaVersion,String,50,,false,false,java版本,
MACHINE_NODE_INFO,jvmTotalMemory,Long,,,false,false,jvm 总内存,
MACHINE_NODE_INFO,jvmFreeMemory,Long,,,false,false,jvm 剩余内存,
MACHINE_NODE_INFO,osOccupyCpu,Double,,,false,false,占用cpu,
MACHINE_NODE_INFO,osOccupyMemory,Double,,,false,false,占用内存,
MACHINE_NODE_INFO,osOccupyDisk,Double,,,false,false,占用磁盘,
MACHINE_NODE_STAT_LOG,id,String,50,,true,true,id,资产机器节点统计
MACHINE_NODE_STAT_LOG,createTimeMillis,Long,,,false,false,数据创建时间,
MACHINE_NODE_STAT_LOG,modifyTimeMillis,Long,,,false,false,数据修改时间,
MACHINE_NODE_STAT_LOG,machineId,String,50,,true,true,机器id,
MACHINE_NODE_STAT_LOG,occupyCpu,Double,,,false,false,占用cpu,
MACHINE_NODE_STAT_LOG,occupyMemory,Double,,,false,false,占用内存,
MACHINE_NODE_STAT_LOG,occupyDisk,Double,,,false,false,占用磁盘,
MACHINE_NODE_STAT_LOG,networkDelay,Integer,,0,false,false,网络耗时,
MACHINE_NODE_STAT_LOG,monitorTime,Long,,,true,false,监控的时间,
MACHINE_NODE_STAT_LOG,netTxBytes,Long,,,false,false,"每秒发送的KB数,rxkB/s",
MACHINE_NODE_STAT_LOG,netRxBytes,Long,,,false,false,"每秒接收的KB数,rxkB/s",
1 tableName name type len defaultValue notNull primaryKey comment tableComment
2 MACHINE_NODE_INFO id String 50 true true id 机器节点信息
3 MACHINE_NODE_INFO createTimeMillis Long false false 数据创建时间
4 MACHINE_NODE_INFO modifyTimeMillis Long false false 数据修改时间
5 MACHINE_NODE_INFO modifyUser String 50 false false 修改人
6 MACHINE_NODE_INFO strike Integer 0 false false 逻辑删除{1,删除,0 未删除(默认)}
7 MACHINE_NODE_INFO name String 50 true false 机器名称
8 MACHINE_NODE_INFO groupName String 50 true false 分组名称
9 MACHINE_NODE_INFO hostName String 255 false false 机器主机名
10 MACHINE_NODE_INFO hostIpv4s TEXT false false 机器所有 IP
11 MACHINE_NODE_INFO osLoadAverage String 100 false false 系统负载
12 MACHINE_NODE_INFO osSystemUptime Long false false 系统运行时间(自启动以来的时间)
13 MACHINE_NODE_INFO osVersion String 255 false false 系统版本
14 MACHINE_NODE_INFO osHardwareVersion String 255 false false 硬件版本
15 MACHINE_NODE_INFO osCpuCores Integer false false CPU数
16 MACHINE_NODE_INFO osMoneyTotal Long false false 总内存
17 MACHINE_NODE_INFO osFileStoreTotal Long false false 硬盘大小
18 MACHINE_NODE_INFO osCpuIdentifierName String 255 false false CPU 型号
19 MACHINE_NODE_INFO osName String 50 false false 系统名称
20 MACHINE_NODE_INFO status TINYINT true false 节点连接状态:0 未连接,1 连接中
21 MACHINE_NODE_INFO statusMsg TEXT false false 状态消息
22 MACHINE_NODE_INFO transportMode TINYINT true false 传输方式。0 服务器拉取,1 节点机器推送
23 MACHINE_NODE_INFO jpomUrl String 100 false false 节点 url IP:PORT
24 MACHINE_NODE_INFO jpomUsername String 100 false false 节点登录名
25 MACHINE_NODE_INFO jpomPassword String 100 false false 节点密码
26 MACHINE_NODE_INFO jpomProtocol String 10 false false 协议 http https
27 MACHINE_NODE_INFO jpomTimeout Integer false false 节点超时时间
28 MACHINE_NODE_INFO jpomHttpProxy String 200 false false http 代理
29 MACHINE_NODE_INFO jpomHttpProxyType String 20 false false http 代理类型
30 MACHINE_NODE_INFO jpomVersion String 50 false false jpom版本号
31 MACHINE_NODE_INFO jpomUptime Long false false jpom运行时间
32 MACHINE_NODE_INFO jpomBuildTime String 50 false false Jpom 打包时间
33 MACHINE_NODE_INFO jpomProjectCount Integer false false jpom项目数
34 MACHINE_NODE_INFO jpomScriptCount Integer false false jpom脚本数
35 MACHINE_NODE_INFO networkDelay Integer false false 网络耗时(延迟)
36 MACHINE_NODE_INFO javaVersion String 50 false false java版本
37 MACHINE_NODE_INFO jvmTotalMemory Long false false jvm 总内存
38 MACHINE_NODE_INFO jvmFreeMemory Long false false jvm 剩余内存
39 MACHINE_NODE_INFO osOccupyCpu Double false false 占用cpu
40 MACHINE_NODE_INFO osOccupyMemory Double false false 占用内存
41 MACHINE_NODE_INFO osOccupyDisk Double false false 占用磁盘
42 MACHINE_NODE_STAT_LOG id String 50 true true id 资产机器节点统计
43 MACHINE_NODE_STAT_LOG createTimeMillis Long false false 数据创建时间
44 MACHINE_NODE_STAT_LOG modifyTimeMillis Long false false 数据修改时间
45 MACHINE_NODE_STAT_LOG machineId String 50 true true 机器id
46 MACHINE_NODE_STAT_LOG occupyCpu Double false false 占用cpu
47 MACHINE_NODE_STAT_LOG occupyMemory Double false false 占用内存
48 MACHINE_NODE_STAT_LOG occupyDisk Double false false 占用磁盘
49 MACHINE_NODE_STAT_LOG networkDelay Integer 0 false false 网络耗时
50 MACHINE_NODE_STAT_LOG monitorTime Long true false 监控的时间
51 MACHINE_NODE_STAT_LOG netTxBytes Long false false 每秒发送的KB数,rxkB/s
52 MACHINE_NODE_STAT_LOG netRxBytes Long false false 每秒接收的KB数,rxkB/s

View File

@ -1,29 +1,29 @@
import axios from "./config";
// import axios from "./config";
import { parseTime, formatPercent2 } from "@/utils/const";
import echarts from "echarts";
// node 列表
export function getStatist(params) {
return axios({
url: "/node/stat/list_data.json",
method: "post",
params: params,
headers: {
loading: "no",
},
});
}
// // node 列表
// export function getStatist(params) {
// return axios({
// url: "/node/stat/list_data.json",
// method: "post",
// params: params,
// headers: {
// loading: "no",
// },
// });
// }
// node 列表
export function statusStat() {
return axios({
url: "/node/stat/status_stat.json",
method: "get",
headers: {
loading: "no",
},
});
}
// // node 列表
// export function statusStat() {
// return axios({
// url: "/node/stat/status_stat.json",
// method: "get",
// headers: {
// loading: "no",
// },
// });
// }
const defaultData = {
title: {
@ -163,7 +163,7 @@ export function generateNodeNetworkTimeChart(data) {
const scales = [];
for (var i = data.length - 1; i >= 0; i--) {
const item = data[i];
dataArray.data.push(parseFloat(item.networkTime));
dataArray.data.push(parseFloat(item.networkDelay));
scales.push(parseTime(item.monitorTime));
}
@ -209,10 +209,10 @@ export function drawChart(data, domId, parseFn) {
return historyChart;
}
export const status = {
1: "无法连接",
0: "正常",
2: "授权信息错误",
3: "状态码错误",
4: "关闭中",
};
// export const status = {
// 1: "无法连接",
// 0: "正常",
// 2: "授权信息错误",
// 3: "状态码错误",
// 4: "关闭中",
// };

View File

@ -43,14 +43,14 @@ export function getNodeListWithVersion(params) {
});
}
// node 状态
export function getNodeStatus(nodeId) {
return axios({
url: "/node/node_status",
method: "post",
data: { nodeId },
});
}
// // node 状态
// export function getNodeStatus(nodeId) {
// return axios({
// url: "/node/node_status",
// method: "post",
// data: { nodeId },
// });
// }
// 节点 + 项目列表
export function getNodeProjectList(params) {
@ -88,14 +88,6 @@ export function syncProject(nodeId) {
});
}
export function unLockWorkspace(params) {
return axios({
url: "/node/un_lock_workspace",
method: "get",
params: params,
});
}
export function syncToWorkspace(params) {
return axios({
url: "/node/sync-to-workspace",

View File

@ -0,0 +1,34 @@
import axios from "@/api/config";
// 机器 列表
export function machineListData(params) {
return axios({
url: "/system/assets/machine/list-data",
method: "post",
params: params,
});
}
export function machineListGroup(params) {
return axios({
url: "/system/assets/machine/list-group",
method: "get",
params: params,
});
}
// 编辑机器
export function machineEdit(params) {
return axios({
url: "/system/assets/machine/edit",
method: "post",
params: params,
});
}
export const statusMap = {
0: "无法连接",
1: "正常",
2: "授权信息错误",
3: "状态码错误",
};

View File

@ -1,20 +1,17 @@
<template>
<div class="full-content">
<!-- <div ref="filter" class="filter"></div> -->
<!-- 表格 :scroll="{ x: 1070, y: tableHeight -60 }" scroll expandedRowRender 不兼容没法同时使用不然会多出一行数据-->
<a-table :columns="columns" :data-source="list" bordered size="middle" rowKey="id" @expand="expand" :pagination="pagination" @change="changePage" :row-selection="rowSelection">
<a-table :columns="columns" :data-source="list" bordered size="middle" rowKey="id" :pagination="pagination" @change="changePage" :row-selection="rowSelection">
<template slot="title">
<a-space>
<a-input v-model="listQuery['%id%']" @pressEnter="loadData" placeholder="节点ID" />
<a-input v-model="listQuery['%name%']" @pressEnter="loadData" placeholder="节点名称" />
<a-input v-model="listQuery['%url%']" @pressEnter="loadData" placeholder="节点地址" />
<a-select show-search option-filter-prop="children" v-model="listQuery.group" allowClear placeholder="分组" class="search-input-item">
<a-select-option v-for="item in groupList" :key="item">{{ item }}</a-select-option>
</a-select>
<a-tooltip title="按住 Ctr 或者 Alt/Option 键点击按钮快速回到第一页">
<a-button :loading="loading" type="primary" @click="loadData">搜索</a-button>
</a-tooltip>
<a-button type="primary jpom-node-manage-add" @click="handleAdd">新增</a-button>
<a-dropdown>
<a class="ant-dropdown-link" @click="(e) => e.preventDefault()"> 更多 <a-icon type="down" /> </a>
<a-menu slot="overlay">
@ -49,15 +46,13 @@
</a-space>
</template>
<a-tooltip slot="url" slot-scope="text, record" placement="topLeft" :title="text">
<span>{{ record.protocol }}://{{ text }}</span>
<template v-if="record.machineNodeData">
<span>{{ record.machineNodeData.jpomProtocol }}://{{ record.machineNodeData.jpomUrl }}</span>
</template>
<span v-else> - </span>
</a-tooltip>
<template slot="name" slot-scope="text, record">
<template v-if="record.unLockType">
<a-tooltip :title="`${text}`">
<span>{{ text }}</span>
</a-tooltip>
</template>
<template v-else-if="record.openStatus !== 1">
<template v-if="record.openStatus !== 1">
<a-tooltip :title="`${text}`">
<span>{{ text }}</span>
</a-tooltip>
@ -79,15 +74,52 @@
</template>
</template>
</template>
<!-- <a-tooltip slot="cycle" slot-scope="text" placement="topLeft" :title="nodeMonitorCycle[text]">
<span>{{ nodeMonitorCycle[text] }}</span>
</a-tooltip> -->
<a-tooltip slot="status" slot-scope="text, item" placement="topLeft" :title="statusMap[item.machineNodeData && item.machineNodeData.status] || '未知'">
<!-- <span>{{ statusMap[item.machineNodeData && item.machineNodeData.status] || "未知" }}</span> -->
<a-tag :color="item.machineNodeData && item.machineNodeData.status === 1 ? 'green' : 'pink'" style="margin-right: 0px">
{{ statusMap[item.machineNodeData && item.machineNodeData.status] || "未知" }}
</a-tag>
</a-tooltip>
<a-tooltip slot="osName" slot-scope="text, item" placement="topLeft" :title="text">
<span>{{ item.machineNodeData && item.machineNodeData.osName }}</span>
</a-tooltip>
<a-tooltip slot="javaVersion" slot-scope="text, item" placement="topLeft" :title="text">
<span>{{ item.machineNodeData && item.machineNodeData.javaVersion }}</span>
</a-tooltip>
<a-tooltip slot="totalMemory" slot-scope="text, item" placement="topLeft" :title="renderSize(item.machineNodeData && item.machineNodeData.jvmTotalMemory)">
<span>{{ renderSize(item.machineNodeData && item.machineNodeData.jvmTotalMemory) }}</span>
</a-tooltip>
<a-tooltip slot="freeMemory" slot-scope="text" placement="topLeft" :title="renderSize(text)">
<span>{{ renderSize(text) }}</span>
</a-tooltip>
<a-tooltip slot="runTime" slot-scope="text, item" placement="topLeft" :title="formatDuration(item.machineNodeData && item.machineNodeData.jpomUptime)">
<span>{{ formatDuration(item.machineNodeData && item.machineNodeData.jpomUptime, "", 2) }}</span>
</a-tooltip>
<template slot="projectCount" slot-scope="text, item">
<div v-if="item.machineNodeData" @click="syncNode(item)">
<a-tooltip placement="topLeft" title="节点中的所有项目数量,点击重新同步节点项目信息">
<a-tag>{{ item.machineNodeData.jpomProjectCount }} </a-tag>
<a-icon type="sync" />
</a-tooltip>
</div>
<span v-else>0</span>
</template>
<template slot="scriptCount" slot-scope="text, item">
<div v-if="item.machineNodeData" @click="syncNodeScript(item)">
<a-tooltip placement="topLeft" title="节点中的所有脚本模版数量,点击重新同步脚本模版信息">
<a-tag>{{ item.machineNodeData.jpomScriptCount }} </a-tag>
<a-icon type="sync" />
</a-tooltip>
</div>
<span v-else>0</span>
</template>
<template slot="operation" slot-scope="text, record, index">
<a-tooltip title="我在这里" :visible="showOptVisible[record.id]">
<a-space>
<a-button size="small" v-if="record.unLockType" type="primary" @click="unlock(record)"><a-icon type="unlock" />解锁</a-button>
<a-tooltip v-else title="如果按钮不可用则表示当前节点已经关闭啦,需要去编辑中启用">
<a-tooltip title="如果按钮不可用则表示当前节点已经关闭啦,需要去编辑中启用">
<a-button size="small" class="jpom-node-manage-btn" type="primary" @click="handleNode(record)" :disabled="record.openStatus !== 1"><a-icon type="apartment" />管理</a-button>
</a-tooltip>
<a-tooltip title="需要到编辑中去为一个节点绑定一个 ssh信息才能启用该功能">
@ -131,45 +163,11 @@
</a-space>
</a-tooltip>
</template>
<!-- 嵌套表格 -->
<!-- <template slot="expandIcon" slot-scope="text" v-if="text.record.openStatus === 1"> <a-icon type="plus" /></template> -->
<template slot="expandedRowRender" slot-scope="record">
<a-table size="middle" :loading="childLoading" :columns="childColumns" :data-source="nodeStatusData[record.id]" :pagination="false" :rowKey="(record, index) => index">
<a-tooltip slot="osName" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span>
</a-tooltip>
<a-tooltip slot="javaVersion" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span>
</a-tooltip>
<a-tooltip slot="runTime" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span>
</a-tooltip>
<template slot="projectCount" slot-scope="text, item">
<div v-if="text" @click="syncNode(item)">
<a-tooltip placement="topLeft" title="节点中的所有项目数量,点击重新同步节点项目信息">
<a-tag>{{ text }} </a-tag>
<a-icon type="sync" />
</a-tooltip>
</div>
</template>
<template slot="scriptCount" slot-scope="text, item">
<div v-if="text" @click="syncNodeScript(item)">
<a-tooltip placement="topLeft" title="节点中的所有脚本模版数量,点击重新同步脚本模版信息">
<a-tag>{{ text }} </a-tag>
<a-icon type="sync" />
</a-tooltip>
</div>
</template>
</a-table>
</template>
</a-table>
<!-- 编辑区 -->
<a-modal destroyOnClose v-model="editNodeVisible" width="50%" title="编辑节点" @ok="handleEditNodeOk" :maskClosable="false">
<a-form-model ref="editNodeForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 19 }">
<!-- <a-form-model-item label="节点 ID" prop="id">
<a-input v-model="temp.id" placeholder="创建之后不能修改" />
</a-form-model-item> -->
<a-form-model-item label="节点名称" prop="name">
<a-input :maxLength="50" v-model="temp.name" placeholder="节点名称" />
</a-form-model-item>
@ -177,12 +175,6 @@
<custom-select v-model="temp.group" :data="groupList" suffixIcon="" inputPlaceholder="添加分组" selectPlaceholder="选择分组名"> </custom-select>
</a-form-model-item>
<!-- <a-form-model-item label="监控周期" prop="cycle">
<a-select v-model="temp.cycle" defaultValue="0" placeholder="监控周期">
<a-select-option v-for="(name, key) in nodeMonitorCycle" :key="parseInt(key)">{{ name }}</a-select-option>
</a-select>
</a-form-model-item> -->
<a-form-model-item label="节点状态" prop="openStatus">
<a-switch
:checked="temp.openStatus == 1"
@ -196,76 +188,12 @@
default-checked
/>
</a-form-model-item>
<a-form-model-item prop="url">
<template slot="label">
节点地址
<a-tooltip v-show="!temp.id">
<template slot="title"
>节点地址为插件端的 IP:PORT 插件端端口默认为2123
<ul>
<li>节点地址建议使用内网地址</li>
<li>如果插件端正常运行但是连接失败请检查端口是否开放,防火墙规则,云服务器的安全组入站规则</li>
</ul>
</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-input v-model="temp.url" placeholder="节点地址 (127.0.0.1:2123)">
<a-select placeholder="选择协议类型" slot="addonBefore" v-model="temp.protocol" default-value="Http://" style="width: 80px">
<a-select-option value="Http"> Http:// </a-select-option>
<a-select-option value="Https"> Https:// </a-select-option>
</a-select>
<!-- <a-input v-model="temp.url" placeholder="节点地址 (127.0.0.1:2123)"/>-->
</a-input>
<a-form-model-item label="绑定 SSH " prop="sshId">
<a-select show-search option-filter-prop="children" v-model="temp.sshId" placeholder="请选择SSH">
<a-select-option value="">不绑定</a-select-option>
<a-select-option v-for="ssh in sshList" :key="ssh.id" :disabled="ssh.disabled">{{ ssh.name }}</a-select-option>
</a-select>
</a-form-model-item>
<!-- <a-form-model-item label="节点协议" prop="protocol">-->
<!-- <a-select v-model="temp.protocol" defaultValue="http" placeholder="节点协议">-->
<!-- <a-select-option key="http">HTTP</a-select-option>-->
<!-- <a-select-option key="htts">HTTPS</a-select-option>-->
<!-- </a-select>-->
<!-- </a-form-model-item>-->
<!-- <a-form-model-item label="节点地址" prop="url">-->
<!-- <a-input v-model="temp.url" placeholder="节点地址 (127.0.0.1:2123)"/>-->
<!-- </a-form-model-item>-->
<div class="node-config">
<a-form-model-item label="节点账号" prop="loginName">
<a-input v-model="temp.loginName" placeholder="节点账号,请查看节点启动输出的信息" />
</a-form-model-item>
<a-form-model-item :prop="`${temp.id ? 'loginPwd-update' : 'loginPwd'}`">
<template slot="label">
节点密码
<a-tooltip v-show="!temp.id">
<template slot="title"> 节点账号密码默认由系统生成可以通过插件端数据目录下 agent_authorize.json 文件查看如果自定义配置了账号密码将没有此文件 </template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-input-password v-model="temp.loginPwd" placeholder="节点密码,请查看节点启动输出的信息" />
</a-form-model-item>
</div>
<a-collapse>
<a-collapse-panel key="1" header="其他配置">
<a-form-model-item label="超时时间(s)" prop="timeOut">
<a-input-number v-model="temp.timeOut" :min="0" placeholder="秒 (值太小可能会取不到节点状态)" style="width: 100%" />
</a-form-model-item>
<a-form-model-item label="绑定 SSH " prop="sshId">
<a-select show-search option-filter-prop="children" v-model="temp.sshId" placeholder="请选择SSH">
<a-select-option value="">不绑定</a-select-option>
<a-select-option v-for="ssh in sshList" :key="ssh.id" :disabled="ssh.disabled">{{ ssh.name }}</a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="代理" prop="httpProxy">
<a-input v-model="temp.httpProxy" placeholder="代理地址 (127.0.0.1:8888)">
<a-select slot="addonBefore" v-model="temp.httpProxyType" placeholder="选择代理类型" default-value="HTTP" style="width: 100px">
<a-select-option value="HTTP">HTTP</a-select-option>
<a-select-option value="SOCKS">SOCKS</a-select-option>
<a-select-option value="DIRECT">DIRECT</a-select-option>
</a-select>
<!-- <a-input v-model="temp.url" placeholder="节点地址 (127.0.0.1:2123)"/>-->
</a-input>
</a-form-model-item>
</a-collapse-panel>
</a-collapse>
</a-form-model>
</a-modal>
<!-- 管理节点 -->
@ -289,16 +217,7 @@
>
<terminal v-if="terminalVisible" :sshId="temp.sshId" :nodeId="temp.id" />
</a-modal>
<!-- 解锁节点 -->
<a-modal destroyOnClose v-model="unlockNode" title="解锁节点" @ok="handleUnLockNodeOk" :maskClosable="false">
<a-form-model :model="temp" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }">
<a-form-model-item label="绑定工作空间" prop="workspaceId">
<a-select show-search option-filter-prop="children" v-model="temp.workspaceId" placeholder="请选择工作空间">
<a-select-option v-for="item in workspaceList" :key="item.id">{{ item.name }}</a-select-option>
</a-select>
</a-form-model-item>
</a-form-model>
</a-modal>
<!-- 快速安装插件端 -->
<a-modal
destroyOnClose
@ -340,15 +259,16 @@
</template>
<script>
import { mapGetters } from "vuex";
import { deleteNode, editNode, getNodeGroupAll, getNodeList, getNodeStatus, syncProject, syncToWorkspace, unbind, unLockWorkspace, sortItem } from "@/api/node";
import { deleteNode, editNode, getNodeGroupAll, getNodeList, syncProject, syncToWorkspace, unbind, sortItem } from "@/api/node";
import { getSshListAll } from "@/api/ssh";
import { syncScript } from "@/api/node-other";
import NodeLayout from "./node-layout";
import Terminal from "@/pages/ssh/terminal";
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, parseTime } from "@/utils/const";
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, formatDuration, renderSize } from "@/utils/const";
import { getWorkSpaceListAll } from "@/api/workspace";
import CustomSelect from "@/components/customSelect";
import fastInstall from "./fast-install.vue";
import { statusMap } from "@/api/system/assets-machine";
export default {
components: {
@ -360,12 +280,12 @@ export default {
data() {
return {
loading: false,
childLoading: false,
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY),
// nodeMonitorCycle: nodeMonitorCycle,
statusMap,
sshList: [],
list: [],
nodeStatusData: {},
groupList: [],
showOptVisible: {},
temp: {},
@ -373,51 +293,29 @@ export default {
editNodeVisible: false,
drawerVisible: false,
terminalVisible: false,
unlockNode: false,
fastInstallNode: false,
syncToWorkspaceVisible: false,
drawerTitle: "",
columns: [
// { title: " ID", dataIndex: "id", sorter: true, key: "id", ellipsis: true, scopedSlots: { customRender: "id" } },
{ title: "节点名称", dataIndex: "name", sorter: true, key: "name", ellipsis: true, scopedSlots: { customRender: "name" } },
// { title: "", dataIndex: "protocol", sorter: true, key: "protocol", width: 100, ellipsis: true, scopedSlots: { customRender: "protocol" } },
{ title: "节点地址", dataIndex: "url", sorter: true, key: "url", ellipsis: true, scopedSlots: { customRender: "url" } },
{ title: "账号", dataIndex: "loginName", sorter: true, width: 150, key: "loginName", ellipsis: true, scopedSlots: { customRender: "loginName" } },
// { title: "", dataIndex: "cycle", sorter: true, key: "cycle", ellipsis: true, scopedSlots: { customRender: "cycle" } },
{ title: "超时时间", dataIndex: "timeOut", sorter: true, key: "timeOut", width: 100, ellipsis: true },
{
title: "修改时间",
dataIndex: "modifyTimeMillis",
ellipsis: true,
sorter: true,
customRender: (text) => {
return parseTime(text);
},
width: 170,
},
{ title: "操作", dataIndex: "operation", key: "operation", width: 210, scopedSlots: { customRender: "operation" }, align: "center" },
],
childColumns: [
{ title: "系统名", dataIndex: "osName", key: "osName", width: 100, ellipsis: true, scopedSlots: { customRender: "osName" } },
{ title: "状态", dataIndex: "status", ellipsis: true, scopedSlots: { customRender: "status" } },
{ title: "节点地址", dataIndex: "url", key: "url", width: "190px", ellipsis: true, scopedSlots: { customRender: "url" } },
{ title: "系统名", dataIndex: "osName", key: "osName", width: "100px", ellipsis: true, scopedSlots: { customRender: "osName" } },
{ title: "JDK 版本", dataIndex: "javaVersion", key: "javaVersion", ellipsis: true, scopedSlots: { customRender: "javaVersion" } },
{ title: "JVM 总内存", dataIndex: "totalMemory", key: "totalMemory", width: 120 },
{ title: "JVM 剩余内存", dataIndex: "freeMemory", key: "freeMemory", width: 140 },
{ title: "版本", dataIndex: "jpomVersion", key: "jpomVersion", width: 120 },
{ title: "Java 程序数", dataIndex: "javaVirtualCount", key: "javaVirtualCount", width: 120 },
{ title: "JVM 总内存", dataIndex: "totalMemory", ellipsis: true, scopedSlots: { customRender: "totalMemory" } },
{ title: "JVM 剩余内存", dataIndex: "machineNodeData.jvmFreeMemory", ellipsis: true, scopedSlots: { customRender: "freeMemory" } },
{ title: "项目数", dataIndex: "count", key: "count", width: 90, scopedSlots: { customRender: "projectCount" } },
{ title: "脚本数", dataIndex: "scriptCount", key: "scriptCount", width: 90, scopedSlots: { customRender: "scriptCount" } },
{ title: "响应时间", dataIndex: "timeOut", key: "timeOut", width: 120 },
{ title: "已运行时间", dataIndex: "runTime", key: "runTime", width: 150, ellipsis: true, scopedSlots: { customRender: "runTime" } },
{ title: "项目数", dataIndex: "count", key: "count", width: "90px", scopedSlots: { customRender: "projectCount" } },
{ title: "脚本数", dataIndex: "scriptCount", key: "scriptCount", width: "90px", scopedSlots: { customRender: "scriptCount" } },
{ title: "已运行时间", dataIndex: "runTime", key: "runTime", ellipsis: true, scopedSlots: { customRender: "runTime" } },
{ title: "操作", dataIndex: "operation", key: "operation", width: "210px", scopedSlots: { customRender: "operation" }, align: "center" },
],
rules: {
id: [{ required: true, message: "Please input node id", trigger: "blur" }],
name: [{ required: true, message: "Please input node name", trigger: "blur" }],
url: [{ required: true, message: "Please input url", trigger: "blur" }],
loginName: [{ required: true, message: "Please input login name", trigger: "blur" }],
loginPwd: [{ required: true, message: "Please input login password", trigger: "blur" }],
timeOut: [{ required: true, message: "Please input timeout", trigger: "blur" }],
name: [{ required: true, message: "请输入节点名称", trigger: "blur" }],
},
workspaceList: [],
tableSelections: [],
@ -448,12 +346,10 @@ export default {
});
this.loadGroupList();
},
// destroyed() {
// if (this.pullFastInstallResultTime) {
// clearInterval(this.pullFastInstallResultTime);
// }
// },
methods: {
formatDuration,
renderSize,
findTipNode() {
if (this.$route.query.tipNodeId) {
this.showOptVisible[this.$route.query.tipNodeId] = true;
@ -492,11 +388,6 @@ export default {
options: {
hidePrev: true,
steps: [
{
title: "导航助手",
element: document.querySelector(".jpom-node-manage-add"),
intro: "如果还没有节点 可以点击【新增】按钮新增节点",
},
{
title: "导航助手",
element: document.querySelector(".jpom-node-manage-btn"),
@ -547,48 +438,7 @@ export default {
});
});
},
//
expand(expanded, record) {
if (expanded) {
if (!record.openStatus) {
this.$notification.error({
message: "节点未启用",
});
return false;
}
//
this.childLoading = true;
getNodeStatus(record.id).then((res) => {
if (res.code === 200) {
// bwcx_jzy 2022-05-11 使
this.nodeStatusData = {
...this.nodeStatusData,
[record.id]: [{ ...res.data[0], id: record.id + new Date().getTime() }],
};
}
this.childLoading = false;
});
}
},
//
handleAdd() {
this.$nextTick(() => {
setTimeout(() => {
this.introGuide();
}, 500);
this.$refs["editNodeForm"] && this.$refs["editNodeForm"].resetFields();
this.temp = {
type: "add",
cycle: 0,
protocol: "http",
openStatus: 1,
timeOut: 0,
loginName: "jpomAgent",
};
this.editNodeVisible = true;
});
this.loadSshList();
},
//
handleTerminal(record) {
this.temp = Object.assign({}, record);
@ -729,44 +579,6 @@ export default {
});
},
unlock(record) {
this.unlockNode = true;
this.loadWorkSpaceListAll();
this.temp = Object.assign({}, record);
this.temp.workspaceId = "";
},
handleUnLockNodeOk() {
if (!this.temp.workspaceId) {
this.$notification.warn({
message: "请选择工作空间",
});
return false;
}
this.$confirm({
title: "系统提示",
content: "确定要将此节点绑定到这个工作空间吗?绑定后不可更改",
okText: "确认",
cancelText: "取消",
onOk: () => {
//
unLockWorkspace({
id: this.temp.id,
toWorkspaceId: this.temp.workspaceId,
}).then((res) => {
if (res.code === 200) {
this.$notification.success({
message: res.msg,
});
this.unlockNode = false;
this.loadData();
return false;
}
});
},
});
},
//
syncToWorkspaceShow() {
this.syncToWorkspaceVisible = true;

View File

@ -61,7 +61,7 @@ export default {
//
nodeMonitorData(params).then((res) => {
if (res.code === 200) {
if (this.type === "networkTime") {
if (this.type === "networkDelay") {
this.historyChart = drawChart(res.data, "historyChart", generateNodeNetworkTimeChart);
} else {
this.historyChart = drawChart(res.data, "historyChart", generateNodeTopChart);

View File

@ -1,41 +1,16 @@
<template>
<div class="full-content">
<div>
<a-card :bodyStyle="{ padding: '10px' }">
<a-card>
<template slot="title">
<a-row style="text-align: center">
<a-col :span="3">
<a-statistic title="节点总数" :value="nodeCount"> </a-statistic>
</a-col>
<a-col :span="3" v-for="(desc, key) in statusMap" :key="key">
<a-statistic :title="desc" :value="statusStatMap[key]">
<template #suffix>
<!-- <a-icon type="question-circle" /> -->
</template>
</a-statistic>
</a-col>
<a-col :span="3"> <a-statistic-countdown format="s 秒" title="刷新倒计时" :value="deadline" @finish="onFinish" /> </a-col>
</a-row>
</template>
<a-space direction="vertical">
<div ref="filter" class="filter">
<a-row>
<a-space>
<a-input v-model="listQuery['%name%']" @pressEnter="loadData" placeholder="节点名称" />
<a-input v-model="listQuery['%url%']" @pressEnter="loadData" placeholder="节点地址" />
<a-select v-model="listQuery.status" allowClear placeholder="请选择状态" class="search-input-item">
<a-select-option v-for="(desc, key) in statusMap" :key="key">{{ desc }}</a-select-option>
</a-select>
<a-select show-search option-filter-prop="children" v-model="listQuery.group" allowClear placeholder="分组" class="search-input-item">
<a-select-option v-for="item in groupList" :key="item">{{ item }}</a-select-option>
</a-select>
<a-select v-model="listQuery['order_field']" allowClear placeholder="请选择排序字段" class="search-input-item">
<a-select-option value="networkTime">网络延迟</a-select-option>
<a-select-option value="occupyCpu">cpu</a-select-option>
<a-select-option value="occupyDisk">硬盘</a-select-option>
<a-select-option value="occupyMemoryUsed">内存Used</a-select-option>
<a-select-option value="occupyMemory">内存</a-select-option>
</a-select>
<a-tooltip title="按住 Ctr 或者 Alt/Option 键点击按钮快速回到第一页">
<a-button :loading="loading" type="primary" @click="loadData">搜索</a-button>
</a-tooltip>
@ -55,9 +30,11 @@
</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
<a-col :span="3"> <a-statistic-countdown format="s 秒" title="" :value="deadline" @finish="onFinish" /> </a-col>
</a-space>
</div>
</a-space>
</a-row>
</template>
<a-row :gutter="[16, 16]">
<template v-if="list && list.length">
<a-col v-for="item in list" :key="item.id" :span="6">
@ -75,8 +52,14 @@
</a-tooltip>
</a-col>
<a-col :span="7" style="text-align: right">
<a-tooltip :title="`当前状态:${statusMap[item.status]} ${item.status === 0 ? '' : '异常描述:' + item.failureMsg} `">
<a-tag :color="item.status === 0 ? 'green' : 'pink'" style="margin-right: 0px"> {{ statusMap[item.status] }}</a-tag>
<a-tooltip>
<template slot="title">
<div>当前状态{{ statusMap[item.machineNodeData && item.machineNodeData.status] }}</div>
<div>状态描述{{ (item.machineNodeData && item.machineNodeData.statusMsg) || "" }}</div>
</template>
<a-tag :color="item.machineNodeData && item.machineNodeData.status === 1 ? 'green' : 'pink'" style="margin-right: 0px">
{{ statusMap[item.machineNodeData && item.machineNodeData.status] }}
</a-tag>
</a-tooltip>
</a-col>
</a-row>
@ -84,7 +67,7 @@
<a-row :gutter="[8, 8]">
<a-col :span="8">
<a-tooltip @click="item.status === 4 ? null : handleHistory(item, 'nodeTop')" :title="`CPU 占用率:${item.occupyCpu}%`">
<a-tooltip @click="handleHistory(item, 'nodeTop')" :title="`CPU 占用率:${item.occupyCpu}%`">
<a-progress
type="circle"
:width="80"
@ -100,7 +83,7 @@
</a-tooltip>
</a-col>
<a-col :span="8">
<a-tooltip @click="item.status === 4 ? null : handleHistory(item, 'nodeTop')" :title="`硬盘占用率:${item.occupyDisk}%`">
<a-tooltip @click="handleHistory(item, 'nodeTop')" :title="`硬盘占用率:${item.occupyDisk}%`">
<a-progress
type="circle"
:width="80"
@ -116,10 +99,7 @@
</a-tooltip>
</a-col>
<a-col :span="8">
<a-tooltip
@click="item.status === 4 ? null : handleHistory(item, 'nodeTop')"
:title="`内存占用率:${item.occupyMemoryUsed && item.occupyMemoryUsed !== -1 ? item.occupyMemoryUsed : item.occupyMemory}%`"
>
<a-tooltip @click="handleHistory(item, 'nodeTop')" :title="`内存占用率:${item.occupyMemory}%`">
<a-progress
:width="80"
type="circle"
@ -130,7 +110,7 @@
}"
size="small"
status="active"
:percent="item.occupyMemoryUsed && item.occupyMemoryUsed !== -1 ? item.occupyMemoryUsed : item.occupyMemory"
:percent="item.occupyMemory"
/>
</a-tooltip>
</a-col>
@ -138,40 +118,43 @@
<a-row :gutter="[8, 8]" style="text-align: center">
<a-col :span="8">
<a-tooltip @click="item.status === 4 ? null : handleHistory(item, 'networkTime')" :title="`${item.status === 4 ? '-' : '延迟' + item.networkTime + 'ms 点击查看历史趋势'}`">
<a-tooltip
@click="handleHistory(item, 'networkDelay')"
:title="`${'延迟' + (formatDuration(item.machineNodeData && item.machineNodeData.networkDelay, '', 2) || '-') + ' 点击查看历史趋势'}`"
>
<a-statistic
title="延迟"
:value="item.networkTime"
:value="item.machineNodeData && item.machineNodeData.networkDelay"
valueStyle="font-size: 14px;overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
:formatter="
(v) => {
return item.networkTime === -1 ? '-' : item.networkTime + 'ms';
return formatDuration(item.machineNodeData && item.machineNodeData.networkDelay, '', 2) || '-';
}
"
/>
</a-tooltip>
</a-col>
<a-col :span="8">
<a-tooltip :title="formatDuration(item.upTimeStr) || '-'">
<a-tooltip :title="formatDuration(item.machineNodeData && item.machineNodeData.jpomUptime, '', 1) || '-'">
<a-statistic
title="运行时间"
valueStyle="font-size: 14px;overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
:formatter="
(v) => {
return formatDuration(item.upTimeStr, '', 1) || '-';
return formatDuration(item.machineNodeData && item.machineNodeData.jpomUptime, '', 2) || '-';
}
"
/>
</a-tooltip>
</a-col>
<a-col :span="8">
<a-tooltip :title="`${item.status === 4 ? '-' : parseTime(item.modifyTimeMillis)}`">
<a-tooltip :title="`${parseTime(item.machineNodeData && item.machineNodeData.modifyTimeMillis)}`">
<a-statistic
title="更新时间"
valueStyle="font-size: 14px;overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
:formatter="
(v) => {
return item.status === 4 ? '-' : parseTime(item.modifyTimeMillis, '{h}:{i}:{s}');
return parseTime(item.machineNodeData && item.machineNodeData.modifyTimeMillis, '{h}:{i}:{s}');
}
"
/>
@ -221,25 +204,25 @@
</div>
</template>
<script>
import { getStatist, status, statusStat } from "@/api/node-stat";
// import { getStatist, status, statusStat } from "@/api/node-stat";
import {} from "@/api/node";
import { PAGE_DEFAULT_LIST_QUERY, PAGE_DEFAULT_SHOW_TOTAL, formatDuration, parseTime, formatPercent2Number } from "@/utils/const";
import NodeTop from "@/pages/node/node-layout/node-top";
import { getNodeGroupAll } from "@/api/node";
import { getNodeGroupAll, getNodeList } from "@/api/node";
import { statusMap } from "@/api/system/assets-machine";
export default {
components: { NodeTop },
data() {
return {
loading: false,
statusMap: status,
listQuery: Object.assign({ order: "descend", order_field: "networkTime" }, PAGE_DEFAULT_LIST_QUERY, {
statusMap,
listQuery: Object.assign({}, PAGE_DEFAULT_LIST_QUERY, {
limit: 8,
}),
sizeOptions: ["8", "12", "16", "20", "24"],
list: [],
statusStatMap: {},
// openStatusMap: {},
nodeCount: 0,
monitorVisible: false,
deadline: 0,
temp: {},
@ -253,11 +236,7 @@ export default {
this.loadData();
this.loadGroupList();
},
destroyed() {
if (this.pullFastInstallResultTime) {
clearInterval(this.pullFastInstallResultTime);
}
},
destroyed() {},
methods: {
PAGE_DEFAULT_SHOW_TOTAL,
parseTime,
@ -267,37 +246,24 @@ export default {
//this.list = [];
this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page;
this.loading = true;
getStatist(this.listQuery).then((res) => {
getNodeList(this.listQuery).then((res) => {
if (res.code === 200) {
this.list =
res.data.result &&
res.data.result.map((item) => {
// console.log(item);
item.occupyCpu = formatPercent2Number(item.occupyCpu);
item.occupyMemoryUsed = formatPercent2Number(item.occupyMemoryUsed);
item.occupyDisk = formatPercent2Number(item.occupyDisk);
item.occupyMemory = formatPercent2Number(item.occupyMemory);
item.occupyCpu = formatPercent2Number(item.machineNodeData?.osOccupyCpu);
item.occupyDisk = formatPercent2Number(item.machineNodeData?.osOccupyDisk);
item.occupyMemory = formatPercent2Number(item.machineNodeData?.osOccupyMemory);
return item;
});
this.listQuery.total = res.data.total;
this.refreshInterval = 30;
this.deadline = Date.now() + this.refreshInterval * 1000;
}
this.loading = false;
});
statusStat().then((res) => {
if (res.data) {
this.statusStatMap = res.data.status;
let nodeCount2 = 0;
// console.log(this.statusStatMap);
Object.values(this.statusStatMap).forEach((element) => {
nodeCount2 += element;
});
this.nodeCount = nodeCount2;
this.refreshInterval = res.data.heartSecond;
this.deadline = Date.now() + this.refreshInterval * 1000;
//
// this.openStatusMap = res.data.openStatus;
}
});
},
onFinish() {
@ -326,4 +292,13 @@ export default {
},
};
</script>
<style scoped></style>
<style scoped>
/* /deep/ .ant-statistic div {
display: inline-block;
}
/deep/ .ant-statistic-content-value,
/deep/ .ant-statistic-content {
font-size: 16px;
} */
</style>

View File

@ -0,0 +1,293 @@
<template>
<div>
<a-card :bodyStyle="{ padding: '10px' }">
<template slot="title">
<a-space>
<a-input class="search-input-item" @pressEnter="getMachineList" v-model="listQuery['%name%']" placeholder="机器名称" />
<a-input class="search-input-item" @pressEnter="getMachineList" v-model="listQuery['%jpomUrl%']" placeholder="节点地址" />
<a-select show-search option-filter-prop="children" v-model="listQuery.groupName" allowClear placeholder="分组" class="search-input-item">
<a-select-option v-for="item in groupList" :key="item">{{ item }}</a-select-option>
</a-select>
<a-select v-model="listQuery['order_field']" allowClear placeholder="请选择排序字段" class="search-input-item">
<a-select-option value="networkDelay">网络延迟</a-select-option>
<a-select-option value="osOccupyCpu">cpu</a-select-option>
<a-select-option value="osOccupyDisk">硬盘</a-select-option>
<a-select-option value="osOccupyMemory">内存</a-select-option>
<a-select-option value="osOccupyMemory">更新时间</a-select-option>
<a-select-option value="osOccupyMemory">创建时间</a-select-option>
</a-select>
<a-button :loading="loading" type="primary" @click="getMachineList">搜索</a-button>
</a-space>
</template>
<a-row :gutter="[16, 16]">
<template v-if="list && list.length">
<a-col v-for="item in list" :key="item.id" :span="6">
<template>
<a-card :headStyle="{ padding: '0 6px' }" :bodyStyle="{ padding: '10px' }">
<template slot="title">
<a-row :gutter="[4, 0]">
<a-col :span="17" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap">
<a-tooltip>
<template slot="title">
<div>节点名称{{ item.name }}</div>
<div>节点地址{{ item.jpomUrl }}</div>
</template>
<a-button type="link" size="small" icon="edit" @click="handleEdit(item)">
{{ item.name }}
</a-button>
</a-tooltip>
</a-col>
<a-col :span="7" style="text-align: right">
<a-tooltip :title="`当前状态:${statusMap[item.status]} ${item.statusMsg ? '状态消息:' + item.statusMsg : ''} `">
<a-tag :color="item.status === 1 ? 'green' : 'pink'" style="margin-right: 0px"> {{ statusMap[item.status] }}</a-tag>
</a-tooltip>
<a-button type="link" icon="fullscreen" size="small"> </a-button>
<!-- <a-icon type="fullscreen" /> -->
</a-col>
</a-row>
</template>
<!-- <a-row :gutter="[8, 8]"> -->
<a-tooltip :title="item.osName">
<div class="item-info">
<div class="title">系统名称:</div>
<div class="content">
{{ item.osName }}
</div>
</div>
</a-tooltip>
<a-tooltip :title="item.osVersion">
<div class="item-info">
<div class="title">系统版本:</div>
<div class="content">
{{ item.osVersion }}
</div>
</div>
</a-tooltip>
<a-tooltip :title="item.osLoadAverage">
<div class="item-info">
<div class="title">系统负载:</div>
<div class="content">
{{ item.osLoadAverage }}
</div>
</div>
</a-tooltip>
<a-tooltip :title="item.jpomVersion">
<div class="item-info">
<div class="title">插件版本:</div>
<div class="content">
{{ item.jpomVersion }}
</div>
</div>
</a-tooltip>
<!-- <a-button type="link" :size="size"> 详情 </a-button> -->
</a-card>
</template>
</a-col>
</template>
<a-col v-else :span="24">
<a-empty description="没有任何节点" />
</a-col>
</a-row>
<a-row>
<a-col>
<a-pagination
v-model="listQuery.page"
v-if="listQuery.total / listQuery.limit > 1"
:showTotal="
(total) => {
return PAGE_DEFAULT_SHOW_TOTAL(total, listQuery);
}
"
:showSizeChanger="true"
:pageSizeOptions="sizeOptions"
:pageSize="listQuery.limit"
:total="listQuery.total"
@showSizeChange="
(current, size) => {
this.listQuery.limit = size;
this.getMachineList();
}
"
@change="this.getMachineList"
show-less-items
/>
</a-col>
</a-row>
</a-card>
<!-- 编辑区 -->
<a-modal destroyOnClose v-model="editVisible" width="50%" title="编辑机器" @ok="handleEditOk" :maskClosable="false">
<a-form-model ref="editNodeForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 19 }">
<a-form-model-item label="机器名称" prop="name">
<a-input :maxLength="50" v-model="temp.name" placeholder="机器名称" />
</a-form-model-item>
<a-form-model-item label="机器分组" prop="group">
<custom-select v-model="temp.groupName" :data="groupList" suffixIcon="" inputPlaceholder="添加分组" selectPlaceholder="选择分组名"> </custom-select>
</a-form-model-item>
<a-form-model-item prop="jpomUrl">
<template slot="label">
节点地址
<a-tooltip v-show="!temp.id">
<template slot="title"
>节点地址为插件端的 IP:PORT 插件端端口默认为2123
<ul>
<li>节点地址建议使用内网地址</li>
<li>如果插件端正常运行但是连接失败请检查端口是否开放,防火墙规则,云服务器的安全组入站规则</li>
</ul>
</template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-input v-model="temp.jpomUrl" placeholder="节点地址 (127.0.0.1:2123)">
<a-select placeholder="选择协议类型" slot="addonBefore" v-model="temp.jpomProtocol" default-value="Http://" style="width: 80px">
<a-select-option value="Http"> Http:// </a-select-option>
<a-select-option value="Https"> Https:// </a-select-option>
</a-select>
</a-input>
</a-form-model-item>
<a-form-model-item label="节点账号" prop="loginName">
<a-input v-model="temp.jpomUsername" placeholder="节点账号,请查看节点启动输出的信息" />
</a-form-model-item>
<a-form-model-item :prop="`${temp.id ? 'loginPwd-update' : 'loginPwd'}`">
<template slot="label">
节点密码
<a-tooltip v-show="!temp.id">
<template slot="title"> 节点账号密码默认由系统生成可以通过插件端数据目录下 agent_authorize.json 文件查看如果自定义配置了账号密码将没有此文件 </template>
<a-icon type="question-circle" theme="filled" />
</a-tooltip>
</template>
<a-input-password v-model="temp.jpomPassword" placeholder="节点密码,请查看节点启动输出的信息" />
</a-form-model-item>
<a-collapse>
<a-collapse-panel key="1" header="其他配置">
<a-form-model-item label="超时时间(s)" prop="timeOut">
<a-input-number v-model="temp.jpomTimeout" :min="0" placeholder="秒 (值太小可能会取不到节点状态)" style="width: 100%" />
</a-form-model-item>
<a-form-model-item label="代理" prop="jpomHttpProxy">
<a-input v-model="temp.jpomHttpProxy" placeholder="代理地址 (127.0.0.1:8888)">
<a-select slot="addonBefore" v-model="temp.jpomHttpProxyType" placeholder="选择代理类型" default-value="HTTP" style="width: 100px">
<a-select-option value="HTTP">HTTP</a-select-option>
<a-select-option value="SOCKS">SOCKS</a-select-option>
<a-select-option value="DIRECT">DIRECT</a-select-option>
</a-select>
</a-input>
</a-form-model-item>
</a-collapse-panel>
</a-collapse>
</a-form-model>
</a-modal>
</div>
</template>
<script>
import { machineListData, machineListGroup, statusMap, machineEdit } from "@/api/system/assets-machine";
import { CHANGE_PAGE, COMPUTED_PAGINATION, PAGE_DEFAULT_LIST_QUERY, PAGE_DEFAULT_SHOW_TOTAL, formatDuration, parseTime } from "@/utils/const";
import CustomSelect from "@/components/customSelect";
export default {
components: {
CustomSelect,
},
data() {
return {
statusMap,
listQuery: Object.assign({ order: "descend", order_field: "networkDelay" }, PAGE_DEFAULT_LIST_QUERY, {}),
sizeOptions: ["8", "12", "16", "20", "24"],
list: [],
groupList: [],
loading: true,
editVisible: false,
temp: {},
rules: {
name: [{ required: true, message: "请输入机器的名称", trigger: "blur" }],
},
};
},
computed: {
pagination() {
return COMPUTED_PAGINATION(this.listQuery);
},
},
mounted() {
this.loadGroupList();
this.getMachineList();
},
methods: {
parseTime,
formatDuration,
PAGE_DEFAULT_SHOW_TOTAL,
//
loadGroupList() {
machineListGroup().then((res) => {
if (res.data) {
this.groupList = res.data;
}
});
},
getMachineList(pointerEvent) {
this.loading = true;
this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page;
machineListData(this.listQuery).then((res) => {
if (res.code === 200) {
this.list = res.data.result;
this.listQuery.total = res.data.total;
}
this.loading = false;
});
},
//
changePage(pagination, filters, sorter) {
this.listQuery = CHANGE_PAGE(this.listQuery, { pagination, sorter });
this.getMachineList();
},
//
handleEdit(record) {
this.temp = Object.assign({}, record);
delete this.temp.statusMsg;
this.editVisible = true;
},
//
handleEditOk() {
//
this.$refs["editNodeForm"].validate((valid) => {
if (!valid) {
return false;
}
//
machineEdit(this.temp).then((res) => {
if (res.code === 200) {
//
this.$notification.success({
message: res.msg,
});
this.$refs["editNodeForm"].resetFields();
this.editVisible = false;
this.loadGroupList();
this.getMachineList();
}
});
});
},
},
};
</script>
<style scoped>
.item-info {
padding: 5px 0;
}
.item-info .title {
display: inline;
font-weight: bold;
}
.item-info .content {
display: inline;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@ -10,8 +10,8 @@
</a-row>
<a-space>
<a-input class="search-input-item" @pressEnter="getNodeList" v-model="listQuery['%name%']" placeholder="节点名称" />
<a-input class="search-input-item" @pressEnter="getNodeList" v-model="listQuery['%url%']" placeholder="节点地址" />
<a-select show-search option-filter-prop="children" v-model="listQuery.group" allowClear placeholder="分组" class="search-input-item">
<a-input class="search-input-item" @pressEnter="getNodeList" v-model="listQuery['%jpomUrl%']" placeholder="节点地址" />
<a-select show-search option-filter-prop="children" v-model="listQuery.groupName" allowClear placeholder="分组" class="search-input-item">
<a-select-option v-for="item in groupList" :key="item">{{ item }}</a-select-option>
</a-select>
<a-button :loading="loading" type="primary" @click="getNodeList">搜索</a-button>
@ -66,7 +66,8 @@
</template>
<script>
import upgrade from "@/components/upgrade";
import { checkVersion, downloadRemote, getNodeGroupAll, getNodeList, uploadAgentFile, uploadAgentFileMerge } from "@/api/node";
import { checkVersion, downloadRemote, uploadAgentFile, uploadAgentFileMerge } from "@/api/node";
import { machineListData, machineListGroup, statusMap } from "@/api/system/assets-machine";
import { mapGetters } from "vuex";
import { CHANGE_PAGE, COMPUTED_PAGINATION, getWebSocketUrl, PAGE_DEFAULT_LIST_QUERY } from "@/utils/const";
import { uploadPieces } from "@/utils/upload-pieces";
@ -96,7 +97,7 @@ export default {
columns: [
// { title: " ID", dataIndex: "id", ellipsis: true, scopedSlots: { customRender: "id" } },
{ title: "节点名称", dataIndex: "name", ellipsis: true, scopedSlots: { customRender: "name" } },
{ title: "节点地址", dataIndex: "url", sorter: true, key: "url", ellipsis: true, scopedSlots: { customRender: "url" } },
{ title: "节点地址", dataIndex: "jpomUrl", sorter: true, key: "url", ellipsis: true, scopedSlots: { customRender: "url" } },
{ title: "版本号", dataIndex: "version", width: "100px", ellipsis: true, scopedSlots: { customRender: "version" } },
{ title: "打包时间", dataIndex: "timeStamp", width: "180px", ellipsis: true, scopedSlots: { customRender: "timeStamp" } },
{ title: "运行时间", dataIndex: "upTimeStr", width: "180px", ellipsis: true, scopedSlots: { customRender: "upTimeStr" } },
@ -110,6 +111,7 @@ export default {
tableSelections: [],
temp: {},
percentage: 0,
statusMap,
};
},
computed: {
@ -159,7 +161,7 @@ export default {
},
//
loadGroupList() {
getNodeGroupAll().then((res) => {
machineListGroup().then((res) => {
if (res.data) {
this.groupList = res.data;
}
@ -261,7 +263,7 @@ export default {
},
getNodeList() {
this.loading = true;
getNodeList(this.listQuery).then((res) => {
machineListData(this.listQuery).then((res) => {
if (res.code === 200) {
this.list = res.data.result;
this.listQuery.total = res.data.total;

View File

@ -122,6 +122,11 @@ const children = [
];
const management = [
{
path: "/system/assets/machine-list",
name: "system-machine-list",
component: () => import("../pages/system/assets/machine/machine-list"),
},
{
path: "/user/permission-group",
name: "permission-group",

View File

@ -42,6 +42,7 @@ const routeMenuMap = {
backup: "/system/backup",
workspace: "/system/workspace",
globalEnv: "/system/global-env",
machine_node_info: "/system/assets/machine-list",
};
export default routeMenuMap;