Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fc0a1a04b0 | ||
|
19a025e888 | ||
|
dfe89d465c | ||
|
93911536d9 | ||
|
f09d0d2994 | ||
|
ca42c72e0f | ||
|
683e7b2cc3 | ||
|
accb060e27 | ||
|
8884d945aa | ||
|
788def6dc4 | ||
|
29c1c791d4 | ||
|
40019568f0 | ||
|
db065073e2 | ||
|
befaa17426 | ||
|
fae96eccf5 | ||
|
4a0ec7bdc9 | ||
|
331849522f | ||
|
fa04d9c83a | ||
|
958b1b4aec | ||
|
72b8473591 | ||
|
13e4006592 | ||
|
f2dcb13e0d | ||
|
edaf9ba770 | ||
|
c3ef51e87f | ||
|
72420f2ede | ||
|
a12552a608 | ||
|
5f200ea989 | ||
|
3f14df72be | ||
|
29902afe65 | ||
ddef41dfca | |||
|
6c6b40593e | ||
|
45d83da5c7 | ||
|
4fa123baa8 |
56
.gitignore
vendored
@@ -233,33 +233,33 @@ fabric.properties
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
./go.work
|
||||
|
||||
.idea
|
||||
web/.idea
|
||||
./.idea
|
||||
./web/.idea
|
||||
|
||||
web/node_modules
|
||||
web/.DS_Store
|
||||
web/dist
|
||||
web/dist-ssr
|
||||
web/*.local
|
||||
web/.eslintcache
|
||||
web/report.html
|
||||
web/vite.config.*.timestamp*
|
||||
./web/node_modules
|
||||
./web/.DS_Store
|
||||
./web/dist
|
||||
./web/dist-ssr
|
||||
./web/*.local
|
||||
./web/.eslintcache
|
||||
./web/report.html
|
||||
./web/vite.config.*.timestamp*
|
||||
|
||||
web/yarn.lock
|
||||
web/npm-debug.log*
|
||||
web/.pnpm-error.log*
|
||||
web/.pnpm-debug.log
|
||||
web/tests/**/coverage/
|
||||
web/.vscode/
|
||||
./web/yarn.lock
|
||||
./web/npm-debug.log*
|
||||
./web/.pnpm-error.log*
|
||||
./web/.pnpm-debug.log
|
||||
./web/tests/**/coverage/
|
||||
./web/.vscode/
|
||||
|
||||
# Editor directories and files
|
||||
web/*.suo
|
||||
web/*.ntvs*
|
||||
web/*.njsproj
|
||||
web/*.sln
|
||||
web/tsconfig.tsbuildinfo
|
||||
./web/*.suo
|
||||
./web/*.ntvs*
|
||||
./web/*.njsproj
|
||||
./web/*.sln
|
||||
./web/tsconfig.tsbuildinfo
|
||||
|
||||
dist/assets
|
||||
dist/resource
|
||||
@@ -267,10 +267,10 @@ dist/favicon.png
|
||||
dist/favicon.svg
|
||||
dist/index.html
|
||||
|
||||
template/tmp/*
|
||||
logs/*
|
||||
app.yaml
|
||||
*.db
|
||||
.env
|
||||
*.env
|
||||
./template/tmp/*
|
||||
./logs/*
|
||||
./app.yaml
|
||||
./*.db
|
||||
./.env
|
||||
./*.env
|
||||
|
||||
|
94
README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Wireguard-UI
|
||||
> wireguard的管理面板UI
|
||||
|
||||
## 安装(仅提供docker方式)
|
||||
|
||||
OS X & Linux & Windows:
|
||||
|
||||
```sh
|
||||
# 先要创建一个配置文件 app.yaml
|
||||
http:
|
||||
port: 6687
|
||||
endpoint: localhost:3100,localhost:6687
|
||||
|
||||
database:
|
||||
driver: sqlite # sqlite时只填写db即可,目前仅支持sqlite | mysql | pgsql
|
||||
host:
|
||||
port:
|
||||
user:
|
||||
password:
|
||||
db: wg
|
||||
|
||||
cache:
|
||||
type: redis # 缓存类型 暂时仅支持redis
|
||||
host: 192.168.1.1
|
||||
port: 6379
|
||||
password: pGhQKwj7DE7FbFL1
|
||||
db: 15
|
||||
|
||||
file:
|
||||
type: oss # 文件类型支持本地文件存储与阿里云oss存储
|
||||
path: test/ # oss填写前缀目录
|
||||
endpoint: # oss必填
|
||||
accessId: # oss必填
|
||||
accessSecret: # oss必填
|
||||
bucketName: # oss必填
|
||||
|
||||
# 一些系统配置
|
||||
wireguard:
|
||||
restartMode: DELAY
|
||||
delayTime: 20
|
||||
|
||||
# 其中依赖了redis等,自行安装一个即可
|
||||
```
|
||||
|
||||
```sh
|
||||
# 创建docker-compose.yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
wg:
|
||||
image: gitea.mrx.ltd/go-pkg/wireguard-srv:2.1.0
|
||||
container_name: wg-srv
|
||||
restart: always
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
network_mode: host
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: 50m
|
||||
volumes:
|
||||
- ./app.yaml:/app/app.yaml
|
||||
- ./db:/app/db
|
||||
- ./logs:/app/logs
|
||||
- /etc/wireguard:/etc/wireguard
|
||||
- /etc/localtime:/etc/localtime
|
||||
```
|
||||
```sh
|
||||
默认账户密码
|
||||
账户: admin
|
||||
密码: admin123
|
||||
```
|
||||
## 配置示例
|
||||
```text
|
||||
1. 邮箱配置如下:
|
||||
code: EMAIL_SMTP
|
||||
配置项:
|
||||
1. host: "xxxx.xxx"
|
||||
2. port: "123"
|
||||
3. user: "haha"
|
||||
4. password: "haha123"
|
||||
5. skipTls: "false"
|
||||
```
|
||||
|
||||
## 页面展示
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
@@ -36,6 +36,18 @@ func Error(err error) string {
|
||||
return errMsg
|
||||
}
|
||||
|
||||
// Validate
|
||||
// @description: 校验
|
||||
// @param data
|
||||
// @return string
|
||||
func Validate(data any) error {
|
||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
return v.Struct(data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// initValidatorTranslator
|
||||
// @description: 初始化翻译机
|
||||
// @receiver vli
|
||||
|
@@ -97,8 +97,8 @@ func (w WireguardComponent) GenerateClientFile(clientInfo *model.Client, server
|
||||
var outPath = "/tmp/" + fmt.Sprintf("%s.conf", clientInfo.Name)
|
||||
var templatePath = "./template/wg.client.conf"
|
||||
if os.Getenv("GIN_MODE") != "release" {
|
||||
outPath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\" + fmt.Sprintf("%s.conf", clientInfo.Name)
|
||||
templatePath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\wg.client.conf"
|
||||
outPath = "E:\\Workspace\\Go\\wireguard-ui\\template\\tmp\\" + fmt.Sprintf("%s.conf", clientInfo.Name)
|
||||
templatePath = "E:\\Workspace\\Go\\wireguard-ui\\template\\conf\\wg.client.conf"
|
||||
}
|
||||
|
||||
err = Template().Execute(templatePath, outPath, execData)
|
||||
|
@@ -7,6 +7,5 @@ type config struct {
|
||||
Database *database `yaml:"database"`
|
||||
Cache *cache `yaml:"redis"`
|
||||
File *file `yaml:"file"`
|
||||
Mail *mail `yaml:"email"`
|
||||
Wireguard *wireguard `yaml:"wireguard"`
|
||||
}
|
||||
|
@@ -1,9 +0,0 @@
|
||||
package config
|
||||
|
||||
type mail struct {
|
||||
Host string `json:"host" yaml:"host"`
|
||||
Port int `json:"port" yaml:"port"`
|
||||
User string `json:"user" yaml:"user"`
|
||||
Password string `json:"password" yaml:"password"`
|
||||
SkipTls bool `json:"skipTls" yaml:"skipTls"`
|
||||
}
|
19
cron/cron.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package cron
|
||||
|
||||
import (
|
||||
"gitee.ltd/lxh/logger/log"
|
||||
"github.com/go-co-op/gocron/v2"
|
||||
"time"
|
||||
"wireguard-ui/cron/task"
|
||||
)
|
||||
|
||||
func Task() {
|
||||
sch, err := gocron.NewScheduler(gocron.WithLocation(time.Local))
|
||||
if err != nil {
|
||||
log.Errorf("初始化定时任务失败")
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = sch.NewJob(gocron.DurationJob(1*time.Minute), gocron.NewTask(task.NetworkClient().ClientOfflineNotify)) // 每分钟执行一次
|
||||
sch.Start()
|
||||
}
|
100
cron/task/client.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.ltd/lxh/logger/log"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"golang.org/x/exp/slices"
|
||||
"strings"
|
||||
"time"
|
||||
"wireguard-ui/component"
|
||||
"wireguard-ui/global/client"
|
||||
"wireguard-ui/model"
|
||||
"wireguard-ui/service"
|
||||
"wireguard-ui/utils"
|
||||
)
|
||||
|
||||
type NetworkClientImpl interface {
|
||||
ClientOfflineNotify() // 客户端离线通知
|
||||
}
|
||||
|
||||
type networkClient struct{}
|
||||
|
||||
func NetworkClient() NetworkClientImpl {
|
||||
return networkClient{}
|
||||
}
|
||||
|
||||
// ClientOfflineNotify
|
||||
// @description: 客户端离线通知
|
||||
// @receiver c
|
||||
// @return error
|
||||
func (c networkClient) ClientOfflineNotify() {
|
||||
log.Debugf("开始执行离线通知任务")
|
||||
// 开始扫描已经链接过的客户端
|
||||
connectedPeers, err := component.Wireguard().GetClients()
|
||||
if err != nil {
|
||||
log.Errorf("获取已连接客户端失败: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 查询一下通知配置
|
||||
code, err := service.Setting().GetByCode("WECHAT_NOTIFY")
|
||||
if err != nil {
|
||||
log.Errorf("获取微信通知配置失败: %v", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 查询出所有配置了离线通知的客户端
|
||||
var clients []model.Client
|
||||
if err := client.DB.Where("offline_monitoring = ?", 1).Find(&clients).Error; err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(clients) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, peer := range connectedPeers {
|
||||
var clientName string
|
||||
if !slices.ContainsFunc(clients, func(cli model.Client) bool {
|
||||
isExist := peer.PublicKey.String() == jsoniter.Get([]byte(cli.Keys), "publicKey").ToString()
|
||||
if isExist {
|
||||
clientName = cli.Name
|
||||
}
|
||||
return isExist
|
||||
}) {
|
||||
continue
|
||||
}
|
||||
|
||||
online := time.Since(peer.LastHandshakeTime).Minutes() < 3
|
||||
log.Debugf("客户端[%v]在线状态: %v,离线时间: %v", clientName, online, time.Since(peer.LastHandshakeTime).Minutes())
|
||||
|
||||
// 如果存在,判断离线时间
|
||||
if !online {
|
||||
var ipAllocation string
|
||||
for _, iaip := range peer.AllowedIPs {
|
||||
ipAllocation += iaip.String() + ","
|
||||
}
|
||||
// 去除一下最右边的逗号
|
||||
if len(ipAllocation) > 0 {
|
||||
ipAllocation = strings.TrimRight(ipAllocation, ",")
|
||||
}
|
||||
|
||||
// 已经离线,发送通知
|
||||
msg := fmt.Sprintf(`
|
||||
[离线通知] \n
|
||||
客户端名称 : %v \n
|
||||
客户端IP : %v \n
|
||||
最后在线时间 : %v
|
||||
`, clientName, ipAllocation, peer.LastHandshakeTime.Format("2006-01-02 15:04:05"))
|
||||
err := utils.WechatNotify(code).SendTextMessage(msg)
|
||||
if err != nil {
|
||||
log.Errorf("微信消息[%v]通知失败: %v", msg, err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
BIN
document/img.png
Normal file
After Width: | Height: | Size: 209 KiB |
BIN
document/img_1.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
document/img_2.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
document/img_3.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
document/img_4.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
document/img_5.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
document/img_6.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
document/img_7.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
document/img_8.png
Normal file
After Width: | Height: | Size: 55 KiB |
45
go.mod
@@ -3,16 +3,31 @@ module wireguard-ui
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
gitee.ltd/lxh/logger v1.0.15
|
||||
gitee.ltd/lxh/logger v1.0.18
|
||||
github.com/cowardmrx/go_aliyun_oss v1.0.7
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gin-contrib/pprof v1.5.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-co-op/gocron/v2 v2.12.4
|
||||
github.com/go-playground/locales v0.14.1
|
||||
github.com/go-playground/universal-translator v0.18.1
|
||||
github.com/go-playground/validator/v10 v10.22.0
|
||||
github.com/go-resty/resty/v2 v2.13.1
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/mojocn/base64Captcha v1.3.6
|
||||
github.com/redis/go-redis/v9 v9.5.3
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/spf13/cast v1.6.0
|
||||
github.com/spf13/viper v1.19.0
|
||||
golang.org/x/crypto v0.23.0
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/postgres v1.5.9
|
||||
gorm.io/gorm v1.25.10
|
||||
@@ -21,33 +36,26 @@ require (
|
||||
require (
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.5 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||
github.com/caarlos0/env/v6 v6.10.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/cors v1.7.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
github.com/go-kit/kit v0.12.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||
@@ -55,10 +63,9 @@ require (
|
||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
|
||||
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||
github.com/josharian/native v1.1.0 // indirect
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lixh00/loki-client-go v1.0.1 // indirect
|
||||
@@ -71,9 +78,9 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mojocn/base64Captcha v1.3.6 // indirect
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
|
||||
github.com/panjf2000/ants/v2 v2.10.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||
@@ -82,12 +89,11 @@ require (
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
@@ -96,21 +102,18 @@ require (
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.23.0 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/image v0.13.0 // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
44
go.sum
@@ -35,8 +35,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
gitee.ltd/lxh/logger v1.0.15 h1:x/HIHujq01ofltBK6WduYs890hiJVlU2ES6ytKusVZA=
|
||||
gitee.ltd/lxh/logger v1.0.15/go.mod h1:Ef3o9duDaGApRUlibvkG92QBFM3HcndI0U30n/SMfYk=
|
||||
gitee.ltd/lxh/logger v1.0.18 h1:LKc0Glk+jx08qDaX1kYIEHZUxUJtV3uqP5ceYK352YY=
|
||||
gitee.ltd/lxh/logger v1.0.18/go.mod h1:hzd8prYXbac9YMaEDFMHYHMKKm1LqiFVtHjENWi+qQE=
|
||||
github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
@@ -122,10 +122,11 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic v1.12.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w=
|
||||
github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
|
||||
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
|
||||
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
|
||||
@@ -210,8 +211,6 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
||||
github.com/gin-contrib/pprof v1.5.0 h1:E/Oy7g+kNw94KfdCy3bZxQFtyDnAX2V7axRS7sNYVrU=
|
||||
github.com/gin-contrib/pprof v1.5.0/go.mod h1:GqFL6LerKoCQ/RSWnkYczkTJ+tOAUVN/8sbnEtaqOKs=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
@@ -226,6 +225,8 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
|
||||
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
|
||||
github.com/go-co-op/gocron/v2 v2.12.4 h1:h1HWApo3T+61UrZqEY2qG1LUpDnB7tkYITxf6YIK354=
|
||||
github.com/go-co-op/gocron/v2 v2.12.4/go.mod h1:xY7bJxGazKam1cz04EebrlP4S9q4iWdiAylMGP3jY9w=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -309,8 +310,6 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
|
||||
@@ -512,6 +511,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
@@ -680,6 +681,8 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
|
||||
github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
|
||||
@@ -755,6 +758,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@@ -828,6 +833,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
@@ -877,8 +883,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
@@ -889,7 +895,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
|
||||
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -926,13 +931,14 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
|
||||
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -1028,8 +1034,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1121,8 +1128,9 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@@ -243,7 +243,14 @@ func (ClientApi) Download(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err = utils.Mail().SendMail(data.Email, fmt.Sprintf("客户端: %s", data.Name), "请查收附件", outPath)
|
||||
// 获取邮箱配置
|
||||
emailConf, err := service.Setting().GetByCode("EMAIL_SMTP")
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError("获取邮箱配置失败,请先到设置页面的【其他】里面添加code为【EMAIL_SMTP】的具体配置")
|
||||
return
|
||||
}
|
||||
|
||||
err = utils.Mail(emailConf).SendMail(data.Email, fmt.Sprintf("客户端: %s", data.Name), "请查收附件", outPath)
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError("发送邮件失败")
|
||||
return
|
||||
|
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strings"
|
||||
"time"
|
||||
"wireguard-ui/component"
|
||||
"wireguard-ui/http/param"
|
||||
@@ -77,6 +78,10 @@ func (DashboardApi) ConnectionList(c *gin.Context) {
|
||||
for _, iaip := range peer.AllowedIPs {
|
||||
ipAllocation += iaip.String() + ","
|
||||
}
|
||||
// 去除一下最右边的逗号
|
||||
if len(ipAllocation) > 0 {
|
||||
ipAllocation = strings.TrimRight(ipAllocation, ",")
|
||||
}
|
||||
connections = append(connections, vo.DataTraffic{
|
||||
Name: clientInfo.Name,
|
||||
Email: clientInfo.Email,
|
||||
|
33
http/api/remote.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package api
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
type remote struct{}
|
||||
|
||||
func Remote() remote {
|
||||
return remote{}
|
||||
}
|
||||
|
||||
// SaveAuthClient
|
||||
// @description: 添加授权客户端
|
||||
// @receiver remote
|
||||
// @param c
|
||||
func (remote) SaveAuthClient(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteAuthClient
|
||||
// @description: 删除授权客户端
|
||||
// @receiver remote
|
||||
// @param c
|
||||
func (remote) DeleteAuthClient(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// GetClientNodes
|
||||
// @description: 获取客户端节点
|
||||
// @receiver remote
|
||||
// @param c
|
||||
func (remote) GetClientNodes(c *gin.Context) {
|
||||
return
|
||||
}
|
@@ -1,11 +1,17 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitee.ltd/lxh/logger/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"os"
|
||||
"slices"
|
||||
"wireguard-ui/component"
|
||||
"wireguard-ui/http/param"
|
||||
"wireguard-ui/http/response"
|
||||
"wireguard-ui/http/vo"
|
||||
"wireguard-ui/model"
|
||||
"wireguard-ui/script"
|
||||
"wireguard-ui/service"
|
||||
@@ -113,3 +119,99 @@ func (setting) GetAllSetting(c *gin.Context) {
|
||||
func (setting) GetPublicAddr(c *gin.Context) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
// Import
|
||||
// @description: 导入配置
|
||||
// @receiver setting
|
||||
// @param c
|
||||
func (setting) Import(c *gin.Context) {
|
||||
var p param.Import
|
||||
if err := c.ShouldBind(&p); err != nil {
|
||||
response.R(c).Validator(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 校验文件是否合规
|
||||
if p.File.Filename != "config.json" {
|
||||
response.R(c).Validator(errors.New("文件名不合规"))
|
||||
return
|
||||
}
|
||||
|
||||
// 校验文件内容是否符合
|
||||
fileBytes, err := p.File.Open()
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
var data vo.Export
|
||||
if err := json.NewDecoder(fileBytes).Decode(&data); err != nil {
|
||||
response.R(c).FailedWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 校验json串是否合规
|
||||
if err = component.Validate(&data); err != nil {
|
||||
response.R(c).Validator(err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前登陆用户
|
||||
var loginUser *vo.User
|
||||
if loginUser = GetCurrentLoginUser(c); c.IsAborted() {
|
||||
return
|
||||
}
|
||||
|
||||
if loginUser.Account != "admin" {
|
||||
response.R(c).FailedWithError("非法操作,你被捕啦!")
|
||||
return
|
||||
}
|
||||
|
||||
if err = service.Setting().Import(&data, loginUser); err != nil {
|
||||
response.R(c).FailedWithError(fmt.Errorf("导入失败: %v", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
response.R(c).OK()
|
||||
}
|
||||
|
@@ -50,13 +50,13 @@ func Authorization() gin.HandlerFunc {
|
||||
// 查询用户
|
||||
user, err := service.User().GetUserById(userClaims.ID)
|
||||
if err != nil {
|
||||
response.R(c).FailedWithError("用户不存在")
|
||||
response.R(c).AuthorizationFailed("用户不存在")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if user.Status != constant.Enabled {
|
||||
response.R(c).FailedWithError("用户状态异常,请联系管理员处理!")
|
||||
response.R(c).AuthorizationFailed("用户状态异常,请联系管理员处理!")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package param
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
// SetSetting
|
||||
// @description: 添加/编辑设置
|
||||
type SetSetting struct {
|
||||
@@ -7,3 +9,9 @@ type SetSetting struct {
|
||||
Data string `json:"data" form:"data" binding:"required"`
|
||||
Describe string `json:"describe" form:"describe" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// Import
|
||||
// @description: 导入
|
||||
type Import struct {
|
||||
File *multipart.FileHeader `json:"file" form:"file" binding:"required"`
|
||||
}
|
||||
|
@@ -17,5 +17,7 @@ func SettingApi(r *gin.RouterGroup) {
|
||||
setting.GET("", api.Setting().GetSetting) // 获取指定配置
|
||||
setting.GET("/all", api.Setting().GetAllSetting) // 获取全部配置
|
||||
setting.GET("/public-addr", api.Setting().GetPublicAddr) // 获取公网IP
|
||||
setting.GET("/export", api.Setting().Export) // 导出配置文件
|
||||
setting.POST("/import", api.Setting().Import) // 导入配置
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package vo
|
||||
|
||||
import "wireguard-ui/global/constant"
|
||||
|
||||
// SettingItem
|
||||
// @description: 设置单项
|
||||
type SettingItem struct {
|
||||
@@ -7,3 +9,61 @@ type SettingItem struct {
|
||||
Data string `json:"data"`
|
||||
Describe string `json:"describe"`
|
||||
}
|
||||
|
||||
type Export struct {
|
||||
Global *Global `json:"global" label:"全局配置" binding:"required"`
|
||||
Server *Server `json:"server" label:"服务端配置" binding:"required"`
|
||||
Clients []Client `json:"clients" label:"客户端" binding:"omitempty"`
|
||||
Other []Other `json:"other" label:"其他" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// Global
|
||||
// @description: 全局配置
|
||||
type Global struct {
|
||||
MTU int `json:"MTU" label:"MTU" binding:"required"`
|
||||
ConfigFilePath string `json:"configFilePath" label:"配置文件路径" binding:"required"`
|
||||
DnsServer []string `json:"dnsServer" label:"DNS" binding:"omitempty"`
|
||||
EndpointAddress string `json:"endpointAddress" label:"公网地址" binding:"required"`
|
||||
FirewallMark string `json:"firewallMark" label:"firewallMark" binding:"omitempty"`
|
||||
PersistentKeepalive int `json:"persistentKeepalive" label:"persistentKeepalive" binding:"required"`
|
||||
Table string `json:"table" label:"table" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// Server
|
||||
// @description: 服务端信息
|
||||
type Server struct {
|
||||
IpScope []string `json:"ipScope" label:"ipScope" binding:"min=1,dive,required"`
|
||||
ListenPort int `json:"listenPort" label:"listenPort" binding:"required"`
|
||||
PrivateKey string `json:"privateKey" label:"privateKey" binding:"required"`
|
||||
PublicKey string `json:"publicKey" label:"publicKey" binding:"required"`
|
||||
PostUpScript string `json:"postUpScript" label:"postUpScript" binding:"omitempty"`
|
||||
PostDownScript string `json:"postDownScript" label:"postDownScript" binding:"omitempty"`
|
||||
}
|
||||
|
||||
// Client
|
||||
// @description: 客户端信息
|
||||
type Client struct {
|
||||
Name string `json:"name" label:"name" binding:"required"`
|
||||
Email string `json:"email" label:"email" binding:"omitempty"`
|
||||
SubnetRange string `json:"subnetRange" label:"subnetRange" binding:"omitempty"`
|
||||
IpAllocation []string `json:"ipAllocation" label:"ipAllocation" binding:"min=1,dive,required"`
|
||||
AllowedIps []string `json:"allowedIps" label:"allowedIps" binding:"min=1,dive,required"`
|
||||
ExtraAllowedIps []string `json:"extraAllowedIps" label:"extraAllowedIps" binding:"omitempty"`
|
||||
Endpoint string `json:"endpoint" label:"endpoint" binding:"endpoint"`
|
||||
UseServerDns *constant.Status `json:"useServerDns" label:"useServerDns" binding:"omitempty"`
|
||||
Keys struct {
|
||||
PresharedKey string `json:"presharedKey" label:"presharedKey" binding:"required"`
|
||||
PrivateKey string `json:"privateKey" label:"privateKey" binding:"required"`
|
||||
PublicKey string `json:"publicKey" label:"publicKey" binding:"required"`
|
||||
} `json:"keys" label:"keys" binding:"required"`
|
||||
Enabled *constant.Status `json:"enabled" label:"enabled" binding:"required"`
|
||||
OfflineMonitoring *constant.Status `json:"offlineMonitoring" label:"offlineMonitoring" binding:"required"`
|
||||
}
|
||||
|
||||
// Other
|
||||
// @description: 其他配置
|
||||
type Other struct {
|
||||
Code string `json:"code" label:"code" binding:"required"`
|
||||
Data string `json:"data" label:"data" binding:"required"`
|
||||
Describe string `json:"describe" label:"describe" binding:"omitempty"`
|
||||
}
|
||||
|
5
main.go
@@ -10,6 +10,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
"wireguard-ui/config"
|
||||
"wireguard-ui/cron"
|
||||
"wireguard-ui/http/router"
|
||||
"wireguard-ui/initialize"
|
||||
"wireguard-ui/script"
|
||||
@@ -20,6 +21,7 @@ func init() {
|
||||
if err := script.New().Do(); err != nil {
|
||||
log.Errorf("执行脚本失败: %v", err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -31,6 +33,9 @@ func main() {
|
||||
pprof.Register(handler, "/monitoring")
|
||||
}
|
||||
|
||||
// 启动定时任务
|
||||
cron.Task()
|
||||
|
||||
httpServe := http.Server{
|
||||
Addr: fmt.Sprintf(":%d", config.Config.Http.Port),
|
||||
Handler: handler,
|
||||
|
@@ -22,3 +22,16 @@ type Client struct {
|
||||
func (Client) TableName() string {
|
||||
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,7 +3,7 @@ package service
|
||||
import "gorm.io/gorm"
|
||||
|
||||
func Paginate(current, size int64) func(db *gorm.DB) *gorm.DB {
|
||||
// 如果页码是-1,就不分页
|
||||
// 如果页码是-1,就不分页👋
|
||||
if current == -1 {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db
|
||||
|
@@ -2,11 +2,16 @@ package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
slog "gitee.ltd/lxh/logger/log"
|
||||
"gorm.io/gorm"
|
||||
"strings"
|
||||
gdb "wireguard-ui/global/client"
|
||||
"wireguard-ui/http/param"
|
||||
"wireguard-ui/http/vo"
|
||||
"wireguard-ui/model"
|
||||
"wireguard-ui/template/render_data"
|
||||
"wireguard-ui/utils"
|
||||
)
|
||||
|
||||
type setting struct{ *gorm.DB }
|
||||
@@ -84,3 +89,154 @@ 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
|
||||
return
|
||||
}
|
||||
|
||||
// GetByCode
|
||||
// @description: 获取指定code的配置
|
||||
// @receiver s
|
||||
// @param code
|
||||
// @return data
|
||||
// @return err
|
||||
func (s setting) GetByCode(code string) (data *model.Setting, err error) {
|
||||
err = s.Model(&model.Setting{}).Where("code = ?", code).Take(&data).Error
|
||||
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{}
|
||||
}
|
||||
}
|
||||
|
||||
// 查询其他配置
|
||||
var others []vo.Other
|
||||
if err = s.Model(&model.Setting{}).Where("code not in ?", []string{"WG_SETTING", "WG_SERVER"}).Find(&others).Error; err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
data.Other = others
|
||||
cj, _ := json.Marshal(clients)
|
||||
_ = json.Unmarshal(cj, &data.Clients)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Import
|
||||
// @description: 导入
|
||||
// @receiver s
|
||||
// @param data
|
||||
// @return err
|
||||
func (s setting) Import(data *vo.Export, loginUser *vo.User) (err error) {
|
||||
// 获取导入系统的公网IP地址
|
||||
pubAddr := utils.Network().GetHostPublicIP()
|
||||
data.Global.EndpointAddress = pubAddr
|
||||
// 先更新global配置
|
||||
gst, _ := json.Marshal(data.Global)
|
||||
gs := &model.Setting{
|
||||
Code: "WG_SETTING",
|
||||
Data: string(gst),
|
||||
Describe: "服务端全局配置",
|
||||
}
|
||||
if err = s.SetData(gs); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
st, _ := json.Marshal(data.Server)
|
||||
ss := &model.Setting{
|
||||
Code: "WG_SERVER",
|
||||
Data: string(st),
|
||||
Describe: "服务端配置",
|
||||
}
|
||||
if err = s.SetData(ss); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 更新client配置
|
||||
for _, v := range data.Clients {
|
||||
keys := ¶m.Keys{
|
||||
PrivateKey: v.Keys.PrivateKey,
|
||||
PublicKey: v.Keys.PublicKey,
|
||||
PresharedKey: v.Keys.PresharedKey,
|
||||
}
|
||||
cc := param.SaveClient{
|
||||
Name: v.Name,
|
||||
Email: v.Email,
|
||||
IpAllocation: v.IpAllocation,
|
||||
AllowedIps: v.AllowedIps,
|
||||
ExtraAllowedIps: v.ExtraAllowedIps,
|
||||
UseServerDns: v.UseServerDns,
|
||||
Keys: keys,
|
||||
Enabled: v.Enabled,
|
||||
OfflineMonitoring: v.OfflineMonitoring,
|
||||
}
|
||||
|
||||
if v.Endpoint != "" {
|
||||
port := strings.Split(v.Endpoint, ":")[1]
|
||||
endpoint := fmt.Sprintf("%s:%s", pubAddr, port)
|
||||
cc.Endpoint = endpoint
|
||||
}
|
||||
|
||||
if err := Client().SaveClient(cc, loginUser); err != nil {
|
||||
slog.Errorf("客户端[%s]导入失败: %v", v.Name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 其他配置写入
|
||||
for _, v := range data.Other {
|
||||
if err = s.SetData(&model.Setting{
|
||||
Code: v.Code,
|
||||
Data: v.Data,
|
||||
Describe: v.Describe,
|
||||
}); err != nil {
|
||||
slog.Errorf("其他配置[%s]导入失败: %v", v.Code, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
gdb "wireguard-ui/global/client"
|
||||
"wireguard-ui/global/constant"
|
||||
@@ -39,6 +40,11 @@ func (s user) CreateUser(user *model.User) (err error) {
|
||||
return s.Model(&model.User{}).Where("id = ?", user.Id).Updates(&updates).Error
|
||||
}
|
||||
|
||||
// 判断账号是否已经存在
|
||||
if _, err = s.GetUserByAccount(user.Account); err == nil {
|
||||
return errors.New("账号已经存在,请勿重复创建!")
|
||||
}
|
||||
|
||||
defaultPassword := utils.Password().GenerateHashPassword("admin123")
|
||||
if user.Password == "" { // 没有密码给一个默认密码
|
||||
user.Password = defaultPassword
|
||||
|
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
|
||||
}
|
@@ -2,28 +2,35 @@ package utils
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jordan-wright/email"
|
||||
"github.com/spf13/cast"
|
||||
"mime"
|
||||
"net/smtp"
|
||||
"net/textproto"
|
||||
"path/filepath"
|
||||
"wireguard-ui/config"
|
||||
"wireguard-ui/model"
|
||||
)
|
||||
|
||||
type mail struct {
|
||||
*email.Email
|
||||
addr string
|
||||
auth smtp.Auth
|
||||
conf map[string]string
|
||||
}
|
||||
|
||||
func Mail() *mail {
|
||||
func Mail(conf *model.Setting) *mail {
|
||||
// 解析配置文件
|
||||
var mailConf = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(conf.Data), &mailConf)
|
||||
var m mail
|
||||
em := email.NewEmail()
|
||||
m.Email = em
|
||||
m.auth = smtp.PlainAuth("", config.Config.Mail.User, config.Config.Mail.Password, config.Config.Mail.Host)
|
||||
m.addr = fmt.Sprintf("%s:%d", config.Config.Mail.Host, config.Config.Mail.Port)
|
||||
m.auth = smtp.PlainAuth("", mailConf["user"], mailConf["password"], mailConf["host"])
|
||||
m.addr = fmt.Sprintf("%s:%s", mailConf["host"], mailConf["port"])
|
||||
m.conf = mailConf
|
||||
return &m
|
||||
}
|
||||
|
||||
@@ -47,7 +54,7 @@ func (m *mail) VerifyConfig() (err error) {
|
||||
// @param attacheFilePath
|
||||
// @return err
|
||||
func (m *mail) SendMail(to, subject, content, attacheFilePath string) (err error) {
|
||||
m.From = fmt.Sprintf("wg-dashboard <%s>", config.Config.Mail.User)
|
||||
m.From = fmt.Sprintf("wg-dashboard <%s>", m.conf["user"])
|
||||
m.To = []string{to}
|
||||
m.Subject = subject
|
||||
m.Text = []byte(content)
|
||||
@@ -61,13 +68,13 @@ func (m *mail) SendMail(to, subject, content, attacheFilePath string) (err error
|
||||
atch.Header = emHeader
|
||||
}
|
||||
|
||||
if config.Config.Mail.SkipTls {
|
||||
if cast.ToBool(m.conf["skipTls"]) {
|
||||
return m.Send(m.addr, m.auth)
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
tlsConfig.InsecureSkipVerify = config.Config.Mail.SkipTls
|
||||
tlsConfig.ServerName = config.Config.Mail.Host
|
||||
tlsConfig.InsecureSkipVerify = cast.ToBool(m.conf["skipTls"])
|
||||
tlsConfig.ServerName = m.conf["host"]
|
||||
|
||||
return m.SendWithTLS(m.addr, m.auth, tlsConfig)
|
||||
}
|
||||
|
65
utils/wechat_notify.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitee.ltd/lxh/logger/log"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"strings"
|
||||
"wireguard-ui/global/client"
|
||||
"wireguard-ui/model"
|
||||
)
|
||||
|
||||
// wxid_472vas3av5ug22 /api/sendTextMsg
|
||||
type wechatNotify struct {
|
||||
Addr string
|
||||
Path string
|
||||
Method string
|
||||
toUserId string
|
||||
}
|
||||
|
||||
func WechatNotify(setting *model.Setting) wechatNotify {
|
||||
var sm = make(map[string]string)
|
||||
_ = json.Unmarshal([]byte(setting.Data), &sm)
|
||||
|
||||
return wechatNotify{
|
||||
Addr: sm["addr"],
|
||||
Path: sm["path"],
|
||||
Method: sm["method"],
|
||||
toUserId: sm["toUserWxId"],
|
||||
}
|
||||
}
|
||||
|
||||
// SendTextMessage
|
||||
// @description: 发送文字通知
|
||||
// @receiver website
|
||||
// @param msg
|
||||
// @return error
|
||||
func (w wechatNotify) SendTextMessage(msg string) error {
|
||||
log.Debugf("发送通知到微信: %v", msg)
|
||||
req := client.HttpClient.R()
|
||||
|
||||
req.SetHeader("Content-Type", "application/json")
|
||||
req.SetBody(map[string]string{
|
||||
"wxid": w.toUserId,
|
||||
"msg": msg,
|
||||
})
|
||||
|
||||
req.URL = fmt.Sprintf("%s:%s", w.Addr, w.Path)
|
||||
switch strings.ToUpper(w.Method) {
|
||||
case "POST":
|
||||
req.Method = "POST"
|
||||
}
|
||||
result, err := req.SetDebug(true).Send()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if jsoniter.Get(result.Body(), "code").ToInt() != 1 {
|
||||
log.Errorf("发送通知到微信失败: %v", jsoniter.Get(result.Body(), "msg").ToString())
|
||||
return errors.New("发送通知到微信失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -32,6 +32,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@unocss/eslint-config": "^0.55.7",
|
||||
"@vicons/ionicons5": "^0.12.0",
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "5.1.12",
|
||||
@@ -40,6 +41,7 @@
|
||||
"echarts": "^5.4.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"md-editor-v3": "^4.7.0",
|
||||
"mitt": "^3.0.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.1.6",
|
||||
"vite": "^4.4.11",
|
||||
|
16
web/pnpm-lock.yaml
generated
@@ -11,6 +11,9 @@ importers:
|
||||
'@unocss/eslint-config':
|
||||
specifier: ^0.55.7
|
||||
version: 0.55.7(eslint@8.50.0)(typescript@5.2.2)
|
||||
'@vicons/ionicons5':
|
||||
specifier: ^0.12.0
|
||||
version: 0.12.0
|
||||
'@vueuse/core':
|
||||
specifier: ^10.4.1
|
||||
version: 10.4.1(vue@3.3.4)
|
||||
@@ -35,6 +38,9 @@ importers:
|
||||
md-editor-v3:
|
||||
specifier: ^4.7.0
|
||||
version: 4.7.0(@codemirror/state@6.2.1)(@codemirror/view@6.21.3)(@lezer/common@1.1.0)(vue@3.3.4)
|
||||
mitt:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
mockjs:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
@@ -1046,6 +1052,9 @@ packages:
|
||||
'@vavt/util@1.4.0':
|
||||
resolution: {integrity: sha512-qLhaokwifMTFqoo4UE2JZUyaxCzX9T4WcIt2KzznbtBrCM4CG119pY/cKqq6jDa3c1phUvPCoIfjWfnF9nj4NA==}
|
||||
|
||||
'@vicons/ionicons5@0.12.0':
|
||||
resolution: {integrity: sha512-Iy1EUVRpX0WWxeu1VIReR1zsZLMc4fqpt223czR+Rpnrwu7pt46nbnC2ycO7ItI/uqDLJxnbcMC7FujKs9IfFA==}
|
||||
|
||||
'@vite-plugin-vue-devtools/core@1.0.0-rc.7':
|
||||
resolution: {integrity: sha512-Tv9JeRZQ6KDwSkOQJvXc5TBcc4fkSazA96GDhi99v4VCthTgXjnhaah41CeZD3hFDKnNS0MHKFFqN+RHAgYDyQ==}
|
||||
peerDependencies:
|
||||
@@ -3008,6 +3017,9 @@ packages:
|
||||
minimist@1.2.8:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
mixin-deep@1.3.2:
|
||||
resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -5490,6 +5502,8 @@ snapshots:
|
||||
|
||||
'@vavt/util@1.4.0': {}
|
||||
|
||||
'@vicons/ionicons5@0.12.0': {}
|
||||
|
||||
'@vite-plugin-vue-devtools/core@1.0.0-rc.7(vite@4.4.11(@types/node@20.5.1)(sass@1.69.0)(terser@5.21.0))':
|
||||
dependencies:
|
||||
'@babel/parser': 7.23.0
|
||||
@@ -7591,6 +7605,8 @@ snapshots:
|
||||
|
||||
minimist@1.2.8: {}
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
mixin-deep@1.3.2:
|
||||
dependencies:
|
||||
for-in: 1.0.2
|
||||
|
10
web/src/api/setting.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
exportConfig: () => request.get('/setting/export'), // 导出配置
|
||||
importConfig: (data) => request.post('/setting/import',data,{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}),
|
||||
}
|
126
web/src/layout/components/header/components/Export.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<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>
|
||||
<n-modal
|
||||
v-model:show="showImportUploader"
|
||||
transform-origin="center"
|
||||
preset="card"
|
||||
title="导入配置"
|
||||
:bordered="false"
|
||||
size="large"
|
||||
style="width: 400px"
|
||||
header-style="text-align: center"
|
||||
>
|
||||
<n-upload
|
||||
ref="uploadRef"
|
||||
:multiple="false"
|
||||
directory-dnd
|
||||
:max="1"
|
||||
:custom-request="customRequest"
|
||||
name="file"
|
||||
accept=".json"
|
||||
:default-upload="false"
|
||||
@before-upload="uploadCheck"
|
||||
>
|
||||
<n-upload-dragger>
|
||||
<div style="margin-bottom: 12px">
|
||||
<n-icon size="48" :depth="3">
|
||||
<ArchiveIcon />
|
||||
</n-icon>
|
||||
</div>
|
||||
<n-text style="font-size: 16px">
|
||||
点击或者拖动文件到该区域来上传
|
||||
</n-text>
|
||||
<n-p depth="3" style="margin: 8px 0 0 0">
|
||||
注意:上传后将覆盖原有的配置以及客户端数据等,请谨慎操作!
|
||||
</n-p>
|
||||
</n-upload-dragger>
|
||||
</n-upload>
|
||||
<n-button type="primary" style="margin-left: 40%" @click="submitUpload">确定</n-button>
|
||||
</n-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from '@/api/setting'
|
||||
import { ArchiveOutline as ArchiveIcon } from "@vicons/ionicons5";
|
||||
import { router } from '@/router'
|
||||
import event from '@/utils/event/event'
|
||||
|
||||
const { $bus } = event();
|
||||
|
||||
const showImportUploader = ref(false)
|
||||
const uploadRef = ref(null)
|
||||
|
||||
// 导入
|
||||
function importConfig() {
|
||||
showImportUploader.value = true
|
||||
}
|
||||
|
||||
// 导出
|
||||
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); // 释放标签
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 上传前检查
|
||||
function uploadCheck(data) {
|
||||
if (data.file.file?.name !== "config.json") {
|
||||
$message.error("导入文件只能是[config.json]");
|
||||
return false;
|
||||
}
|
||||
if (data.file.file?.type !== "application/json") {
|
||||
$message.error("只能上传json类型文件,请重新上传");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 自定义上传
|
||||
function customRequest(file) {
|
||||
api.importConfig({
|
||||
file: file.file.file,
|
||||
}).then(response => {
|
||||
if (response.data.code === 200) {
|
||||
showImportUploader.value = false;
|
||||
switch (router.options.history.location) {
|
||||
case "/client":
|
||||
$bus.emit('refreshClients',true);
|
||||
break;
|
||||
case "/setting":
|
||||
$bus.emit('refreshSetting',true)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$message.error(response.data.message);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// 点击按钮上传
|
||||
function submitUpload() {
|
||||
uploadRef.value?.submit();
|
||||
}
|
||||
</script>
|
@@ -72,8 +72,11 @@
|
||||
import { useUserStore } from '@/store'
|
||||
import { renderIcon } from '@/utils'
|
||||
import api from '@/api/user'
|
||||
import event from '@/utils/event/event'
|
||||
import { router } from '@/router'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { $bus } = event();
|
||||
|
||||
const options = [
|
||||
{
|
||||
@@ -212,6 +215,9 @@ async function updateInfo() {
|
||||
infoFormModel.value = ref(null)
|
||||
showInfoModel.value = false
|
||||
await useUserStore().getUserInfo()
|
||||
if (router.options.history.location === '/user') {
|
||||
$bus.emit('refreshUserInfo',true);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -3,7 +3,12 @@
|
||||
<MenuCollapse />
|
||||
<BreadCrumb ml-15 hidden sm:block />
|
||||
</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 />
|
||||
<UserAvatar />
|
||||
</div>
|
||||
@@ -14,4 +19,7 @@ import BreadCrumb from './components/BreadCrumb.vue'
|
||||
import MenuCollapse from './components/MenuCollapse.vue'
|
||||
import FullScreen from './components/FullScreen.vue'
|
||||
import UserAvatar from './components/UserAvatar.vue'
|
||||
import Export from './components/Export.vue'
|
||||
import { useUserStore } from '@/store'
|
||||
const loginUser = useUserStore()
|
||||
</script>
|
||||
|
@@ -9,10 +9,14 @@ import { setupRouter } from '@/router'
|
||||
import { setupStore } from '@/store'
|
||||
import App from './App.vue'
|
||||
import { setupNaiveDiscreteApi } from './utils'
|
||||
import mitt from 'mitt'
|
||||
|
||||
const EventMitt = mitt();
|
||||
|
||||
async function setupApp() {
|
||||
const app = createApp(App)
|
||||
setupStore(app)
|
||||
app.config.globalProperties.$bus = EventMitt;
|
||||
await setupRouter(app)
|
||||
app.mount('#app')
|
||||
setupNaiveDiscreteApi()
|
||||
|
@@ -37,7 +37,6 @@ export async function addDynamicRoutes() {
|
||||
// 有token的情况
|
||||
const userStore = useUserStore()
|
||||
try {
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
!userStore.id && (await userStore.getUserInfo())
|
||||
|
||||
@@ -49,8 +48,8 @@ export async function addDynamicRoutes() {
|
||||
router.addRoute(NOT_FOUND_ROUTE)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
$message.error('初始化用户信息失败: ' + error)
|
||||
userStore.logout()
|
||||
$message.error('初始化用户信息失败: ' + error)
|
||||
}
|
||||
}
|
||||
|
||||
|
7
web/src/utils/event/event.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { getCurrentInstance } from "vue";
|
||||
|
||||
export default function event() {
|
||||
const instance = getCurrentInstance();
|
||||
const globalProperties = instance?.appContext.config.globalProperties;
|
||||
return { ...globalProperties };
|
||||
}
|
1
web/src/utils/event/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export * from './event'
|
@@ -2,3 +2,4 @@ export * from './common'
|
||||
export * from './storage'
|
||||
export * from './http'
|
||||
export * from './auth'
|
||||
export * from './event'
|
@@ -85,14 +85,14 @@
|
||||
{{ cip }}
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="可访问IP:">
|
||||
<n-button v-if="row.allowedIps.length <= 0" dashed size="small">
|
||||
-
|
||||
</n-button>
|
||||
<n-button v-else dashed mr-2 type="warning" v-for="aip in row.allowedIps" size="small">
|
||||
{{ aip }}
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<!-- <n-form-item label="可访问IP:">-->
|
||||
<!-- <n-button v-if="row.allowedIps.length <= 0" dashed size="small">-->
|
||||
<!-- - -->
|
||||
<!-- </n-button>-->
|
||||
<!-- <n-button v-else dashed mr-2 type="warning" v-for="aip in row.allowedIps" size="small">-->
|
||||
<!-- {{ aip }}-->
|
||||
<!-- </n-button>-->
|
||||
<!-- </n-form-item>-->
|
||||
<n-form-item label="创建人:">
|
||||
<n-button color="#54150F" dashed size="small">
|
||||
{{ row.createUser }}
|
||||
@@ -106,7 +106,7 @@
|
||||
禁用
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item label="离线监听:">
|
||||
<n-form-item label="离线通知:">
|
||||
<n-button v-if="row.offlineMonitoring === 1" color="#067748" round :bordered="false" size="small">
|
||||
启用
|
||||
</n-button>
|
||||
@@ -114,12 +114,12 @@
|
||||
禁用
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
<n-form-item class="timeItem" label="时间:">
|
||||
<n-space vertical>
|
||||
<span> 创建时间: {{ row.createdAt }}</span>
|
||||
<span> 更新时间: {{ row.updatedAt }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
<!-- <n-form-item class="timeItem" label="时间:">-->
|
||||
<!-- <n-space vertical>-->
|
||||
<!-- <span> 创建时间: {{ row.createdAt }}</span>-->
|
||||
<!-- <span> 更新时间: {{ row.updatedAt }}</span>-->
|
||||
<!-- </n-space>-->
|
||||
<!-- </n-form-item>-->
|
||||
</n-form>
|
||||
</div>
|
||||
</n-card>
|
||||
@@ -228,12 +228,18 @@
|
||||
<n-radio :value="0" :checked="editModalForm.enabled === 0" @change="editModalForm.enabled = 0">禁用</n-radio>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
<n-form-item label="离线监听" path="offlineMonitoring">
|
||||
<n-form-item label="离线通知" path="offlineMonitoring">
|
||||
<n-radio-group :value="editModalForm.offlineMonitoring">
|
||||
<n-radio :value="1" :checked="editModalForm.offlineMonitoring === 1" @change="editModalForm.offlineMonitoring = 1">启用</n-radio>
|
||||
<n-radio :value="0" :checked="editModalForm.offlineMonitoring === 0" @change="editModalForm.offlineMonitoring = 0">禁用</n-radio>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
<n-form-item class="timeItem" label="时间:">
|
||||
<n-space vertical>
|
||||
<span> 创建时间: {{ editModalForm.createdAt }}</span>
|
||||
<span> 更新时间: {{ editModalForm.updatedAt }}</span>
|
||||
</n-space>
|
||||
</n-form-item>
|
||||
<n-button type="info" style="margin-left: 40%" @click="updateClient()">确认</n-button>
|
||||
</n-form>
|
||||
</n-modal>
|
||||
@@ -319,7 +325,7 @@
|
||||
<n-radio :value="0" :checked="addModalForm.enabled === 0" @change="addModalForm.enabled = 0">禁用</n-radio>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
<n-form-item label="离线监听" path="offlineMonitoring">
|
||||
<n-form-item label="离线通知" path="offlineMonitoring">
|
||||
<n-radio-group :value="addModalForm.offlineMonitoring">
|
||||
<n-radio :value="1" :checked="addModalForm.offlineMonitoring === 1" @change="addModalForm.offlineMonitoring = 1">启用</n-radio>
|
||||
<n-radio :value="0" :checked="addModalForm.offlineMonitoring === 0" @change="addModalForm.offlineMonitoring = 0">禁用</n-radio>
|
||||
@@ -335,6 +341,9 @@ import { NButton } from 'naive-ui'
|
||||
import { debounce, ellipsis } from '@/utils'
|
||||
import clientApi from '@/views/client/api'
|
||||
import QueryBar from '@/components/query-bar/QueryBar.vue'
|
||||
import event from '@/utils/event/event'
|
||||
|
||||
const { $bus } = event();
|
||||
|
||||
const selOptions = [
|
||||
{
|
||||
@@ -437,7 +446,9 @@ const editModalForm = ref({
|
||||
presharedKey: ''
|
||||
},
|
||||
enabled: 1,
|
||||
offlineMonitoring: 1
|
||||
offlineMonitoring: 1,
|
||||
createdAt: '',
|
||||
updatedAt: ''
|
||||
})
|
||||
|
||||
// 添加模态框的数据集
|
||||
@@ -573,6 +584,8 @@ function openEditModal(row) {
|
||||
editModalForm.value.keys.presharedKey = row.keys.presharedKey
|
||||
editModalForm.value.enabled = row.enabled
|
||||
editModalForm.value.offlineMonitoring = row.offlineMonitoring
|
||||
editModalForm.value.createdAt = row.createdAt
|
||||
editModalForm.value.updatedAt = row.updatedAt
|
||||
}
|
||||
|
||||
// 更改客户端信息
|
||||
@@ -658,6 +671,13 @@ function search() {
|
||||
getClientList()
|
||||
}
|
||||
|
||||
// 监听事件
|
||||
$bus.on("refreshClients",value => {
|
||||
if (value) {
|
||||
getClientList();
|
||||
}
|
||||
})
|
||||
|
||||
getClientList()
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
@@ -6,6 +6,7 @@ export default {
|
||||
component: Layout,
|
||||
redirect: '/client',
|
||||
meta: {
|
||||
title: '客户端',
|
||||
order: 2,
|
||||
},
|
||||
children: [
|
||||
|
@@ -174,6 +174,9 @@
|
||||
<n-radio :value="false" :checked="editFormModel.data[index] === false" @change="editFormModel.data[index] = false">否</n-radio>
|
||||
</n-radio-group>
|
||||
</n-form-item>
|
||||
<n-form-item label="配置描述">
|
||||
<n-input v-model:value="editFormModel.describe" />
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="info" @click="updateSetting">确认</n-button>
|
||||
</n-form-item>
|
||||
@@ -209,6 +212,10 @@ import AppPage from '@/components/page/AppPage.vue'
|
||||
import api from '@/views/setting/api'
|
||||
import { NButton } from 'naive-ui'
|
||||
import { renderIcon } from '@/utils'
|
||||
import event from '@/utils/event/event'
|
||||
|
||||
const { $bus } = event();
|
||||
const tabCode = ref("")
|
||||
|
||||
// 表头
|
||||
const tableColumns = [
|
||||
@@ -401,12 +408,15 @@ function tabChange(code) {
|
||||
switch (code) {
|
||||
case "Server":
|
||||
getServerConfig()
|
||||
tabCode.value = "Server"
|
||||
break;
|
||||
case "Global":
|
||||
getGlobalConfig()
|
||||
tabCode.value = "Global"
|
||||
break;
|
||||
case "Other":
|
||||
allSetting()
|
||||
tabCode.value = "Other"
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -480,6 +490,22 @@ async function addSetting() {
|
||||
}
|
||||
}
|
||||
|
||||
$bus.on("refreshSetting",value => {
|
||||
if (value) {
|
||||
if (tabCode.value === "" || tabCode.value === undefined) {
|
||||
getServerConfig()
|
||||
} else {
|
||||
switch (tabCode.value) {
|
||||
case "Server":
|
||||
getServerConfig()
|
||||
break;
|
||||
case "Global":
|
||||
getGlobalConfig()
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
getServerConfig()
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
@@ -6,6 +6,7 @@ export default {
|
||||
component: Layout,
|
||||
redirect: '/setting',
|
||||
meta: {
|
||||
title: '设置',
|
||||
order: 3,
|
||||
},
|
||||
children: [
|
||||
|
@@ -106,6 +106,9 @@ import userApi from '@/api/user'
|
||||
import { NAvatar,NTag,NButton } from 'naive-ui'
|
||||
import { renderIcon } from '@/utils'
|
||||
import { useUserStore } from '@/store'
|
||||
import event from '@/utils/event/event'
|
||||
|
||||
const { $bus } = event();
|
||||
|
||||
const infoFormRef = ref()
|
||||
|
||||
@@ -400,6 +403,12 @@ function addUser() {
|
||||
showInfoModel.value = true
|
||||
}
|
||||
|
||||
$bus.on('refreshUserInfo',value => {
|
||||
if (value) {
|
||||
getUserList();
|
||||
}
|
||||
})
|
||||
|
||||
getUserList()
|
||||
</script>
|
||||
<style></style>
|
@@ -6,6 +6,7 @@ export default {
|
||||
component: Layout,
|
||||
redirect: '/user',
|
||||
meta: {
|
||||
title: '管理员',
|
||||
order: 1,
|
||||
},
|
||||
children: [
|
||||
|
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-40 text-14 opacity-60" style="cursor: pointer" @click="dailyPoe">{{ dailyPoetry.content || '莫向外求,但从心觅,行有不得,反求诸己。' }}</p>
|
||||
<p class="mt-40 text-14 opacity-60">{{ dailyPoetry.content || '莫向外求,但从心觅,行有不得,反求诸己。' }}</p>
|
||||
<p class="mt-32 text-right text-12 opacity-40">—— {{ dailyPoetry.author || '佚名' }}</p>
|
||||
</n-card>
|
||||
<n-card class="ml-12 w-70%">
|
||||
@@ -27,6 +27,18 @@
|
||||
remote
|
||||
:columns="connectionsColumns"
|
||||
:data="connectionsData.data"
|
||||
:row-props="rowProps"
|
||||
/>
|
||||
<n-dropdown
|
||||
placement="bottom-start"
|
||||
trigger="manual"
|
||||
size="small"
|
||||
:x="xRef"
|
||||
:y="yRef"
|
||||
:options="rightMenuOpts"
|
||||
@select="rowSelect"
|
||||
@clickoutside="rowClick"
|
||||
:show="showDropdownRef"
|
||||
/>
|
||||
</n-card>
|
||||
</AppPage>
|
||||
@@ -35,7 +47,7 @@
|
||||
<script setup>
|
||||
import { useUserStore } from '@/store'
|
||||
import api from '@/views/workbench/api'
|
||||
import { debounce } from '@/utils'
|
||||
import { debounce, renderIcon } from '@/utils'
|
||||
import { NTag } from 'naive-ui'
|
||||
const userStore = useUserStore()
|
||||
|
||||
@@ -152,6 +164,49 @@ const connectionsColumns = [
|
||||
}
|
||||
]
|
||||
|
||||
// 链接列表邮件刷新菜单
|
||||
const rightMenuOpts = [
|
||||
{
|
||||
label: () => h('span',{ style: { color: 'green' }}, '刷新'),
|
||||
key: "refresh",
|
||||
icon: renderIcon('tabler:refresh',{ size: 14 })
|
||||
}
|
||||
]
|
||||
|
||||
// 右键菜单的设置
|
||||
const showDropdownRef = ref(false);
|
||||
const xRef = ref(0);
|
||||
const yRef = ref(0);
|
||||
|
||||
// 右键菜单的基本位置逻辑
|
||||
function rowProps(row) {
|
||||
return {
|
||||
onContextmenu: (e) => {
|
||||
// $message.info(JSON.stringify(row, null, 2));
|
||||
e.preventDefault();
|
||||
showDropdownRef.value = false;
|
||||
nextTick().then(() => {
|
||||
showDropdownRef.value = true;
|
||||
xRef.value = e.clientX;
|
||||
yRef.value = e.clientY;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 右键菜单的逻辑
|
||||
function rowSelect(row) {
|
||||
switch (row) {
|
||||
case "refresh":
|
||||
getClientConnections()
|
||||
showDropdownRef.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function rowClick() {
|
||||
showDropdownRef.value = false
|
||||
}
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref({
|
||||
data: []
|
||||
@@ -233,7 +288,7 @@ async function getClientConnections() {
|
||||
|
||||
const initFunc = debounce(() => {
|
||||
getClientConnections()
|
||||
dailyPoe()
|
||||
// dailyPoe()
|
||||
// connectionList()
|
||||
},500)
|
||||
|
||||
|
@@ -6,6 +6,7 @@ export default {
|
||||
component: Layout,
|
||||
redirect: '/workbench',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
order: 0,
|
||||
},
|
||||
children: [
|
||||
|