🆕为实现数据迁移以及备份新增配置导出
This commit is contained in:
parent
c3ef51e87f
commit
edaf9ba770
@ -97,8 +97,8 @@ func (w WireguardComponent) GenerateClientFile(clientInfo *model.Client, server
|
|||||||
var outPath = "/tmp/" + fmt.Sprintf("%s.conf", clientInfo.Name)
|
var outPath = "/tmp/" + fmt.Sprintf("%s.conf", clientInfo.Name)
|
||||||
var templatePath = "./template/wg.client.conf"
|
var templatePath = "./template/wg.client.conf"
|
||||||
if os.Getenv("GIN_MODE") != "release" {
|
if os.Getenv("GIN_MODE") != "release" {
|
||||||
outPath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\" + fmt.Sprintf("%s.conf", clientInfo.Name)
|
outPath = "E:\\Workspace\\Go\\wireguard-ui\\template\\tmp\\" + fmt.Sprintf("%s.conf", clientInfo.Name)
|
||||||
templatePath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\wg.client.conf"
|
templatePath = "E:\\Workspace\\Go\\wireguard-ui\\template\\conf\\wg.client.conf"
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Template().Execute(templatePath, outPath, execData)
|
err = Template().Execute(templatePath, outPath, execData)
|
||||||
|
1
cron/cron.go
Normal file
1
cron/cron.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package cron
|
@ -1,11 +1,13 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"gitee.ltd/lxh/logger/log"
|
"gitee.ltd/lxh/logger/log"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"slices"
|
"slices"
|
||||||
"wireguard-ui/http/param"
|
"wireguard-ui/http/param"
|
||||||
"wireguard-ui/http/response"
|
"wireguard-ui/http/response"
|
||||||
|
"wireguard-ui/http/vo"
|
||||||
"wireguard-ui/model"
|
"wireguard-ui/model"
|
||||||
"wireguard-ui/script"
|
"wireguard-ui/script"
|
||||||
"wireguard-ui/service"
|
"wireguard-ui/service"
|
||||||
@ -113,3 +115,48 @@ func (setting) GetAllSetting(c *gin.Context) {
|
|||||||
func (setting) GetPublicAddr(c *gin.Context) {
|
func (setting) GetPublicAddr(c *gin.Context) {
|
||||||
response.R(c).OkWithData(utils.Network().GetHostPublicIP())
|
response.R(c).OkWithData(utils.Network().GetHostPublicIP())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export
|
||||||
|
// @description: 导出配置
|
||||||
|
// @receiver setting
|
||||||
|
// @param c
|
||||||
|
func (setting) Export(c *gin.Context) {
|
||||||
|
// 获取当前登陆用户
|
||||||
|
var loginUser *vo.User
|
||||||
|
if loginUser = GetCurrentLoginUser(c); c.IsAborted() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if loginUser.Account != "admin" {
|
||||||
|
response.R(c).FailedWithError("非法操作,你被捕啦!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取配置
|
||||||
|
data, err := service.Setting().Export()
|
||||||
|
if err != nil {
|
||||||
|
response.R(c).FailedWithError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成配置文件
|
||||||
|
dataBytes, _ := json.Marshal(data)
|
||||||
|
filepath, err := utils.FileUtils().GenerateFile("config.json", dataBytes)
|
||||||
|
if err != nil {
|
||||||
|
response.R(c).FailedWithError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Header("Content-Type", "application/octet-stream")
|
||||||
|
c.Header("Content-Disposition", "attachment; filename="+filepath)
|
||||||
|
c.Header("Content-Transfer-Encoding", "binary")
|
||||||
|
c.Header("Connection", "keep-alive")
|
||||||
|
c.File(filepath)
|
||||||
|
//if err = os.Remove(filepath); err != nil {
|
||||||
|
// log.Errorf("删除临时文件失败: %s", err.Error())
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting) Import(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -17,5 +17,6 @@ func SettingApi(r *gin.RouterGroup) {
|
|||||||
setting.GET("", api.Setting().GetSetting) // 获取指定配置
|
setting.GET("", api.Setting().GetSetting) // 获取指定配置
|
||||||
setting.GET("/all", api.Setting().GetAllSetting) // 获取全部配置
|
setting.GET("/all", api.Setting().GetAllSetting) // 获取全部配置
|
||||||
setting.GET("/public-addr", api.Setting().GetPublicAddr) // 获取公网IP
|
setting.GET("/public-addr", api.Setting().GetPublicAddr) // 获取公网IP
|
||||||
|
setting.GET("/export", api.Setting().Export) // 导出配置文件
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,3 +7,46 @@ type SettingItem struct {
|
|||||||
Data string `json:"data"`
|
Data string `json:"data"`
|
||||||
Describe string `json:"describe"`
|
Describe string `json:"describe"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Export struct {
|
||||||
|
Global Global `json:"global"`
|
||||||
|
Server Server `json:"server"`
|
||||||
|
Clients []Client `json:"clients"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Global struct {
|
||||||
|
MTU int `json:"MTU"`
|
||||||
|
ConfigFilePath string `json:"configFilePath"`
|
||||||
|
DnsServer []string `json:"dnsServer"`
|
||||||
|
EndpointAddress string `json:"endpointAddress"`
|
||||||
|
FirewallMark string `json:"firewallMark"`
|
||||||
|
PersistentKeepalive int `json:"persistentKeepalive"`
|
||||||
|
Table string `json:"table"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
IpScope []string `json:"ipScope"`
|
||||||
|
ListenPort int `json:"listenPort"`
|
||||||
|
PrivateKey string `json:"privateKey"`
|
||||||
|
PublicKey string `json:"publicKey"`
|
||||||
|
PostUpScript string `json:"postUpScript"`
|
||||||
|
PostDownScript string `json:"postDownScript"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
SubnetRange string `json:"subnetRange"`
|
||||||
|
IpAllocation []string `json:"ipAllocation"`
|
||||||
|
AllowedIps []string `json:"allowedIps"`
|
||||||
|
ExtraAllowedIps []string `json:"extraAllowedIps"`
|
||||||
|
Endpoint string `json:"endpoint"`
|
||||||
|
UseServerDns int `json:"useServerDns"`
|
||||||
|
Keys struct {
|
||||||
|
PresharedKey string `json:"presharedKey"`
|
||||||
|
PrivateKey string `json:"privateKey"`
|
||||||
|
PublicKey string `json:"publicKey"`
|
||||||
|
} `json:"keys"`
|
||||||
|
Enabled int `json:"enabled"`
|
||||||
|
OfflineMonitoring int `json:"offlineMonitoring"`
|
||||||
|
}
|
||||||
|
@ -22,3 +22,16 @@ type Client struct {
|
|||||||
func (Client) TableName() string {
|
func (Client) TableName() string {
|
||||||
return "t_client"
|
return "t_client"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Watcher
|
||||||
|
// @description: 监听日志
|
||||||
|
type Watcher struct {
|
||||||
|
Base
|
||||||
|
ClientId string `json:"clientId" gorm:"type:char(36);not null;comment:'客户端id'"`
|
||||||
|
NotifyResult string `json:"notifyResult" gorm:"type:text;default null;comment:'通知结果'"`
|
||||||
|
IsSend int `json:"isSend" gorm:"type:tinyint(1);default 0;comment:'是否已通知'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Watcher) TableName() string {
|
||||||
|
return "t_watcher"
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@ package service
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"strings"
|
||||||
gdb "wireguard-ui/global/client"
|
gdb "wireguard-ui/global/client"
|
||||||
"wireguard-ui/http/vo"
|
"wireguard-ui/http/vo"
|
||||||
"wireguard-ui/model"
|
"wireguard-ui/model"
|
||||||
@ -84,3 +85,60 @@ func (s setting) GetAllSetting(blackList []string) (data []vo.SettingItem, err e
|
|||||||
err = s.Model(&model.Setting{}).Select("code, data, describe").Where("code not in ?", blackList).Find(&data).Error
|
err = s.Model(&model.Setting{}).Select("code, data, describe").Where("code not in ?", blackList).Find(&data).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Export
|
||||||
|
// @description: 导出
|
||||||
|
// @receiver s
|
||||||
|
// @return data
|
||||||
|
// @return err
|
||||||
|
func (s setting) Export() (data vo.Export, err error) {
|
||||||
|
// 先查询global配置
|
||||||
|
var gs, ss *model.Setting
|
||||||
|
if err = s.Model(&model.Setting{}).Where("code = ?", "WG_SETTING").Take(&gs).Error; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal([]byte(gs.Data), &data.Global); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询server配置
|
||||||
|
if err = s.Model(&model.Setting{}).Where("code = ?", "WG_SERVER").Take(&ss).Error; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal([]byte(ss.Data), &data.Server); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询client配置
|
||||||
|
var clients []vo.ClientItem
|
||||||
|
if err = s.Model(&model.Client{}).Select("id,name,email,ip_allocation as ip_allocation_str," +
|
||||||
|
"allowed_ips as allowed_ips_str,extra_allowed_ips as extra_allowed_ips_str," +
|
||||||
|
"endpoint,use_server_dns,keys as keys_str," +
|
||||||
|
"enabled,offline_monitoring").Order("created_at DESC").Find(&clients).Error; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range clients {
|
||||||
|
if v.KeysStr != "" {
|
||||||
|
_ = json.Unmarshal([]byte(v.KeysStr), &clients[i].Keys)
|
||||||
|
}
|
||||||
|
if v.IpAllocationStr != "" {
|
||||||
|
clients[i].IpAllocation = strings.Split(v.IpAllocationStr, ",")
|
||||||
|
}
|
||||||
|
if v.AllowedIpsStr != "" {
|
||||||
|
clients[i].AllowedIps = strings.Split(v.AllowedIpsStr, ",")
|
||||||
|
} else {
|
||||||
|
clients[i].AllowedIps = []string{}
|
||||||
|
}
|
||||||
|
if v.ExtraAllowedIpsStr != "" {
|
||||||
|
clients[i].ExtraAllowedIps = strings.Split(v.ExtraAllowedIpsStr, ",")
|
||||||
|
} else {
|
||||||
|
clients[i].ExtraAllowedIps = []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cj, _ := json.Marshal(clients)
|
||||||
|
_ = json.Unmarshal(cj, &data.Clients)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
28
utils/file.go
Normal file
28
utils/file.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileUtils struct{}
|
||||||
|
|
||||||
|
func FileUtils() fileUtils {
|
||||||
|
return fileUtils{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateFile
|
||||||
|
// @description: 生成文件
|
||||||
|
// @receiver fileUtils
|
||||||
|
// @param filename 文件名称
|
||||||
|
// @param content 文件内容
|
||||||
|
// @return error
|
||||||
|
func (fileUtils) GenerateFile(filename string, content []byte) (filepath string, err error) {
|
||||||
|
path := "/tmp/"
|
||||||
|
if os.Getenv("GIN_MODE") != "release" {
|
||||||
|
path = "E:\\Workspace\\Go\\wireguard-ui\\template\\tmp\\"
|
||||||
|
}
|
||||||
|
filepath = fmt.Sprintf("%s%s", path, filename)
|
||||||
|
err = os.WriteFile(filepath, content, 0777)
|
||||||
|
return filepath, err
|
||||||
|
}
|
5
web/src/api/setting.js
Normal file
5
web/src/api/setting.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { request } from '@/utils'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
exportConfig: () => request.get('/setting/export'), // 获取当前登陆用户信息
|
||||||
|
}
|
40
web/src/layout/components/header/components/Export.vue
Normal file
40
web/src/layout/components/header/components/Export.vue
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<n-icon mr-20 size="18" style="cursor: pointer" @click="importConfig()">
|
||||||
|
<icon-gg-import />
|
||||||
|
</n-icon>
|
||||||
|
<n-icon mr-20 size="18" style="cursor: pointer" @click="exportConfig()">
|
||||||
|
<icon-ph-export-bold />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import api from '@/api/setting'
|
||||||
|
// 导入
|
||||||
|
function importConfig() {
|
||||||
|
console.log('导入配置')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
function exportConfig() {
|
||||||
|
$dialog.confirm({
|
||||||
|
type: 'warning',
|
||||||
|
title: '导出配置',
|
||||||
|
content: `是否导出需要导出系统全部配置?`,
|
||||||
|
async confirm() {
|
||||||
|
api.exportConfig().then(response => {
|
||||||
|
const blob = new Blob([JSON.stringify(response.data)], {
|
||||||
|
type: "text/plain"
|
||||||
|
});
|
||||||
|
const link = document.createElement("a"); // 创建a标签
|
||||||
|
link.download = "config.json"; // a标签添加属性
|
||||||
|
link.style.display = "none";
|
||||||
|
link.href = URL.createObjectURL(blob);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click(); // 执行下载
|
||||||
|
URL.revokeObjectURL(link.href); // 释放url
|
||||||
|
document.body.removeChild(link); // 释放标签
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
@ -3,7 +3,12 @@
|
|||||||
<MenuCollapse />
|
<MenuCollapse />
|
||||||
<BreadCrumb ml-15 hidden sm:block />
|
<BreadCrumb ml-15 hidden sm:block />
|
||||||
</div>
|
</div>
|
||||||
<div ml-auto flex items-center>
|
<div ml-auto flex items-center v-if="loginUser.account === 'admin'">
|
||||||
|
<Export/>
|
||||||
|
<FullScreen />
|
||||||
|
<UserAvatar />
|
||||||
|
</div>
|
||||||
|
<div ml-auto flex items-center v-else>
|
||||||
<FullScreen />
|
<FullScreen />
|
||||||
<UserAvatar />
|
<UserAvatar />
|
||||||
</div>
|
</div>
|
||||||
@ -14,4 +19,7 @@ import BreadCrumb from './components/BreadCrumb.vue'
|
|||||||
import MenuCollapse from './components/MenuCollapse.vue'
|
import MenuCollapse from './components/MenuCollapse.vue'
|
||||||
import FullScreen from './components/FullScreen.vue'
|
import FullScreen from './components/FullScreen.vue'
|
||||||
import UserAvatar from './components/UserAvatar.vue'
|
import UserAvatar from './components/UserAvatar.vue'
|
||||||
|
import Export from './components/Export.vue'
|
||||||
|
import { useUserStore } from '@/store'
|
||||||
|
const loginUser = useUserStore()
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user