完成SSH连接信息存储
This commit is contained in:
parent
77b5b2ef7f
commit
f06c2fd68f
@ -5,10 +5,8 @@ import com.example.entity.dto.Account;
|
||||
import com.example.entity.vo.request.RenameClientVO;
|
||||
import com.example.entity.vo.request.RenameNodeVO;
|
||||
import com.example.entity.vo.request.RuntimeDetailVO;
|
||||
import com.example.entity.vo.response.ClientDetailsVO;
|
||||
import com.example.entity.vo.response.ClientPreviewVO;
|
||||
import com.example.entity.vo.response.ClientSimpleVO;
|
||||
import com.example.entity.vo.response.RuntimeHistoryVO;
|
||||
import com.example.entity.vo.request.SshConnectionVO;
|
||||
import com.example.entity.vo.response.*;
|
||||
import com.example.service.AccountService;
|
||||
import com.example.service.ClientService;
|
||||
import com.example.utils.Const;
|
||||
@ -128,6 +126,29 @@ public class MonitorController {
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/ssh-save")
|
||||
public RestBean<Void> saveSshConnection(@RequestBody @Valid SshConnectionVO vo,
|
||||
@RequestAttribute(Const.ATTR_USER_ID) int userId,
|
||||
@RequestAttribute(Const.ATTR_USER_ROLE) String userRole) {
|
||||
if(this.permissionCheck(userId, userRole, vo.getId())) {
|
||||
service.saveClientSshConnection(vo);
|
||||
return RestBean.success();
|
||||
} else {
|
||||
return RestBean.noPermission();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/ssh")
|
||||
public RestBean<SshSettingsVO> sshSettings(int clientId,
|
||||
@RequestAttribute(Const.ATTR_USER_ID) int userId,
|
||||
@RequestAttribute(Const.ATTR_USER_ROLE) String userRole) {
|
||||
if(this.permissionCheck(userId, userRole, clientId)) {
|
||||
return RestBean.success(service.sshSettings(clientId));
|
||||
} else {
|
||||
return RestBean.noPermission();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Integer> accountAccessClients(int uid) {
|
||||
Account account = accountService.getById(uid);
|
||||
return account.getClientList();
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.example.entity.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.example.entity.BaseData;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("db_client_ssh")
|
||||
public class ClientSsh implements BaseData {
|
||||
@TableId
|
||||
Integer id;
|
||||
Integer port;
|
||||
String username;
|
||||
String password;
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.example.entity.vo.request;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
@Data
|
||||
public class SshConnectionVO {
|
||||
int id;
|
||||
int port;
|
||||
@NotNull
|
||||
@Length(min = 1)
|
||||
String username;
|
||||
@NotNull
|
||||
@Length(min = 1)
|
||||
String password;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.example.entity.vo.response;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SshSettingsVO {
|
||||
String ip;
|
||||
int port = 22;
|
||||
String username;
|
||||
String password;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.example.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.example.entity.dto.ClientSsh;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface ClientSshMapper extends BaseMapper<ClientSsh> {
|
||||
}
|
@ -2,14 +2,8 @@ package com.example.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.example.entity.dto.Client;
|
||||
import com.example.entity.vo.request.ClientDetailVO;
|
||||
import com.example.entity.vo.request.RenameClientVO;
|
||||
import com.example.entity.vo.request.RenameNodeVO;
|
||||
import com.example.entity.vo.request.RuntimeDetailVO;
|
||||
import com.example.entity.vo.response.ClientDetailsVO;
|
||||
import com.example.entity.vo.response.ClientPreviewVO;
|
||||
import com.example.entity.vo.response.ClientSimpleVO;
|
||||
import com.example.entity.vo.response.RuntimeHistoryVO;
|
||||
import com.example.entity.vo.request.*;
|
||||
import com.example.entity.vo.response.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -28,4 +22,6 @@ public interface ClientService extends IService<Client> {
|
||||
RuntimeHistoryVO clientRuntimeDetailsHistory(int clientId);
|
||||
RuntimeDetailVO clientRuntimeDetailsNow(int clientId);
|
||||
void deleteClient(int clientId);
|
||||
void saveClientSshConnection(SshConnectionVO vo);
|
||||
SshSettingsVO sshSettings(int clientId);
|
||||
}
|
||||
|
@ -4,16 +4,12 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.example.entity.dto.Client;
|
||||
import com.example.entity.dto.ClientDetail;
|
||||
import com.example.entity.vo.request.ClientDetailVO;
|
||||
import com.example.entity.vo.request.RenameClientVO;
|
||||
import com.example.entity.vo.request.RenameNodeVO;
|
||||
import com.example.entity.vo.request.RuntimeDetailVO;
|
||||
import com.example.entity.vo.response.ClientDetailsVO;
|
||||
import com.example.entity.vo.response.ClientPreviewVO;
|
||||
import com.example.entity.vo.response.ClientSimpleVO;
|
||||
import com.example.entity.vo.response.RuntimeHistoryVO;
|
||||
import com.example.entity.dto.ClientSsh;
|
||||
import com.example.entity.vo.request.*;
|
||||
import com.example.entity.vo.response.*;
|
||||
import com.example.mapper.ClientDetailMapper;
|
||||
import com.example.mapper.ClientMapper;
|
||||
import com.example.mapper.ClientSshMapper;
|
||||
import com.example.service.ClientService;
|
||||
import com.example.utils.InfluxDbUtils;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
@ -39,6 +35,9 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
|
||||
@Resource
|
||||
InfluxDbUtils influx;
|
||||
|
||||
@Resource
|
||||
ClientSshMapper sshMapper;
|
||||
|
||||
@PostConstruct
|
||||
public void initClientCache() {
|
||||
clientTokenCache.clear();
|
||||
@ -160,6 +159,33 @@ public class ClientServiceImpl extends ServiceImpl<ClientMapper, Client> impleme
|
||||
currentRuntime.remove(clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveClientSshConnection(SshConnectionVO vo) {
|
||||
Client client = clientIdCache.get(vo.getId());
|
||||
if(client == null) return;
|
||||
ClientSsh ssh = new ClientSsh();
|
||||
BeanUtils.copyProperties(vo, ssh);
|
||||
if(Objects.nonNull(sshMapper.selectById(client.getId()))) {
|
||||
sshMapper.updateById(ssh);
|
||||
} else {
|
||||
sshMapper.insert(ssh);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SshSettingsVO sshSettings(int clientId) {
|
||||
ClientDetail detail = detailMapper.selectById(clientId);
|
||||
ClientSsh ssh = sshMapper.selectById(clientId);
|
||||
SshSettingsVO vo;
|
||||
if(ssh == null) {
|
||||
vo = new SshSettingsVO();
|
||||
} else {
|
||||
vo = ssh.asViewObject(SshSettingsVO.class);
|
||||
}
|
||||
vo.setIp(detail.getIp());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private boolean isOnline(RuntimeDetailVO runtime) {
|
||||
return runtime != null && System.currentTimeMillis() - runtime.getTimestamp() < 60 * 1000;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import {get, post} from "@/net";
|
||||
import {copyIp, cpuNameToImage, fitByUnit, osNameToIcon, percentageToStatus, rename} from "@/tools";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import RuntimeHistory from "@/component/RuntimeHistory.vue";
|
||||
import {Delete} from "@element-plus/icons-vue";
|
||||
import {Connection, Delete} from "@element-plus/icons-vue";
|
||||
|
||||
const locations = [
|
||||
{name: 'cn', desc: '中国大陆'},
|
||||
@ -20,7 +20,7 @@ const props = defineProps({
|
||||
id: Number,
|
||||
update: Function
|
||||
})
|
||||
const emits = defineEmits(['delete'])
|
||||
const emits = defineEmits(['delete', 'terminal'])
|
||||
|
||||
const details = reactive({
|
||||
base: {},
|
||||
@ -101,8 +101,12 @@ watch(() => props.id, init, { immediate: true })
|
||||
<i class="fa-solid fa-server"></i>
|
||||
服务器信息
|
||||
</div>
|
||||
<el-button :icon="Delete" type="danger"
|
||||
@click="deleteClient" plain text>删除此主机</el-button>
|
||||
<div>
|
||||
<el-button :icon="Connection" type="info"
|
||||
@click="emits('terminal', id)" plain text>SSH远程连接</el-button>
|
||||
<el-button :icon="Delete" type="danger" style="margin-left: 0"
|
||||
@click="deleteClient" plain text>删除此主机</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider style="margin: 10px 0"/>
|
||||
<div class="details-list">
|
||||
|
91
itbaima-monitor-web/src/component/TerminalWindow.vue
Normal file
91
itbaima-monitor-web/src/component/TerminalWindow.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<script setup>
|
||||
import {reactive, ref, watch} from "vue";
|
||||
import {get, post} from "@/net";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const props = defineProps({
|
||||
id: Number
|
||||
})
|
||||
|
||||
const connection = reactive({
|
||||
ip: '',
|
||||
port: 22,
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
port: [
|
||||
{ required: true, message: '请输入端口', trigger: ['blur', 'change'] },
|
||||
],
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: ['blur', 'change'] },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: ['blur', 'change'] },
|
||||
]
|
||||
}
|
||||
|
||||
const formRef = ref()
|
||||
|
||||
function saveConnection() {
|
||||
formRef.value.validate((isValid) => {
|
||||
if(isValid) {
|
||||
post('/api/monitor/ssh-save', {
|
||||
...connection,
|
||||
id: props.id
|
||||
}, () => {
|
||||
ElMessage.success('正在连接')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => props.id, id => {
|
||||
if(id !== -1) {
|
||||
connection.ip = ''
|
||||
get(`/api/monitor/ssh?clientId=${id}`, data => Object.assign(connection, data))
|
||||
}
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="terminal-main">
|
||||
<div class="login" v-loading="!connection.ip">
|
||||
<i style="font-size: 50px" class="fa-solid fa-terminal"></i>
|
||||
<div style="margin-top: 10px;font-weight: bold;font-size: 20px">服务端连接信息</div>
|
||||
<el-form style="width: 400px;margin: 20px auto" :model="connection"
|
||||
:rules="rules" ref="formRef" label-width="100">
|
||||
<div style="display: flex;gap: 10px">
|
||||
<el-form-item style="width: 100%" label="服务器IP地址" prop="ip">
|
||||
<el-input v-model="connection.ip" disabled/>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 80px" prop="port" label-width="0">
|
||||
<el-input placeholder="端口" v-model="connection.port"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item prop="username" label="登录用户名">
|
||||
<el-input placeholder="请输入用户名..." v-model="connection.username"/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password" label="登录密码">
|
||||
<el-input placeholder="请输入密码..." type="password" v-model="connection.password"/>
|
||||
</el-form-item>
|
||||
<el-button type="success" @click="saveConnection" plain>立即连接</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.terminal-main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.login {
|
||||
text-align: center;
|
||||
padding-top: 50px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -7,6 +7,7 @@ import RegisterCard from "@/component/RegisterCard.vue";
|
||||
import {Plus} from "@element-plus/icons-vue";
|
||||
import {useRoute} from "vue-router";
|
||||
import {useStore} from "@/store";
|
||||
import TerminalWindow from "@/component/TerminalWindow.vue";
|
||||
|
||||
const locations = [
|
||||
{name: 'cn', desc: '中国大陆'},
|
||||
@ -54,6 +55,16 @@ const register = reactive({
|
||||
token: ''
|
||||
})
|
||||
const refreshToken = () => get('/api/monitor/register', token => register.token = token)
|
||||
|
||||
function openTerminal(id) {
|
||||
terminal.show = true
|
||||
terminal.id = id
|
||||
detail.show = false
|
||||
}
|
||||
const terminal = reactive({
|
||||
show: false,
|
||||
id: -1
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -84,12 +95,24 @@ const refreshToken = () => get('/api/monitor/register', token => register.token
|
||||
<el-empty description="还没有任何主机哦,点击右上角添加一个吧" v-else/>
|
||||
<el-drawer size="520" :show-close="false" v-model="detail.show"
|
||||
:with-header="false" v-if="list.length" @close="detail.id = -1">
|
||||
<client-details :id="detail.id" :update="updateList" @delete="updateList"/>
|
||||
<client-details :id="detail.id" :update="updateList" @delete="updateList" @terminal="openTerminal"/>
|
||||
</el-drawer>
|
||||
<el-drawer v-model="register.show" direction="btt" :with-header="false"
|
||||
style="width: 600px;margin: 10px auto" size="320" @open="refreshToken">
|
||||
<register-card :token="register.token"/>
|
||||
</el-drawer>
|
||||
<el-drawer style="width: 800px" :size="500" direction="btt"
|
||||
v-model="terminal.show" :close-on-click-modal="false">
|
||||
<template #header>
|
||||
<div>
|
||||
<div style="font-size: 18px;color: dodgerblue;font-weight: bold;">SSH远程连接</div>
|
||||
<div style="font-size: 14px">
|
||||
远程连接的建立将由服务端完成,因此在内网环境下也可以正常使用。
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<terminal-window :id="terminal.id"/>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user