This commit is contained in:
parent
67f394f136
commit
6f249d20b0
@ -35,6 +35,7 @@ type SaveClient struct {
|
|||||||
EnabledAfterCreation *int `json:"enableAfterCreation" form:"enableAfterCreation" binding:"required,oneof=1 0"`
|
EnabledAfterCreation *int `json:"enableAfterCreation" form:"enableAfterCreation" binding:"required,oneof=1 0"`
|
||||||
Keys *template_data.Keys `json:"keys" form:"keys" binding:"omitempty"`
|
Keys *template_data.Keys `json:"keys" form:"keys" binding:"omitempty"`
|
||||||
Enabled *int `json:"enabled" form:"enabled" binding:"required,oneof=1 0"`
|
Enabled *int `json:"enabled" form:"enabled" binding:"required,oneof=1 0"`
|
||||||
|
OfflineMonitoring *int `json:"offlineMonitoring" form:"offlineMonitoring" binding:"required,oneof=1 0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ControlServer
|
// ControlServer
|
||||||
|
@ -35,6 +35,7 @@ type Client struct {
|
|||||||
Keys string `json:"keys" gorm:"type:text;default null;comment:'公钥和密钥的json串'"`
|
Keys string `json:"keys" gorm:"type:text;default null;comment:'公钥和密钥的json串'"`
|
||||||
UserId string `json:"userId" gorm:"type:char(36);not null;comment:'创建人id'"`
|
UserId string `json:"userId" gorm:"type:char(36);not null;comment:'创建人id'"`
|
||||||
Enabled *int `json:"enabled" gorm:"type:tinyint(1);default 1;comment:'状态(0 - 禁用 | 1 - 正常)'"`
|
Enabled *int `json:"enabled" gorm:"type:tinyint(1);default 1;comment:'状态(0 - 禁用 | 1 - 正常)'"`
|
||||||
|
OfflineMonitoring *int `json:"offlineMonitoring" gorm:"tinyint(1);default 0;comment:'是否启用离线监听(0 - 禁用 | 1 - 启用)"`
|
||||||
User *User `json:"user" gorm:"foreignKey:UserId"`
|
User *User `json:"user" gorm:"foreignKey:UserId"`
|
||||||
Server *Server `json:"server" gorm:"foreignKey:ServerId"`
|
Server *Server `json:"server" gorm:"foreignKey:ServerId"`
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ type Client struct {
|
|||||||
Keys template_data.Keys `json:"keys" gorm:"-"`
|
Keys template_data.Keys `json:"keys" gorm:"-"`
|
||||||
CreateUser string `json:"createUser"`
|
CreateUser string `json:"createUser"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
|
OfflineMonitoring int `json:"offlineMonitoring"`
|
||||||
CreatedAt entity.JsonTime `json:"createdAt"`
|
CreatedAt entity.JsonTime `json:"createdAt"`
|
||||||
UpdatedAt entity.JsonTime `json:"updatedAt"`
|
UpdatedAt entity.JsonTime `json:"updatedAt"`
|
||||||
}
|
}
|
||||||
|
@ -3,5 +3,8 @@ package queues
|
|||||||
// StartConsumer
|
// StartConsumer
|
||||||
// @description: 启动消费者
|
// @description: 启动消费者
|
||||||
func StartConsumer() {
|
func StartConsumer() {
|
||||||
|
// 同步配置文件
|
||||||
go asyncWireguardConfigFile()
|
go asyncWireguardConfigFile()
|
||||||
|
// 离线监听
|
||||||
|
go offlineMonitoring()
|
||||||
}
|
}
|
||||||
|
65
queues/offline_monitoring.go
Normal file
65
queues/offline_monitoring.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package queues
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitee.ltd/lxh/logger/log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"wireguard-dashboard/client"
|
||||||
|
"wireguard-dashboard/repository"
|
||||||
|
"wireguard-dashboard/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// offlineMonitoring
|
||||||
|
// @description: 离线监听任务
|
||||||
|
func offlineMonitoring() {
|
||||||
|
for {
|
||||||
|
devices, err := client.WireguardClient.Devices()
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(5 * time.Minute) // 休眠五分钟再执行
|
||||||
|
offlineMonitoring()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历客户端数据,并渲染数据信息
|
||||||
|
for _, d := range devices {
|
||||||
|
for _, p := range d.Peers {
|
||||||
|
clientInfo, err := repository.Client().GetByPublicKey(p.PublicKey.String())
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有启用离线监听时,即使客户端已经离线则也不执行
|
||||||
|
if *clientInfo.OfflineMonitoring != 1 || clientInfo.Email == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipAllocation string
|
||||||
|
for _, iaip := range p.AllowedIPs {
|
||||||
|
ipAllocation += iaip.String() + ","
|
||||||
|
}
|
||||||
|
ipAllocation = strings.TrimRight(ipAllocation, ",")
|
||||||
|
isOnline := time.Since(p.LastHandshakeTime).Minutes() < 3
|
||||||
|
|
||||||
|
// 未离线
|
||||||
|
if !isOnline {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
content := fmt.Sprintf("客户端:%s\r\n", clientInfo.Name)
|
||||||
|
content += fmt.Sprintf("客户端IP:%s\r\n", ipAllocation)
|
||||||
|
content += fmt.Sprintf("端点IP:%s", p.Endpoint.String())
|
||||||
|
content += fmt.Sprintf("最后握手时间:%s\r\n", p.LastHandshakeTime.Format("2006-01-02 15:04:05"))
|
||||||
|
content += fmt.Sprintf("离线时间:%s\r\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
|
||||||
|
// 离线并且配置了邮箱,准备发送邮件
|
||||||
|
err = utils.Mail().SendMail(clientInfo.Email, fmt.Sprintf("客户端离线通知"), content, "")
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("发送离线通知邮件失败: %v", err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,7 @@ func (r clientRepo) List(p param.ClientList) (data []vo.Client, total int64, err
|
|||||||
Scopes(utils.Page(p.Current, p.Size)).
|
Scopes(utils.Page(p.Current, p.Size)).
|
||||||
Joins("LEFT JOIN t_user as tu ON twc.user_id = tu.id").
|
Joins("LEFT JOIN t_user as tu ON twc.user_id = tu.id").
|
||||||
Select("twc.id", "twc.created_at", "twc.updated_at", "twc.name", "twc.email", "twc.subnet_range", "twc.ip_allocation as ip_allocation_str", "twc.allowed_ips as allowed_ips_str",
|
Select("twc.id", "twc.created_at", "twc.updated_at", "twc.name", "twc.email", "twc.subnet_range", "twc.ip_allocation as ip_allocation_str", "twc.allowed_ips as allowed_ips_str",
|
||||||
"twc.extra_allowed_ips as extra_allowed_ips_str", "twc.endpoint", "twc.use_server_dns", "twc.enable_after_creation", "twc.enabled", "twc.keys as keys_str", "tu.name as create_user")
|
"twc.extra_allowed_ips as extra_allowed_ips_str", "twc.endpoint", "twc.use_server_dns", "twc.enable_after_creation", "twc.enabled", "twc.keys as keys_str", "tu.name as create_user", "twc.offline_monitoring")
|
||||||
|
|
||||||
if p.Name != "" {
|
if p.Name != "" {
|
||||||
sel.Where("twc.name LIKE ?", "%"+p.Name+"%")
|
sel.Where("twc.name LIKE ?", "%"+p.Name+"%")
|
||||||
@ -104,6 +104,7 @@ func (r clientRepo) Save(p param.SaveClient, adminId string) (client *entity.Cli
|
|||||||
EnableAfterCreation: p.EnabledAfterCreation,
|
EnableAfterCreation: p.EnabledAfterCreation,
|
||||||
UserId: adminId,
|
UserId: adminId,
|
||||||
Enabled: p.Enabled,
|
Enabled: p.Enabled,
|
||||||
|
OfflineMonitoring: p.OfflineMonitoring,
|
||||||
}
|
}
|
||||||
|
|
||||||
// id不为空,更新信息
|
// id不为空,更新信息
|
||||||
@ -113,7 +114,7 @@ func (r clientRepo) Save(p param.SaveClient, adminId string) (client *entity.Cli
|
|||||||
if err = r.Model(&entity.Client{}).
|
if err = r.Model(&entity.Client{}).
|
||||||
Where("id = ?", p.Id).Select("name", "email", "subnet_range", "ip_allocation",
|
Where("id = ?", p.Id).Select("name", "email", "subnet_range", "ip_allocation",
|
||||||
"allowed_ips", "extra_allowed_ips", "endpoint", "use_server_dns", "enable_after_creation",
|
"allowed_ips", "extra_allowed_ips", "endpoint", "use_server_dns", "enable_after_creation",
|
||||||
"user_id", "enabled").
|
"user_id", "enabled", "offline_monitoring").
|
||||||
Updates(ent).Error; err != nil {
|
Updates(ent).Error; err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -186,6 +187,7 @@ func (r clientRepo) Save(p param.SaveClient, adminId string) (client *entity.Cli
|
|||||||
Keys: string(keysStr),
|
Keys: string(keysStr),
|
||||||
UserId: adminId,
|
UserId: adminId,
|
||||||
Enabled: p.Enabled,
|
Enabled: p.Enabled,
|
||||||
|
OfflineMonitoring: p.OfflineMonitoring,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.Model(&entity.Client{}).Create(ent).Error
|
err = r.Model(&entity.Client{}).Create(ent).Error
|
||||||
|
18
web-src/.vscode/extensions.json
vendored
18
web-src/.vscode/extensions.json
vendored
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"christian-kohler.path-intellisense",
|
|
||||||
"vscode-icons-team.vscode-icons",
|
|
||||||
"davidanson.vscode-markdownlint",
|
|
||||||
"ms-azuretools.vscode-docker",
|
|
||||||
"stylelint.vscode-stylelint",
|
|
||||||
"bradlc.vscode-tailwindcss",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"redhat.vscode-yaml",
|
|
||||||
"csstools.postcss",
|
|
||||||
"mikestead.dotenv",
|
|
||||||
"eamodio.gitlens",
|
|
||||||
"antfu.iconify",
|
|
||||||
"Vue.volar"
|
|
||||||
]
|
|
||||||
}
|
|
31
web-src/.vscode/settings.json
vendored
31
web-src/.vscode/settings.json
vendored
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"editor.formatOnType": true,
|
|
||||||
"editor.formatOnSave": true,
|
|
||||||
"[vue]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
||||||
},
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"editor.formatOnPaste": true,
|
|
||||||
"editor.guides.bracketPairs": "active",
|
|
||||||
"files.autoSave": "afterDelay",
|
|
||||||
"git.confirmSync": false,
|
|
||||||
"workbench.startupEditor": "newUntitledFile",
|
|
||||||
"editor.suggestSelection": "first",
|
|
||||||
"editor.acceptSuggestionOnCommitCharacter": false,
|
|
||||||
"css.lint.propertyIgnoredDueToDisplay": "ignore",
|
|
||||||
"editor.quickSuggestions": {
|
|
||||||
"other": true,
|
|
||||||
"comments": true,
|
|
||||||
"strings": true
|
|
||||||
},
|
|
||||||
"files.associations": {
|
|
||||||
"editor.snippetSuggestions": "top"
|
|
||||||
},
|
|
||||||
"[css]": {
|
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
||||||
},
|
|
||||||
"editor.codeActionsOnSave": {
|
|
||||||
"source.fixAll.eslint": "explicit"
|
|
||||||
},
|
|
||||||
"iconify.excludes": ["el"]
|
|
||||||
}
|
|
22
web-src/.vscode/vue3.0.code-snippets
vendored
22
web-src/.vscode/vue3.0.code-snippets
vendored
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"Vue3.0快速生成模板": {
|
|
||||||
"scope": "vue",
|
|
||||||
"prefix": "Vue3.0",
|
|
||||||
"body": [
|
|
||||||
"<template>",
|
|
||||||
"\t<div>test</div>",
|
|
||||||
"</template>\n",
|
|
||||||
"<script lang='ts'>",
|
|
||||||
"export default {",
|
|
||||||
"\tsetup() {",
|
|
||||||
"\t\treturn {}",
|
|
||||||
"\t}",
|
|
||||||
"}",
|
|
||||||
"</script>\n",
|
|
||||||
"<style lang='scss' scoped>\n",
|
|
||||||
"</style>",
|
|
||||||
"$2"
|
|
||||||
],
|
|
||||||
"description": "Vue3.0"
|
|
||||||
}
|
|
||||||
}
|
|
17
web-src/.vscode/vue3.2.code-snippets
vendored
17
web-src/.vscode/vue3.2.code-snippets
vendored
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"Vue3.2+快速生成模板": {
|
|
||||||
"scope": "vue",
|
|
||||||
"prefix": "Vue3.2+",
|
|
||||||
"body": [
|
|
||||||
"<script setup lang='ts'>",
|
|
||||||
"</script>\n",
|
|
||||||
"<template>",
|
|
||||||
"\t<div>test</div>",
|
|
||||||
"</template>\n",
|
|
||||||
"<style lang='scss' scoped>\n",
|
|
||||||
"</style>",
|
|
||||||
"$2"
|
|
||||||
],
|
|
||||||
"description": "Vue3.2+"
|
|
||||||
}
|
|
||||||
}
|
|
20
web-src/.vscode/vue3.3.code-snippets
vendored
20
web-src/.vscode/vue3.3.code-snippets
vendored
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"Vue3.3+defineOptions快速生成模板": {
|
|
||||||
"scope": "vue",
|
|
||||||
"prefix": "Vue3.3+",
|
|
||||||
"body": [
|
|
||||||
"<script setup lang='ts'>",
|
|
||||||
"defineOptions({",
|
|
||||||
"\tname: ''",
|
|
||||||
"})",
|
|
||||||
"</script>\n",
|
|
||||||
"<template>",
|
|
||||||
"\t<div>test</div>",
|
|
||||||
"</template>\n",
|
|
||||||
"<style lang='scss' scoped>\n",
|
|
||||||
"</style>",
|
|
||||||
"$2"
|
|
||||||
],
|
|
||||||
"description": "Vue3.3+defineOptions快速生成模板"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Version": "5.5.0",
|
"Version": "5.5.0",
|
||||||
"Title": "WG-Dashboard",
|
"Title": "wg-dashboard",
|
||||||
"FixedHeader": true,
|
"FixedHeader": true,
|
||||||
"HiddenSideBar": false,
|
"HiddenSideBar": false,
|
||||||
"MultiTagsCache": false,
|
"MultiTagsCache": false,
|
||||||
|
@ -47,3 +47,8 @@ export const getClientConnects = () => {
|
|||||||
export const offlineClient = (id: string) => {
|
export const offlineClient = (id: string) => {
|
||||||
return http.request<any>("post", baseUri("/client/offline/" + id));
|
return http.request<any>("post", baseUri("/client/offline/" + id));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 发送邮件
|
||||||
|
export const sendMail = (id: string) => {
|
||||||
|
return http.request<any>("post", baseUri("/client/to-email/" + id));
|
||||||
|
};
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { http } from "@/utils/http";
|
import { http } from "@/utils/http";
|
||||||
import { baseUri } from "@/api/utils";
|
import { baseUri } from "@/api/utils";
|
||||||
|
import { data } from "autoprefixer";
|
||||||
|
|
||||||
// 获取当前登陆用户信息
|
// 获取当前登陆用户信息
|
||||||
export const getUser = () => {
|
export const getUser = () => {
|
||||||
@ -30,3 +31,8 @@ export const deleteUser = (userId: string) => {
|
|||||||
export const changePassword = (data?: object) => {
|
export const changePassword = (data?: object) => {
|
||||||
return http.request("post", baseUri("/user/change-password"), { data });
|
return http.request("post", baseUri("/user/change-password"), { data });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 生成头像
|
||||||
|
export const generateAvatar = () => {
|
||||||
|
return http.request<any>("post", baseUri("/user/change-avatar"));
|
||||||
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { FormInstance } from "element-plus";
|
import { FormInstance } from "element-plus";
|
||||||
|
import { generateAvatar } from "@/api/user";
|
||||||
|
|
||||||
// 声明 props 类型
|
// 声明 props 类型
|
||||||
export interface FormProps {
|
export interface FormProps {
|
||||||
@ -41,11 +42,29 @@ function getUserEditFormRef() {
|
|||||||
return userEditFormRef.value;
|
return userEditFormRef.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 切换头像
|
||||||
|
const changeAvatar = () => {
|
||||||
|
generateAvatar().then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
userEditForm.value.avatar = res.data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({ getUserEditFormRef });
|
defineExpose({ getUserEditFormRef });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-form ref="userEditFormRef" :model="userEditForm" label-width="20%">
|
<el-form ref="userEditFormRef" :model="userEditForm" label-width="20%">
|
||||||
|
<el-form-item prop="avatar">
|
||||||
|
<el-avatar
|
||||||
|
style="cursor: pointer; margin-left: 25%"
|
||||||
|
size="large"
|
||||||
|
fit="cover"
|
||||||
|
:src="userEditForm.avatar"
|
||||||
|
@click="changeAvatar()"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
prop="name"
|
prop="name"
|
||||||
label="名称"
|
label="名称"
|
||||||
|
@ -135,14 +135,6 @@ class PureHttp {
|
|||||||
$error.isCancelRequest = Axios.isCancel($error);
|
$error.isCancelRequest = Axios.isCancel($error);
|
||||||
// 关闭进度条动画
|
// 关闭进度条动画
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
if ($error.response.status === 401) {
|
|
||||||
router.replace({
|
|
||||||
path: "/login",
|
|
||||||
query: {
|
|
||||||
redirect: router.currentRoute.value.fullPath
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 所有的响应异常 区分来源为取消请求/非取消请求
|
// 所有的响应异常 区分来源为取消请求/非取消请求
|
||||||
return Promise.reject($error);
|
return Promise.reject($error);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
deleteClient,
|
deleteClient,
|
||||||
downloadClient, generateClientIP,
|
downloadClient,
|
||||||
|
generateClientIP,
|
||||||
getClients,
|
getClients,
|
||||||
saveClient
|
saveClient,
|
||||||
|
sendMail
|
||||||
} from "@/api/clients";
|
} from "@/api/clients";
|
||||||
import { h, reactive, ref } from "vue";
|
import { h, reactive, ref } from "vue";
|
||||||
import { addDialog } from "@/components/ReDialog/index";
|
import { addDialog } from "@/components/ReDialog/index";
|
||||||
@ -14,6 +16,7 @@ import "plus-pro-components/es/components/search/style/css";
|
|||||||
import { storageLocal } from "@pureadmin/utils";
|
import { storageLocal } from "@pureadmin/utils";
|
||||||
import { ArrowDown } from "@element-plus/icons-vue";
|
import { ArrowDown } from "@element-plus/icons-vue";
|
||||||
import { ElMessageBox } from "element-plus";
|
import { ElMessageBox } from "element-plus";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
// name 作为一种规范最好必须写上并且和路由的name保持一致
|
// name 作为一种规范最好必须写上并且和路由的name保持一致
|
||||||
@ -151,7 +154,7 @@ const openAddClientDialog = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const serverInfo = storageLocal().getItem("server-info");
|
const serverInfo = storageLocal().getItem("server-info");
|
||||||
const restartRule = storageLocal().getItem("restart-rule") ? 1 : 0;
|
const restartRule = !storageLocal().getItem("restart-rule") ? 1 : 0;
|
||||||
addDialog({
|
addDialog({
|
||||||
width: "40%",
|
width: "40%",
|
||||||
title: "新增",
|
title: "新增",
|
||||||
@ -174,7 +177,8 @@ const openAddClientDialog = () => {
|
|||||||
publicKey: "",
|
publicKey: "",
|
||||||
presharedKey: ""
|
presharedKey: ""
|
||||||
},
|
},
|
||||||
enabled: 1
|
enabled: 1,
|
||||||
|
offlineMonitoring: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeSure: (done, { options }) => {
|
beforeSure: (done, { options }) => {
|
||||||
@ -213,7 +217,8 @@ const openEditClientDialog = (client?: any) => {
|
|||||||
useServerDNS: client.useServerDNS,
|
useServerDNS: client.useServerDNS,
|
||||||
enableAfterCreation: client.enableAfterCreation,
|
enableAfterCreation: client.enableAfterCreation,
|
||||||
keys: client.keys,
|
keys: client.keys,
|
||||||
enabled: Number(client.enabled)
|
enabled: Number(client.enabled),
|
||||||
|
offlineMonitoring: client.offlineMonitoring
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeSure: (done, { options }) => {
|
beforeSure: (done, { options }) => {
|
||||||
@ -279,6 +284,15 @@ const ellipsis = (str: string) => {
|
|||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 发送到邮件
|
||||||
|
const sendToEmail = (clientID: string) => {
|
||||||
|
sendMail(clientID).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
message("发送邮件成功", { type: "success" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
getClientsApi(clientSearchForm.value);
|
getClientsApi(clientSearchForm.value);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -309,7 +323,10 @@ getClientsApi(clientSearchForm.value);
|
|||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<el-card body-style="padding: inherit" shadow="hover">
|
<el-card body-style="padding: inherit" shadow="hover">
|
||||||
<div class="flex flex-wrap gap-4" style="display: flex;justify-content: center;">
|
<div
|
||||||
|
class="flex flex-wrap gap-4"
|
||||||
|
style="display: flex; justify-content: center"
|
||||||
|
>
|
||||||
<el-card
|
<el-card
|
||||||
v-for="val in clientsList.data"
|
v-for="val in clientsList.data"
|
||||||
style="float: left; width: 500px"
|
style="float: left; width: 500px"
|
||||||
@ -338,6 +355,11 @@ getClientsApi(clientSearchForm.value);
|
|||||||
>下载</el-button
|
>下载</el-button
|
||||||
>
|
>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button type="success" @click="sendToEmail(val.id)"
|
||||||
|
>邮件</el-button
|
||||||
|
>
|
||||||
|
</el-dropdown-item>
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-button
|
<el-button
|
||||||
type="danger"
|
type="danger"
|
||||||
@ -418,7 +440,10 @@ getClientsApi(clientSearchForm.value);
|
|||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
<div class="paginate" style="background-color: #ffffff; margin-top: 5px">
|
<div
|
||||||
|
class="paginate"
|
||||||
|
style="background-color: #ffffff; margin-top: 5px"
|
||||||
|
>
|
||||||
<el-card>
|
<el-card>
|
||||||
<el-pagination
|
<el-pagination
|
||||||
small
|
small
|
||||||
|
@ -4,7 +4,7 @@ import { FormInstance } from "element-plus";
|
|||||||
import { storageLocal } from "@pureadmin/utils";
|
import { storageLocal } from "@pureadmin/utils";
|
||||||
import { userKey } from "@/utils/auth";
|
import { userKey } from "@/utils/auth";
|
||||||
import { clientFormRules } from "@/views/server/component/rules";
|
import { clientFormRules } from "@/views/server/component/rules";
|
||||||
import {generateClientKeys} from "@/api/clients";
|
import { generateClientKeys } from "@/api/clients";
|
||||||
|
|
||||||
// 声明 props 类型
|
// 声明 props 类型
|
||||||
export interface DetailFormProps {
|
export interface DetailFormProps {
|
||||||
@ -26,6 +26,7 @@ export interface DetailFormProps {
|
|||||||
presharedKey: string;
|
presharedKey: string;
|
||||||
};
|
};
|
||||||
enabled: number;
|
enabled: number;
|
||||||
|
offlineMonitoring: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +50,8 @@ const props = withDefaults(defineProps<DetailFormProps>(), {
|
|||||||
publicKey: "",
|
publicKey: "",
|
||||||
presharedKey: ""
|
presharedKey: ""
|
||||||
},
|
},
|
||||||
enabled: 1
|
enabled: 1,
|
||||||
|
offlineMonitoring: 0
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,7 +90,10 @@ defineExpose({ getDetailFormRef });
|
|||||||
<el-input v-model="detailForm.name" />
|
<el-input v-model="detailForm.name" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="email" label="邮箱">
|
<el-form-item prop="email" label="邮箱">
|
||||||
<el-input v-model="detailForm.email" />
|
<el-input
|
||||||
|
v-model="detailForm.email"
|
||||||
|
placeholder="可用于离线监听通知或接收客户端配置文件"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="subnetRange" label="子网范围">
|
<el-form-item prop="subnetRange" label="子网范围">
|
||||||
<el-input v-model="detailForm.subnetRange" />
|
<el-input v-model="detailForm.subnetRange" />
|
||||||
@ -146,27 +151,33 @@ defineExpose({ getDetailFormRef });
|
|||||||
v-if="detailForm.id === ''"
|
v-if="detailForm.id === ''"
|
||||||
v-model="detailForm.keys.privateKey"
|
v-model="detailForm.keys.privateKey"
|
||||||
/>
|
/>
|
||||||
<el-input v-else disabled v-model="detailForm.keys.privateKey" />
|
<el-input v-else v-model="detailForm.keys.privateKey" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="publicKey" label="公钥">
|
<el-form-item prop="publicKey" label="公钥">
|
||||||
<el-input
|
<el-input
|
||||||
v-if="detailForm.id === ''"
|
v-if="detailForm.id === ''"
|
||||||
v-model="detailForm.keys.publicKey"
|
v-model="detailForm.keys.publicKey"
|
||||||
/>
|
/>
|
||||||
<el-input v-else disabled v-model="detailForm.keys.publicKey" />
|
<el-input v-else v-model="detailForm.keys.publicKey" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="presharedKey" label="共享密钥">
|
<el-form-item prop="presharedKey" label="共享密钥">
|
||||||
<el-input
|
<el-input
|
||||||
v-if="detailForm.id === ''"
|
v-if="detailForm.id === ''"
|
||||||
v-model="detailForm.keys.presharedKey"
|
v-model="detailForm.keys.presharedKey"
|
||||||
/>
|
/>
|
||||||
<el-input v-else disabled v-model="detailForm.keys.presharedKey" />
|
<el-input v-else v-model="detailForm.keys.presharedKey" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="detailForm.id === ''">
|
<el-form-item v-if="detailForm.id === ''">
|
||||||
<el-button type="primary" size="small" @click="generateClientKeysApi()"
|
<el-button type="primary" size="small" @click="generateClientKeysApi()"
|
||||||
>生成密钥对</el-button
|
>生成密钥对</el-button
|
||||||
>
|
>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item prop="OfflineMonitoring" label="是否启用离线监听">
|
||||||
|
<el-radio-group v-model="detailForm.offlineMonitoring">
|
||||||
|
<el-radio :value="1">是</el-radio>
|
||||||
|
<el-radio :value="0">否</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item prop="useServerDNS" label="是否使用服务端DNS">
|
<el-form-item prop="useServerDNS" label="是否使用服务端DNS">
|
||||||
<el-radio-group v-model="detailForm.useServerDNS">
|
<el-radio-group v-model="detailForm.useServerDNS">
|
||||||
<el-radio :value="1">是</el-radio>
|
<el-radio :value="1">是</el-radio>
|
||||||
|
Loading…
Reference in New Issue
Block a user