Compare commits

..

No commits in common. "v2" and "v2.0.8" have entirely different histories.
v2 ... v2.0.8

68 changed files with 186 additions and 7507 deletions

56
.gitignore vendored
View File

@ -233,33 +233,33 @@ fabric.properties
# vendor/ # vendor/
# Go workspace file # Go workspace file
./go.work go.work
./.idea .idea
./web/.idea web/.idea
./web/node_modules web/node_modules
./web/.DS_Store web/.DS_Store
./web/dist web/dist
./web/dist-ssr web/dist-ssr
./web/*.local web/*.local
./web/.eslintcache web/.eslintcache
./web/report.html web/report.html
./web/vite.config.*.timestamp* web/vite.config.*.timestamp*
./web/yarn.lock web/yarn.lock
./web/npm-debug.log* web/npm-debug.log*
./web/.pnpm-error.log* web/.pnpm-error.log*
./web/.pnpm-debug.log web/.pnpm-debug.log
./web/tests/**/coverage/ web/tests/**/coverage/
./web/.vscode/ web/.vscode/
# Editor directories and files # Editor directories and files
./web/*.suo web/*.suo
./web/*.ntvs* web/*.ntvs*
./web/*.njsproj web/*.njsproj
./web/*.sln web/*.sln
./web/tsconfig.tsbuildinfo web/tsconfig.tsbuildinfo
dist/assets dist/assets
dist/resource dist/resource
@ -267,10 +267,10 @@ dist/favicon.png
dist/favicon.svg dist/favicon.svg
dist/index.html dist/index.html
./template/tmp/* template/tmp/*
./logs/* logs/*
./app.yaml app.yaml
./*.db *.db
./.env .env
./*.env *.env

View File

@ -1,9 +1,9 @@
# 打包前端 # 打包前端
FROM node:18-alpine AS build-front FROM node:18-alpine as build-front
WORKDIR /front WORKDIR front
COPY . . COPY . .
WORKDIR ./web WORKDIR web
RUN corepack enable RUN corepack enable
RUN corepack prepare pnpm@8.6.10 --activate RUN corepack prepare pnpm@8.6.10 --activate
@ -13,7 +13,7 @@ RUN pnpm build
RUN ls -lh && pwd RUN ls -lh && pwd
# 前后端集成打包 # 前后端集成打包
FROM golang:alpine AS build-backend FROM golang:alpine as build-backend
RUN apk add upx RUN apk add upx
WORKDIR /build WORKDIR /build
@ -43,4 +43,4 @@ COPY --from=build-backend /build/template/* /app/template/
RUN chmod +x wgui RUN chmod +x wgui
ENTRYPOINT ["./wgui","http:serve"] ENTRYPOINT ["./wgui"]

View File

@ -1,94 +0,0 @@
# 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"
```
## 页面展示
![img.png](document/img.png)
![img_1.png](document/img_1.png)
![img_7.png](document/img_7.png)
![img_8.png](document/img_8.png)
![img_2.png](document/img_2.png)
![img_3.png](document/img_3.png)
![img_4.png](document/img_4.png)
![img_5.png](document/img_5.png)
![img_6.png](document/img_6.png)

View File

@ -1,10 +0,0 @@
package cli
import (
"wireguard-ui/cli/tui"
)
func Kernel() error {
tui.NewApp().Run()
return nil
}

View File

@ -1,165 +0,0 @@
package tui
import (
"context"
"errors"
"fmt"
"github.com/google/uuid"
"time"
"wireguard-ui/component"
"wireguard-ui/global/client"
"wireguard-ui/global/constant"
"wireguard-ui/http/vo"
"wireguard-ui/service"
"wireguard-ui/utils"
)
type App struct {
TokenSecret string // token密钥
User *vo.User // 登陆用户
Client *ClientComponent // 客户端组件
Server *ServerComponent // 服务端组件
Setting *SettingComponent // 设置组件
}
func NewApp() *App {
app := &App{}
if _, err := app.Login(); err != nil {
fmt.Println("登陆失败: ", err)
return nil
}
err := app.AuthLogin()
if err != nil {
fmt.Println("登陆失败: ", err)
return nil
}
fmt.Println("\n=============== 登陆成功 ==============================================================================")
app.Client = NewClientComponent(app.User)
app.Server = NewServerComponent(app.User)
app.Setting = NewSettingComponent(app.User)
fmt.Println("=============== 欢迎使用wireguard-tui =================================================================")
fmt.Println("=============== 当前用户: ", app.User.Nickname, " ================================================================")
fmt.Println("=============== 当前时间: ", time.Now().Format("2006-01-02 15:04:05"), " =======================================================")
fmt.Println("=============== 注意事项如下: =========================================================================")
fmt.Println("=============== 1. 请确保服务端已经安装wireguard ======================================================")
fmt.Println("=============== 2. 请确保服务端和客户端配置文件路径正确 ===============================================")
fmt.Println("=============== 3. 请确保服务端和客户端配置文件权限正确 ===============================================")
fmt.Println("=============== 4. 请确保服务端和客户端配置文件内容正确 ===============================================")
fmt.Println("=============== 5. 请勿泄露配置文件内容 ===============================================================")
fmt.Println("=============== 6. 每次修改客户端、服务端配置或者全局配置过后,请使用重启功能重启服务端,以保证生效 ===")
fmt.Println("=============== 7. 当使用重启无效时,请手动执行对应命令 ===============================================")
fmt.Println("=============== 8. 请勿随意删除客户端,删除后无法恢复 =================================================")
fmt.Println("=============== 9. 手动命令 ===========================================================================")
fmt.Println("=============== 10. 启动wireguard服务端: wg-quick up wg0 ==============================================")
fmt.Println("=============== 11. 停止wireguard服务端: wg-quick down wg0 ============================================")
return app
}
func (a *App) Run() {
if a == nil {
return
}
for {
PrintMenu()
chooseMenu := readInput("请选择菜单: ")
switch chooseMenu {
case "1":
a.Client.ConnectList()
case "2":
a.Client.Menus()
case "3":
a.Server.Menus()
case "4":
a.Setting.Menus()
case "q":
return
}
}
}
// AuthLogin
// @description: 登陆认证
// @receiver a
// @return string
func (a *App) AuthLogin() error {
// 先判断token是否存在
tokenStr, err := client.Redis.Get(context.Background(), fmt.Sprintf("%s:%s", constant.TUIUserToken, a.User.Id)).Result()
if err != nil {
// 不存在,去登陆
tokenStr, err = a.Login()
if err != nil {
return err
}
}
// 存在,不必要再次登陆,解析token
claims, err := component.JWT().ParseToken("Bearer "+tokenStr, a.TokenSecret, "tui")
if err != nil {
return err
}
user, err := service.User().GetUserById(claims.ID)
if err != nil {
return err
}
if user.Status != constant.Enabled {
return errors.New("用户状态异常,请联系管理员处理")
}
a.User = &vo.User{
Id: user.Id,
Account: user.Account,
Nickname: user.Nickname,
Avatar: user.Avatar,
Contact: user.Contact,
IsAdmin: user.IsAdmin,
Status: user.Status,
}
return nil
}
// Login
// @description: 登陆
// @receiver a
// @return string
func (a *App) Login() (tokenStr string, err error) {
fmt.Println("============== 登陆 ==============")
username := readInput("请输入用户名: ")
password := readInput("请输入密码: ")
// 验证码正确,查询用户信息
user, err := service.User().GetUserByAccount(username)
if err != nil {
return "", fmt.Errorf("用户不存在: %v", err)
}
// 对比密码
if !utils.Password().ComparePassword(user.Password, password) {
return "", errors.New("密码错误")
}
secret := component.JWT().GenerateSecret(password, uuid.NewString(), time.Now().Local().String())
// 生成token
token, _, err := component.JWT().GenerateToken(user.Id, secret, "tui")
if err != nil {
return "", fmt.Errorf("登陆失败: %v", err)
}
a.User = &vo.User{
Id: user.Id,
Account: user.Account,
Nickname: user.Nickname,
Avatar: user.Avatar,
Contact: user.Contact,
IsAdmin: user.IsAdmin,
Status: user.Status,
}
a.TokenSecret = secret
return "Bearer " + token, nil
}

View File

@ -1,520 +0,0 @@
package tui
import (
"encoding/json"
"fmt"
"gitee.ltd/lxh/logger/log"
"github.com/charmbracelet/bubbles/table"
"github.com/spf13/cast"
"os"
"strconv"
"strings"
"time"
"wireguard-ui/component"
"wireguard-ui/global/constant"
"wireguard-ui/http/param"
"wireguard-ui/http/vo"
"wireguard-ui/service"
"wireguard-ui/utils"
)
type ClientComponent struct {
LoginUser *vo.User
Menu []string
Clients [][]string
}
func NewClientComponent(loginUser *vo.User) *ClientComponent {
ccp := &ClientComponent{
LoginUser: loginUser,
Menu: []string{"[1] 客户端列表", "[2] 查看客户端配置", "[3] 添加客户端", "[4] 编辑客户端", "[5] 删除客户端", "[q] 返回上一级菜单"},
}
return ccp
}
// Menus
// @description: 客户端菜单
// @receiver c
func (c *ClientComponent) Menus() {
fmt.Println("")
for _, r := range c.Menu {
fmt.Println(" -> " + r)
}
chooseMenu := readInput("\n请选择: ")
switch chooseMenu {
case "1":
c.List(true)
case "2":
c.ShowConfig()
case "3":
c.Add()
case "4":
c.Edit()
case "5":
c.Delete()
case "q":
return
}
}
// ConnectList
// @description: 客户端链接列表
// @receiver c
// @return string
func (c *ClientComponent) ConnectList() {
fmt.Println("\n客户端链接列表")
connectList, err := component.Wireguard().GetClients()
if err != nil {
fmt.Println("获取客户端链接列表失败: ", err.Error())
return
}
var data [][]string
for _, peer := range connectList {
// 获取客户端链接信息
clientInfo, err := service.Client().GetByPublicKey(peer.PublicKey.String())
if err != nil {
continue
}
var ipAllocation string
for _, iaip := range peer.AllowedIPs {
ipAllocation += iaip.String() + ","
}
// 去除一下最右边的逗号
if len(ipAllocation) > 0 {
ipAllocation = strings.TrimRight(ipAllocation, ",")
}
var isOnline = "否"
if time.Since(peer.LastHandshakeTime).Minutes() < 3 {
isOnline = "是"
}
data = append(data, []string{clientInfo.Name,
clientInfo.Email,
ipAllocation,
isOnline,
utils.FlowCalculation().Parse(peer.TransmitBytes),
utils.FlowCalculation().Parse(peer.ReceiveBytes),
peer.Endpoint.String(),
peer.LastHandshakeTime.Format("2006-01-02 15:04:05")})
}
//if len(data) <= 0 {
// //data = append(data, []string{"暂无数据"})
// // data = append(data, []string{"名称1", "12345678910@qq.com", "192.168.100.1", "是", "10G", "20G", "1.14.30.133:51280", "2024-12-20 15:07:36"}, []string{"名称2", "12345678910@qq.com", "192.168.100.2", "否", "20G", "40G", "1.14.30.133:51280", "2024-12-22 15:07:36"})
//}
title := []table.Column{
{
Title: "客户端名称",
Width: 20,
}, {
Title: "联系邮箱",
Width: 20,
}, {
Title: "分配的IP",
Width: 30,
}, {
Title: "是否在线",
Width: 10,
}, {
Title: "接收流量",
Width: 10,
}, {
Title: "传输流量",
Width: 10,
}, {
Title: "链接端点",
Width: 30,
}, {
Title: "最后握手时间",
Width: 30,
}}
Show(GenerateTable(title, data))
return
}
// List
// @description: 客户端列表
// @receiver c
func (c *ClientComponent) List(showMenu bool) {
title := []table.Column{
{
Title: "序号",
Width: 5,
},
{
Title: "ID",
Width: 35,
},
{
Title: "名称",
Width: 25,
},
{
Title: "联系邮箱",
Width: 30,
},
{
Title: "客户端IP",
Width: 20,
},
{
Title: "状态",
Width: 10,
},
{
Title: "离线通知",
Width: 10,
},
}
records, _, err := service.Client().List(param.ClientList{
Page: param.Page{
Current: -1,
},
})
if err != nil {
fmt.Println("获取客户端列表失败: ", err.Error())
return
}
fmt.Println("\n客户端列表")
var data [][]string
for i, client := range records {
var status, offlineNotify string
if client.Enabled == 1 {
status = "启用"
} else {
status = "禁用"
}
if client.OfflineMonitoring == 1 {
offlineNotify = "开启"
} else {
offlineNotify = "关闭"
}
data = append(data, []string{
cast.ToString(i + 1),
client.Id,
client.Name,
client.Email,
client.IpAllocationStr,
status,
offlineNotify,
})
}
Show(GenerateTable(title, data))
c.Clients = data
if showMenu {
c.Menus()
}
return
}
// ShowConfig
// @description: 显示配置
// @receiver c
func (c *ClientComponent) ShowConfig() {
c.List(false)
clientIdx := readInput("请输入客户端序号: ")
downloadType := readInput("请输入下载类型(FILE - 文件 | EMAIL - 邮件): ")
idx, err := strconv.Atoi(clientIdx)
if err != nil {
fmt.Println("输入有误: ", err.Error())
return
}
if idx < 1 || idx > len(c.Clients) {
fmt.Println("输入有误")
return
}
client := c.Clients[idx-1]
// 取到id
clientID := client[1]
// 查询客户端信息
clientInfo, err := service.Client().GetByID(clientID)
if err != nil {
fmt.Println("获取客户端信息失败: ", err.Error())
return
}
// 渲染配置
var keys vo.Keys
_ = json.Unmarshal([]byte(clientInfo.Keys), &keys)
globalSet, err := service.Setting().GetWGSetForConfig()
if err != nil {
fmt.Println("获取全局配置失败: ", err.Error())
return
}
serverConf, err := service.Setting().GetWGServerForConfig()
if err != nil {
fmt.Println("获取服务器配置失败: ", err.Error())
return
}
outPath, err := component.Wireguard().GenerateClientFile(clientInfo, serverConf, globalSet)
if err != nil {
fmt.Println("生成客户端配置失败: ", err.Error())
return
}
// 根据不同下载类型执行不同逻辑
switch downloadType {
case "FILE": // 二维码
// 读取文件内容
fileContent, err := os.ReadFile(outPath)
if err != nil {
fmt.Println("读取文件失败: ", err.Error())
return
}
fmt.Println("\n#请将以下内容复制到客户端配置文件中【不包含本行提示语】")
fmt.Println("\n" + string(fileContent))
if err = os.Remove(outPath); err != nil {
log.Errorf("删除临时文件失败: %s", err.Error())
}
case "EMAIL": // 邮件
if clientInfo.Email == "" {
fmt.Println("当前客户端并未配置通知邮箱")
return
}
// 获取邮箱配置
emailConf, err := service.Setting().GetByCode("EMAIL_SMTP")
if err != nil {
fmt.Println("获取邮箱配置失败请先到设置页面的【其他】里面添加code为【EMAIL_SMTP】的具体配置")
return
}
err = utils.Mail(emailConf).SendMail(clientInfo.Email, fmt.Sprintf("客户端: %s", clientInfo.Name), "请查收附件", outPath)
if err != nil {
fmt.Println("发送邮件失败")
return
}
if err = os.Remove(outPath); err != nil {
log.Errorf("删除临时文件失败: %s", err.Error())
}
fmt.Println("发送邮件成功,请注意查收!")
}
c.Menus()
}
// Add
// @description: 添加客户端
// @receiver c
func (c *ClientComponent) Add() {
fmt.Println("\n添加客户端")
clientIP, serverIP, err := GenerateIP()
if err != nil {
fmt.Println("生成客户端IP失败: ", err.Error())
return
}
keys, err := GenerateKeys()
if err != nil {
fmt.Println("生成密钥对失败: ", err.Error())
return
}
var p param.SaveClient
p.Name = readInput("请输入客户端名称: ")
p.Email = readInput("请输入联系邮箱: ")
clientIPIn := readInput("请输入客户端IP(默认自动生成,多个采用 ',' 分割,例如 10.10.0.1/32,10.10.0.2/32): ")
if clientIPIn == "" {
p.IpAllocation = clientIP
} else {
p.IpAllocation = strings.Split(clientIPIn, ",")
}
p.AllowedIps = serverIP
p.Keys = &param.Keys{
PrivateKey: keys.PrivateKey,
PublicKey: keys.PublicKey,
PresharedKey: keys.PresharedKey,
}
var useServerDNS, enabled, offlineNotify constant.Status
useServerDNSIn := readInput("是否使用服务器DNS(默认不使用 1 - 是 | 0 - 否 ): ")
switch useServerDNSIn {
case "1":
useServerDNS = constant.Enabled
case "0":
useServerDNS = constant.Disabled
default:
useServerDNS = constant.Disabled
}
enabledIn := readInput("是否启用(默认启用 1 - 是 | 0 - 否 ): ")
switch enabledIn {
case "1":
enabled = constant.Enabled
case "0":
enabled = constant.Disabled
default:
enabled = constant.Enabled
}
offlineNotifyIn := readInput("是否开启离线通知(默认关闭 1 - 是 | 0 - 否 ): ")
switch offlineNotifyIn {
case "1":
offlineNotify = constant.Enabled
case "0":
offlineNotify = constant.Disabled
default:
offlineNotify = constant.Disabled
}
p.UseServerDns = &useServerDNS
p.Enabled = &enabled
p.OfflineMonitoring = &offlineNotify
err = service.Client().SaveClient(p, c.LoginUser)
if err != nil {
fmt.Println("添加客户端失败: ", err.Error())
return
}
fmt.Println("添加客户端成功")
c.List(true)
return
}
// Edit
// @description: 编辑客户端
// @receiver c
func (c *ClientComponent) Edit() {
fmt.Println("\n编辑客户端")
c.List(false)
clientIdx := readInput("请输入客户端序号: ")
idx, err := strconv.Atoi(clientIdx)
if err != nil {
fmt.Println("输入有误: ", err.Error())
return
}
if idx < 1 || idx > len(c.Clients) {
fmt.Println("输入有误")
return
}
client := c.Clients[idx-1]
// 取到id
clientID := client[1]
// 查询客户端信息
clientInfo, err := service.Client().GetByID(clientID)
if err != nil {
fmt.Println("获取客户端信息失败: ", err.Error())
return
}
var p param.SaveClient
p.Id = clientID
p.Name = readInput("请输入客户端名称[无需改变请回车跳过,下同]: ")
p.Email = readInput("请输入联系邮箱: ")
clientIPIn := readInput("请输入客户端IP(默认自动生成,多个采用 ',' 分割,例如 10.10.0.1/32,10.10.0.2/32): ")
if clientIPIn == "" {
p.IpAllocation = strings.Split(clientInfo.IpAllocation, ",")
} else {
p.IpAllocation = strings.Split(clientIPIn, ",")
}
p.AllowedIps = strings.Split(clientInfo.AllowedIps, ",")
var keys *param.Keys
_ = json.Unmarshal([]byte(clientInfo.Keys), &keys)
p.Keys = keys
var useServerDNS, enabled, offlineNotify constant.Status
useServerDNSIn := readInput("是否使用服务器DNS(默认不使用 1 - 是 | 0 - 否 ): ")
switch useServerDNSIn {
case "1":
useServerDNS = constant.Enabled
case "0":
useServerDNS = constant.Disabled
default:
useServerDNS = clientInfo.UseServerDns
}
enabledIn := readInput("是否启用(默认启用 1 - 是 | 0 - 否 ): ")
switch enabledIn {
case "1":
enabled = constant.Enabled
case "0":
enabled = constant.Disabled
default:
enabled = clientInfo.Enabled
}
offlineNotifyIn := readInput("是否开启离线通知(默认关闭 1 - 是 | 0 - 否 ): ")
switch offlineNotifyIn {
case "1":
offlineNotify = constant.Enabled
case "0":
offlineNotify = constant.Disabled
default:
offlineNotify = clientInfo.OfflineMonitoring
}
p.UseServerDns = &useServerDNS
p.Enabled = &enabled
p.OfflineMonitoring = &offlineNotify
err = service.Client().SaveClient(p, c.LoginUser)
if err != nil {
fmt.Println("编辑客户端失败: ", err.Error())
return
}
fmt.Println("编辑客户端成功")
c.List(true)
return
}
// Delete
// @description: 删除客户端
// @receiver c
func (c *ClientComponent) Delete() {
fmt.Println("\n删除客户端")
c.List(false)
clientIdx := readInput("请输入客户端序号: ")
idx, err := strconv.Atoi(clientIdx)
if err != nil {
fmt.Println("输入有误: ", err.Error())
return
}
if idx < 1 || idx > len(c.Clients) {
fmt.Println("输入有误")
return
}
client := c.Clients[idx-1]
// 取到id
clientID := client[1]
if err := service.Client().Delete(clientID); err != nil {
fmt.Println("删除客户端失败: ", err.Error())
return
}
c.List(true)
fmt.Println("删除客户端成功")
return
}

View File

@ -1,62 +0,0 @@
package tui
import (
"fmt"
"wireguard-ui/command"
"wireguard-ui/http/vo"
)
type ServerComponent struct {
LoginUser *vo.User
Menu []string
}
func NewServerComponent(loginUser *vo.User) *ServerComponent {
return &ServerComponent{
LoginUser: loginUser,
Menu: []string{"[1] 启动服务", "[2] 关闭服务", "[3] 重启服务", "[q] 返回上一级菜单"},
}
}
// Menus
// @description: 服务端菜单
// @receiver s
func (s *ServerComponent) Menus() {
fmt.Println("")
for _, r := range s.Menu {
fmt.Println(" -> " + r)
}
chooseMenu := readInput("\n请选择: ")
switch chooseMenu {
case "1":
s.Start()
case "2":
s.Stop()
case "3":
s.Restart()
case "q":
return
}
}
// Start
// @description: 启动服务
// @receiver s
func (s *ServerComponent) Start() {
command.StartWireguard("")
}
// Stop
// @description: 停止服务
// @receiver s
func (s *ServerComponent) Stop() {
command.StopWireguard("")
}
// Restart
// @description: 重启服务
// @receiver s
func (s *ServerComponent) Restart() {
command.RestartWireguard(false, "")
}

View File

@ -1,440 +0,0 @@
package tui
import (
"encoding/json"
"fmt"
"github.com/charmbracelet/bubbles/table"
"github.com/eiannone/keyboard"
"github.com/spf13/cast"
"strings"
"wireguard-ui/http/vo"
"wireguard-ui/model"
"wireguard-ui/service"
"wireguard-ui/utils"
)
type SettingComponent struct {
LoginUser *vo.User
Menu []string
Other *OtherSettingComponent
}
func NewSettingComponent(loginUser *vo.User) *SettingComponent {
return &SettingComponent{
LoginUser: loginUser,
Menu: []string{"[1] 服务端配置", "[2] 全局设置", "[3] 其他配置", "[q] 返回上一级菜单"},
Other: NewOtherSettingComponent(),
}
}
// Menus
// @description: 设置菜单
// @receiver s
func (s *SettingComponent) Menus() {
fmt.Println("")
for _, r := range s.Menu {
fmt.Println(" -> " + r)
}
chooseMenu := readInput("\n请选择: ")
switch chooseMenu {
case "1":
s.ServerSetting()
case "2":
s.GlobalSetting()
case "3":
s.Other.Menus(s)
case "q":
return
}
}
// ServerSetting
// @description: 服务端配置
// @receiver s
func (s *SettingComponent) ServerSetting() {
fmt.Println("\n服务端配置")
// 先读取一下服务端配置
servConf, err := service.Setting().GetByCode("WG_SERVER")
if err != nil {
fmt.Println("获取服务端配置失败: ", err)
return
}
type serverConf 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"`
}
// 解析出来好渲染
var conf serverConf
if err = json.Unmarshal([]byte(servConf.Data), &conf); err != nil {
fmt.Println("解析服务端配置失败: ", err)
return
}
ipScopeIn := readInput(fmt.Sprintf("请输入IP段多个采用 ',[英文逗号]' 分割,不填写默认当前值,下同,当前值[%s] ", strings.Replace(strings.Join(conf.IpScope, ","), " ", "", -1)))
listenPortIn := readInput(fmt.Sprintf("请输入监听端口,当前值[%d] ", conf.ListenPort))
privateKeyIn := readInput(fmt.Sprintf("请输入私钥,当前值[%s] ", conf.PrivateKey))
publicKeyIn := readInput(fmt.Sprintf("请输入公钥,当前值[%s] ", conf.PublicKey))
postUpScriptIn := readInput(fmt.Sprintf("请输入PostUp脚本当前值[%s] ", conf.PostUpScript))
postDownScriptIn := readInput(fmt.Sprintf("请输入PostDown脚本当前值[%s] ", conf.PostDownScript))
if ipScopeIn != "" {
conf.IpScope = strings.Split(ipScopeIn, ",")
}
if listenPortIn != "" {
conf.ListenPort = cast.ToInt(listenPortIn)
}
if privateKeyIn != "" {
conf.PrivateKey = privateKeyIn
}
if publicKeyIn != "" {
conf.PublicKey = publicKeyIn
}
if postUpScriptIn != "" {
conf.PostUpScript = postUpScriptIn
}
if postDownScriptIn != "" {
conf.PostDownScript = postDownScriptIn
}
data, _ := json.Marshal(conf)
if err := service.Setting().SetData(&model.Setting{
Code: "WG_SERVER",
Data: string(data),
}); err != nil {
fmt.Println("保存服务端配置失败: ", err)
return
}
fmt.Println("修改服务端配置成功")
return
}
// GlobalSetting
// @description: 全局设置
// @receiver s
func (s *SettingComponent) GlobalSetting() {
fmt.Println("\n服务端配置")
// 先读取一下服务端配置
globalConf, err := service.Setting().GetByCode("WG_SETTING")
if err != nil {
fmt.Println("获取服务端配置失败: ", err)
return
}
type gConf 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"`
}
// 解析出来好渲染
var conf gConf
if err = json.Unmarshal([]byte(globalConf.Data), &conf); err != nil {
fmt.Println("解析全局配置失败: ", err)
return
}
mtu := readInput(fmt.Sprintf("请输入mtu不填写默认当前值下同当前值[%d] ", conf.MTU))
configFilePath := readInput(fmt.Sprintf("请输入配置文件地址,当前值[%s] ", conf.ConfigFilePath))
dnsServer := readInput(fmt.Sprintf("请输入dns多个采用 ',[英文逗号]' 分割,当前值[%s] ", strings.Replace(strings.Join(conf.DnsServer, ","), " ", "", -1)))
endpointAddress := readInput(fmt.Sprintf("请输入公网IP默认系统自动获取当前值[%s] ", conf.EndpointAddress))
firewallMark := readInput(fmt.Sprintf("请输入FirewallMark当前值[%s] ", conf.FirewallMark))
persistentKeepalive := readInput(fmt.Sprintf("请输入PersistentKeepalive当前值[%d] ", conf.PersistentKeepalive))
tableRule := readInput(fmt.Sprintf("请输入Table当前值[%s] ", conf.Table))
if mtu != "" {
conf.MTU = cast.ToInt(mtu)
}
if configFilePath != "" {
conf.ConfigFilePath = configFilePath
}
if dnsServer != "" {
conf.DnsServer = strings.Split(dnsServer, ",")
}
if endpointAddress != "" {
conf.EndpointAddress = endpointAddress
} else {
conf.EndpointAddress = utils.Network().GetHostPublicIP()
}
if firewallMark != "" {
conf.FirewallMark = firewallMark
}
if persistentKeepalive != "" {
conf.PersistentKeepalive = cast.ToInt(persistentKeepalive)
}
if tableRule != "" {
conf.Table = tableRule
}
data, _ := json.Marshal(conf)
if err := service.Setting().SetData(&model.Setting{
Code: "WG_SETTING",
Data: string(data),
}); err != nil {
fmt.Println("保存全局配置失败: ", err)
return
}
fmt.Println("修改全局配置成功")
return
}
// OtherSettingComponent
// @description: 其他配置杂项
type OtherSettingComponent struct {
Setting *SettingComponent
Menu []string
}
func NewOtherSettingComponent() *OtherSettingComponent {
return &OtherSettingComponent{
Menu: []string{"[1] 列表", "[2] 添加", "[3] 编辑", "[4] 删除", "[q] 返回上一级菜单"},
}
}
func (s *OtherSettingComponent) Menus(setting *SettingComponent) {
if s.Setting == nil {
s.Setting = setting
}
fmt.Println("")
for _, r := range s.Menu {
fmt.Println(" -> " + r)
}
chooseMenu := readInput("\n请选择: ")
switch chooseMenu {
case "1":
s.List(true)
case "2":
s.Add()
case "3":
s.Edit()
case "4":
s.Delete()
case "q":
s.Setting.Menus()
}
}
// List
// @description: 其他配置
// @receiver s
// @param showMenu
func (s *OtherSettingComponent) List(showMenu bool) {
fmt.Println("\n其他配置列表")
// 不查询的配置
var blackList = []string{"WG_SETTING", "WG_SERVER"}
data, err := service.Setting().GetAllSetting(blackList)
if err != nil {
fmt.Println("获取配置失败")
return
}
title := []table.Column{
{
Title: "序号",
Width: 10,
},
{
Title: "编码",
Width: 40,
},
{
Title: "描述",
Width: 50,
},
{
Title: "创建时间",
Width: 30,
},
{
Title: "更新时间",
Width: 30,
},
}
var result [][]string
for i, v := range data {
result = append(result, []string{
cast.ToString(i + 1),
v.Code,
v.Describe,
v.CreatedAt.Format("2006-01-02 15:04:05"),
v.UpdatedAt.Format("2006-01-02 15:04:05"),
})
}
Show(GenerateTable(title, result))
if showMenu {
s.Menus(nil)
}
}
// Add
// @description: 新增其他配置
// @receiver s
func (s *OtherSettingComponent) Add() {
fmt.Println("\n新增其他配置")
code := readInput("请输入配置编码,此编码是唯一编码不可重复:")
desc := readInput("请输入配置描述:")
// 监听键盘事件,只监听 + 和 - 和 enter
if err := keyboard.Open(); err != nil {
return
}
defer func() {
_ = keyboard.Close()
}()
// + <=> 43 | - <=> 45 | enter <=> 0
fmt.Println("请按下 + 或者 - 进行配置项的新增和删除")
fmt.Println("每一项配置如此: key=val ")
fmt.Println("确认输入完成后 enter[按一次代表当前配置项输入完成,两次代表新增完成]")
fmt.Println("首先进入时请输入 + 进行第一个配置项填写")
var breakCycle bool
var keyVal []string
for {
char, _, err := keyboard.GetKey()
if err != nil {
break
}
if breakCycle {
break
}
switch char {
case 0:
// 收到enter事件触发执行后面的
var dm = make(map[string]any)
for _, kv := range keyVal {
kvs := strings.Split(kv, "=")
key := kvs[0]
val := kvs[1]
dm[key] = val
}
dms, err := json.Marshal(dm)
if err != nil {
breakCycle = true
continue
}
if err = service.Setting().SetData(&model.Setting{
Code: code,
Data: string(dms),
Describe: desc,
}); err != nil {
breakCycle = true
fmt.Println("保存配置失败: ", err.Error())
continue
}
fmt.Println("保存配置成功")
s.List(true)
breakCycle = true
case 43:
keyVal = append(keyVal, readInput("请输入配置项:"))
case 45:
keyVal = keyVal[:len(keyVal)-1]
fmt.Println("已删除最后一个配置项,当前配置项为:", keyVal)
}
}
}
// Edit
// @description: 编辑
// @receiver s
func (s *OtherSettingComponent) Edit() {
fmt.Println("\n编辑其他配置")
s.List(false)
code := readInput("请输入需要编辑的配置编码:")
// 通过编码查询配置
setting, err := service.Setting().GetByCode(code)
if err != nil {
fmt.Println("查找["+code+"]配置失败:", err.Error())
return
}
desc := readInput("请输入需要编辑的配置描述:")
if desc != "" {
setting.Describe = desc
}
var kvm = make(map[string]any)
if err = json.Unmarshal([]byte(setting.Data), &kvm); err != nil {
fmt.Println("配置解析失败: ", err.Error())
return
}
for k, v := range kvm {
valIn := readInput(fmt.Sprintf("请输入配置项值,仅能修改值,当前键值对:%s=%v ", k, v))
if valIn != "" {
kvm[k] = valIn
}
}
// 处理以下数据
kvmStr, err := json.Marshal(kvm)
if err != nil {
fmt.Println("序列化数据失败: ", err.Error())
return
}
setting.Data = string(kvmStr)
if err = service.Setting().SetData(setting); err != nil {
fmt.Println("修改配置失败: ", err.Error())
return
}
fmt.Println("修改配置成功")
}
// Delete
// @description: 删除
// @receiver s
func (s *OtherSettingComponent) Delete() {
fmt.Println("\n 删除指定配置")
s.List(false)
code := readInput("请输入要删除的配置项编码:")
// 查询配置是否存在
if err := service.Setting().Model(&model.Setting{}).
Where("code NOT IN (?)", []string{"WG_SETTING", "WG_SERVER"}).
Where("code = ?", code).Delete(&model.Setting{}).Error; err != nil {
fmt.Println("删除[" + code + "]配置失败")
return
}
fmt.Println("删除成功")
s.List(true)
}

View File

@ -1,136 +0,0 @@
package tui
import (
"bufio"
"fmt"
"gitee.ltd/lxh/logger/log"
"github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/lipgloss"
"github.com/spf13/cast"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"os"
"strings"
"wireguard-ui/http/vo"
"wireguard-ui/model"
"wireguard-ui/service"
"wireguard-ui/utils"
)
var BaseStyle = lipgloss.NewStyle().
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240"))
var menus = []string{"[1] 客户端链接列表", "[2] 客户端", "[3] 服务端", "[4] 设置", "[q] 退出"}
// PrintMenu
// @description: 打印一下菜单
func PrintMenu() {
fmt.Println("\n菜单:")
for _, menu := range menus {
fmt.Println(menu)
}
}
// GenerateTable
// @description: 生成数据表
// @param column
// @param rows
// @return string
func GenerateTable(column []table.Column, rows [][]string) string {
var data []table.Row
for _, v := range rows {
data = append(data, v)
}
tl := table.New(
table.WithColumns(column),
table.WithRows(data),
table.WithHeight(len(data)+1),
)
s := table.DefaultStyles()
s.Header = s.Header.
BorderStyle(lipgloss.NormalBorder()).
BorderForeground(lipgloss.Color("240")).
BorderBottom(true).
Bold(false)
s.Selected = lipgloss.NewStyle()
tl.SetStyles(s)
return BaseStyle.Render(tl.View()+"\n") + "\n一共有 【" + fmt.Sprintf("%d", len(data)) + "】 条数据"
}
// GenerateIP
// @description: 生成IP
// @return clientIPS
// @return serverIPS
// @return err
func GenerateIP() (clientIPS, serverIPS []string, err error) {
// 获取一下服务端信息因为IP分配需要根据服务端的IP制定
serverInfo, err := service.Setting().GetWGServerForConfig()
if err != nil {
return nil, nil, err
}
var assignIPS []string
// 只获取最新的一个
var clientInfo *model.Client
if err = service.Client().Order("created_at DESC").Take(&clientInfo).Error; err == nil {
// 遍历每一个ip是否可允许再分配
for _, ip := range strings.Split(clientInfo.IpAllocation, ",") {
if cast.ToInt64(utils.Network().GetIPSuffix(ip)) >= 255 {
log.Errorf("IP[%s]已无法分配新IP", ip)
continue
} else {
assignIPS = append(assignIPS, ip)
}
}
}
ips := utils.Network().GenerateIPByIPS(serverInfo.Address, assignIPS...)
return ips, serverInfo.Address, nil
}
// GenerateKeys
// @description: 生成密钥对
// @return keys
// @return err
func GenerateKeys() (keys *vo.Keys, err error) {
// 为空,新增
privateKey, err := wgtypes.GeneratePrivateKey()
if err != nil {
return nil, err
}
publicKey := privateKey.PublicKey().String()
presharedKey, err := wgtypes.GenerateKey()
if err != nil {
return nil, fmt.Errorf("生成预共享密钥失败: %s", err.Error())
}
keys = &vo.Keys{
PrivateKey: privateKey.String(),
PublicKey: publicKey,
PresharedKey: presharedKey.String(),
}
return keys, nil
}
// Show
// @description: 展示
// @param data
func Show(data any) {
fmt.Println(data)
}
// readInput
// @description: 读取输入
// @param prompt
// @return string
func readInput(prompt string) string {
fmt.Print(prompt)
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
return scanner.Text()
}

View File

@ -36,7 +36,7 @@ func JWT() JwtComponent {
// @return token // @return token
// @return expireTime // @return expireTime
// @return err // @return err
func (JwtComponent) GenerateToken(userId, secret, source string, times ...time.Time) (token string, expireTime *jwt.NumericDate, err error) { func (JwtComponent) GenerateToken(userId, secret string, times ...time.Time) (token string, expireTime *jwt.NumericDate, err error) {
var notBefore, issuedAt *jwt.NumericDate var notBefore, issuedAt *jwt.NumericDate
if len(times) != 0 { if len(times) != 0 {
expireTime = jwt.NewNumericDate(times[0]) expireTime = jwt.NewNumericDate(times[0])
@ -68,19 +68,10 @@ func (JwtComponent) GenerateToken(userId, secret, source string, times ...time.T
return "", nil, errors.New("生成token失败") return "", nil, errors.New("生成token失败")
} }
switch source { client.Redis.Set(context.Background(),
case "http": fmt.Sprintf("%s:%s", constant.UserToken, userId),
client.Redis.Set(context.Background(), token,
fmt.Sprintf("%s:%s", constant.UserToken, userId), time.Duration(expireTime.Sub(time.Now()).Abs().Seconds())*time.Second)
token,
time.Duration(expireTime.Sub(time.Now()).Abs().Seconds())*time.Second)
case "tui":
client.Redis.Set(context.Background(),
fmt.Sprintf("%s:%s", constant.TUIUserToken, userId),
token,
time.Duration(expireTime.Sub(time.Now()).Abs().Seconds())*time.Second)
}
return return
} }
@ -90,7 +81,7 @@ func (JwtComponent) GenerateToken(userId, secret, source string, times ...time.T
// @param token // @param token
// @return *JwtComponent // @return *JwtComponent
// @return error // @return error
func (JwtComponent) ParseToken(token, secret, source string) (*JwtComponent, error) { func (JwtComponent) ParseToken(token, secret string) (*JwtComponent, error) {
tokenStr := strings.Split(token, "Bearer ")[1] tokenStr := strings.Split(token, "Bearer ")[1]
t, err := jwt.ParseWithClaims(tokenStr, &JwtComponent{}, func(token *jwt.Token) (any, error) { t, err := jwt.ParseWithClaims(tokenStr, &JwtComponent{}, func(token *jwt.Token) (any, error) {
@ -98,20 +89,10 @@ func (JwtComponent) ParseToken(token, secret, source string) (*JwtComponent, err
}) })
if claims, ok := t.Claims.(*JwtComponent); ok && t.Valid { if claims, ok := t.Claims.(*JwtComponent); ok && t.Valid {
var userToken string userToken, err := client.Redis.Get(context.Background(), fmt.Sprintf("%s:%s", constant.UserToken, claims.ID)).Result()
switch source { if err != nil {
case "http": log.Errorf("缓存中用户[%s]的token查找失败: %v", claims.ID, err.Error())
userToken, err = client.Redis.Get(context.Background(), fmt.Sprintf("%s:%s", constant.UserToken, claims.ID)).Result() return nil, errors.New("token不存在")
if err != nil {
log.Errorf("缓存中用户[%s]的token查找失败: %v", claims.ID, err.Error())
return nil, errors.New("token不存在")
}
case "tui":
userToken, err = client.Redis.Get(context.Background(), fmt.Sprintf("%s:%s", constant.TUIUserToken, claims.ID)).Result()
if err != nil {
log.Errorf("缓存中用户[%s]的token查找失败: %v", claims.ID, err.Error())
return nil, errors.New("token不存在")
}
} }
if userToken != tokenStr { if userToken != tokenStr {

View File

@ -36,18 +36,6 @@ func Error(err error) string {
return errMsg 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 // initValidatorTranslator
// @description: 初始化翻译机 // @description: 初始化翻译机
// @receiver vli // @receiver vli

View File

@ -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-ui\\template\\tmp\\" + fmt.Sprintf("%s.conf", clientInfo.Name) outPath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\" + fmt.Sprintf("%s.conf", clientInfo.Name)
templatePath = "E:\\Workspace\\Go\\wireguard-ui\\template\\conf\\wg.client.conf" templatePath = "E:\\Workspace\\Go\\wireguard-dashboard\\template\\wg.client.conf"
} }
err = Template().Execute(templatePath, outPath, execData) err = Template().Execute(templatePath, outPath, execData)

View File

@ -7,5 +7,6 @@ type config struct {
Database *database `yaml:"database"` Database *database `yaml:"database"`
Cache *cache `yaml:"redis"` Cache *cache `yaml:"redis"`
File *file `yaml:"file"` File *file `yaml:"file"`
Mail *mail `yaml:"email"`
Wireguard *wireguard `yaml:"wireguard"` Wireguard *wireguard `yaml:"wireguard"`
} }

9
config/mail.go Normal file
View File

@ -0,0 +1,9 @@
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"`
}

View File

@ -1,19 +0,0 @@
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()
}

View File

@ -1,119 +0,0 @@
package task
import (
"context"
"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/global/constant"
"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())
var ipAllocation string
for _, iaip := range peer.AllowedIPs {
ipAllocation += iaip.String() + ","
}
// 去除一下最右边的逗号
if len(ipAllocation) > 0 {
ipAllocation = strings.TrimRight(ipAllocation, ",")
}
// 如果存在,判断离线时间
if !online {
// 已经离线,发送通知
msg := fmt.Sprintf(`[离线通知]
客户端名称 : %v
客户端IP : %v
最后在线时间 : %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
}
// 离线了,设置离线标识
client.Redis.Set(context.Background(), fmt.Sprintf("%s%s", constant.ClientOffline, utils.Hash().MD5(ipAllocation)), true, 0)
} else {
// 判断是否存在缓存
if client.Redis.Exists(context.Background(), fmt.Sprintf("%s%s", constant.ClientOffline, utils.Hash().MD5(ipAllocation))).Val() > 0 {
// 存在,删除离线标识
client.Redis.Del(context.Background(), fmt.Sprintf("%s%s", constant.ClientOffline, utils.Hash().MD5(ipAllocation)))
// 微信通知该客户端已经上线
msg := fmt.Sprintf(`[上线通知]
客户端名称 : %v
客户端IP : %v
最后在线时间 : %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
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View File

@ -1,8 +1,6 @@
package constant package constant
const ( const (
Captcha = "captcha" Captcha = "captcha"
UserToken = "token" UserToken = "token"
TUIUserToken = "tui:token"
ClientOffline = "client:offline:"
) )

73
go.mod
View File

@ -3,68 +3,51 @@ module wireguard-ui
go 1.21 go 1.21
require ( require (
gitee.ltd/lxh/logger v1.0.18 gitee.ltd/lxh/logger v1.0.15
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/lipgloss v0.13.0
github.com/cowardmrx/go_aliyun_oss v1.0.7 github.com/cowardmrx/go_aliyun_oss v1.0.7
github.com/dustin/go-humanize v1.0.1
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
github.com/fsnotify/fsnotify v1.7.0 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/gin-gonic/gin v1.10.0
github.com/glebarez/sqlite v1.11.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/go-resty/resty/v2 v2.13.1
github.com/golang-jwt/jwt/v5 v5.2.1 github.com/redis/go-redis/v9 v9.5.3
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.7.0
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/spf13/cast v1.6.0
github.com/spf13/viper v1.19.0 github.com/spf13/viper v1.19.0
github.com/urfave/cli/v2 v2.27.5
golang.org/x/crypto v0.31.0
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 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/mysql v1.5.7
gorm.io/driver/postgres v1.5.11 gorm.io/driver/postgres v1.5.9
gorm.io/gorm v1.25.10 gorm.io/gorm v1.25.10
) )
require ( require (
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible // indirect github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.12.5 // indirect github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/caarlos0/env/v6 v6.10.1 // indirect github.com/caarlos0/env/v6 v6.10.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/charmbracelet/bubbletea v1.1.0 // indirect
github.com/charmbracelet/x/ansi v0.2.3 // indirect
github.com/charmbracelet/x/term v0.2.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect github.com/cloudwego/iasm v0.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // 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/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-kit/kit v0.12.0 // indirect github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.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/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.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/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.6.0 // 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/hashicorp/hcl v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
@ -72,17 +55,15 @@ require (
github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/jonboulle/clockwork v0.4.0 // indirect github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/jpillora/backoff v1.0.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/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/lixh00/loki-client-go v1.0.1 // indirect github.com/lixh00/loki-client-go v1.0.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect
@ -90,12 +71,9 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/mojocn/base64Captcha v1.3.6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible // 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/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.13.0 // indirect github.com/prometheus/client_golang v1.13.0 // indirect
@ -104,34 +82,35 @@ require (
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect
github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24 // indirect github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.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/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.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/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.uber.org/atomic v1.10.0 // indirect go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.23.0 // indirect go.uber.org/zap v1.23.0 // indirect
golang.org/x/arch v0.8.0 // indirect golang.org/x/arch v0.8.0 // indirect
golang.org/x/image v0.18.0 // indirect golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.33.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/image v0.13.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.18.0 // indirect golang.org/x/oauth2 v0.18.0 // indirect
golang.org/x/sync v0.10.0 // indirect golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.28.0 // indirect golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.21.0 // indirect golang.org/x/text v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b // indirect golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
google.golang.org/grpc v1.62.1 // indirect google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.34.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/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect

111
go.sum
View File

@ -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= 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= 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= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gitee.ltd/lxh/logger v1.0.18 h1:LKc0Glk+jx08qDaX1kYIEHZUxUJtV3uqP5ceYK352YY= gitee.ltd/lxh/logger v1.0.15 h1:x/HIHujq01ofltBK6WduYs890hiJVlU2ES6ytKusVZA=
gitee.ltd/lxh/logger v1.0.18/go.mod h1:hzd8prYXbac9YMaEDFMHYHMKKm1LqiFVtHjENWi+qQE= gitee.ltd/lxh/logger v1.0.15/go.mod h1:Ef3o9duDaGApRUlibvkG92QBFM3HcndI0U30n/SMfYk=
github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= 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-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= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
@ -60,8 +60,8 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
@ -106,10 +106,6 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.35.5/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= github.com/aws/aws-sdk-go v1.35.5/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@ -126,11 +122,10 @@ 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/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 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.12.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= 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/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 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/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 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
@ -143,18 +138,6 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c=
github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY=
github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q=
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -176,8 +159,6 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/cowardmrx/go_aliyun_oss v1.0.7 h1:MCSKUWi4RZnHhwe4fd7VAsgeRXL0kT9z56TTde+1lME= github.com/cowardmrx/go_aliyun_oss v1.0.7 h1:MCSKUWi4RZnHhwe4fd7VAsgeRXL0kT9z56TTde+1lME=
github.com/cowardmrx/go_aliyun_oss v1.0.7/go.mod h1:xz6B8H840TX7yPcgSLUbK7q6nnEsxFutaltR08Aetdg= github.com/cowardmrx/go_aliyun_oss v1.0.7/go.mod h1:xz6B8H840TX7yPcgSLUbK7q6nnEsxFutaltR08Aetdg=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -205,8 +186,6 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
@ -214,8 +193,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
@ -233,6 +210,10 @@ 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/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 v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/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= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
@ -245,8 +226,6 @@ 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/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/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/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 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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -330,6 +309,8 @@ 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/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 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 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 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 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= github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
@ -531,8 +512,6 @@ 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/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/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.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 h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= 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= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
@ -592,8 +571,6 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lixh00/loki-client-go v1.0.1 h1:y/ePf/s66N77eikIujRS/QQAKvbMmPmesMxAuMuP8lM= github.com/lixh00/loki-client-go v1.0.1 h1:y/ePf/s66N77eikIujRS/QQAKvbMmPmesMxAuMuP8lM=
github.com/lixh00/loki-client-go v1.0.1/go.mod h1:JSeu3fIBPjnmf5bBq6I8hvJlhYum2eLQEzwU149vyfQ= github.com/lixh00/loki-client-go v1.0.1/go.mod h1:JSeu3fIBPjnmf5bBq6I8hvJlhYum2eLQEzwU149vyfQ=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
@ -617,12 +594,8 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@ -664,12 +637,6 @@ github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DG
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
@ -713,8 +680,6 @@ 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.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/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/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.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/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= github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE=
@ -784,17 +749,12 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua
github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24 h1:V/4Cj2GytqdqK7OMEz6c4LNjey3SNyfw3pg5jPKtJvQ= github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24 h1:V/4Cj2GytqdqK7OMEz6c4LNjey3SNyfw3pg5jPKtJvQ=
github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24/go.mod h1:MDRkz271loM/PrYN+wUNEaTMDGSP760MQzB0yEjdgSQ= github.com/prometheus/prometheus v1.8.2-0.20201028100903-3245b3267b24/go.mod h1:MDRkz271loM/PrYN+wUNEaTMDGSP760MQzB0yEjdgSQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= 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/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/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
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 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/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -804,8 +764,6 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
@ -870,7 +828,6 @@ 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.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.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.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.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 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@ -888,8 +845,6 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
@ -897,8 +852,6 @@ github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHM
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg= github.com/xlab/treeprint v1.0.0/go.mod h1:IoImgRak9i3zJyuxOKUP1v4UZd1tMoKkq/Cimt1uhCg=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -924,8 +877,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 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 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.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 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.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
@ -936,6 +889,7 @@ 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.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= 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 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -957,9 +911,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -973,14 +926,13 @@ 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-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-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-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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-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.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.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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -1050,9 +1002,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1077,9 +1028,8 @@ 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-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.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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.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-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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1143,7 +1093,6 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -1152,9 +1101,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -1173,9 +1121,8 @@ 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.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.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.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.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1399,8 +1346,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

View File

@ -243,14 +243,7 @@ func (ClientApi) Download(c *gin.Context) {
return 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 { if err != nil {
response.R(c).FailedWithError("发送邮件失败") response.R(c).FailedWithError("发送邮件失败")
return return

View File

@ -76,7 +76,7 @@ func (LoginApi) Login(c *gin.Context) {
secret := component.JWT().GenerateSecret(p.Password, uuid.NewString(), time.Now().Local().String()) secret := component.JWT().GenerateSecret(p.Password, uuid.NewString(), time.Now().Local().String())
// 生成token // 生成token
token, expireAt, err := component.JWT().GenerateToken(user.Id, secret, "http") token, expireAt, err := component.JWT().GenerateToken(user.Id, secret)
if err != nil { if err != nil {
log.Errorf("用户[%s]生成token失败: %v", user.Account, err.Error()) log.Errorf("用户[%s]生成token失败: %v", user.Account, err.Error())
response.R(c).FailedWithError("登陆失败!") response.R(c).FailedWithError("登陆失败!")

View File

@ -1,33 +0,0 @@
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
}

View File

@ -1,17 +1,11 @@
package api package api
import ( import (
"encoding/json"
"errors"
"fmt"
"gitee.ltd/lxh/logger/log" "gitee.ltd/lxh/logger/log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"os"
"slices" "slices"
"wireguard-ui/component"
"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"
@ -67,7 +61,7 @@ func (setting) Delete(c *gin.Context) {
return return
} }
if err := service.Setting().Model(&model.Setting{}).Where("code NOT IN (?)", []string{"WG_SETTING", "WG_SERVER"}).Where("code = ?", code).Delete(&model.Setting{}).Error; err != nil { if err := service.Setting().Model(&model.Setting{}).Where("code = ?", code).Delete(&model.Setting{}).Error; err != nil {
response.R(c).FailedWithError("删除失败") response.R(c).FailedWithError("删除失败")
return return
} }
@ -119,99 +113,3 @@ 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())
}
}
// 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()
}

View File

@ -1,26 +0,0 @@
package http
import (
"fmt"
"gitee.ltd/lxh/logger/log"
"net/http"
"wireguard-ui/config"
"wireguard-ui/http/router"
)
// Kernel
// @description: http启动
// @return error
func Kernel() error {
router.Rooters()
handler := router.InitRouter()
addr := fmt.Sprintf(":%d", config.Config.Http.Port)
httpServer := http.Server{
Addr: addr,
Handler: handler,
}
log.Infof("[HTTP] server runing in %s", addr)
return httpServer.ListenAndServe()
}

View File

@ -33,7 +33,7 @@ func Authorization() gin.HandlerFunc {
return return
} }
userClaims, err := component.JWT().ParseToken(token, hashPassword, "http") userClaims, err := component.JWT().ParseToken(token, hashPassword)
if err != nil { if err != nil {
response.R(c).AuthorizationFailed("未登陆") response.R(c).AuthorizationFailed("未登陆")
c.Abort() c.Abort()
@ -70,7 +70,7 @@ func Authorization() gin.HandlerFunc {
// 生成一个新token // 生成一个新token
secret := component.JWT().GenerateSecret(user.Password, uuid.NewString(), time.Now().Local().String()) secret := component.JWT().GenerateSecret(user.Password, uuid.NewString(), time.Now().Local().String())
tokenStr, _, err := component.JWT().GenerateToken(user.Id, secret, "http", userClaims.ExpiresAt.Time, time.Now().Local()) tokenStr, _, err := component.JWT().GenerateToken(user.Id, secret, userClaims.ExpiresAt.Time, time.Now().Local())
if err != nil { if err != nil {
response.R(c).AuthorizationFailed("校验失败") response.R(c).AuthorizationFailed("校验失败")
c.Abort() c.Abort()

View File

@ -1,7 +1,5 @@
package param package param
import "mime/multipart"
// SetSetting // SetSetting
// @description: 添加/编辑设置 // @description: 添加/编辑设置
type SetSetting struct { type SetSetting struct {
@ -9,9 +7,3 @@ type SetSetting struct {
Data string `json:"data" form:"data" binding:"required"` Data string `json:"data" form:"data" binding:"required"`
Describe string `json:"describe" form:"describe" binding:"omitempty"` Describe string `json:"describe" form:"describe" binding:"omitempty"`
} }
// Import
// @description: 导入
type Import struct {
File *multipart.FileHeader `json:"file" form:"file" binding:"required"`
}

View File

@ -17,7 +17,5 @@ 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) // 导出配置文件
setting.POST("/import", api.Setting().Import) // 导入配置
} }
} }

View File

@ -1,74 +1,9 @@
package vo package vo
import (
"wireguard-ui/global/constant"
"wireguard-ui/model"
)
// SettingItem // SettingItem
// @description: 设置单项 // @description: 设置单项
type SettingItem struct { type SettingItem struct {
Code string `json:"code"` Code string `json:"code"`
Data string `json:"data"` Data string `json:"data"`
Describe string `json:"describe"` Describe string `json:"describe"`
CreatedAt model.JsonTime `json:"createdAt"`
UpdatedAt model.JsonTime `json:"updatedAt"`
}
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"`
} }

View File

@ -6,7 +6,6 @@ import (
"gitee.ltd/lxh/logger/log" "gitee.ltd/lxh/logger/log"
"github.com/cowardmrx/go_aliyun_oss" "github.com/cowardmrx/go_aliyun_oss"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite" "github.com/glebarez/sqlite"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
@ -16,7 +15,6 @@ import (
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
gl "gorm.io/gorm/logger" gl "gorm.io/gorm/logger"
"os"
"time" "time"
"wireguard-ui/config" "wireguard-ui/config"
"wireguard-ui/global/client" "wireguard-ui/global/client"
@ -142,14 +140,8 @@ func initOSS() {
// initLogger // initLogger
// @description: 初始化日志 // @description: 初始化日志
func initLogger() { func initLogger() {
mode := logger.Dev
if os.Getenv("GIN_MODE") == gin.ReleaseMode {
mode = logger.Prod
}
logger.InitLogger(logger.LogConfig{ logger.InitLogger(logger.LogConfig{
Mode: mode, Mode: logger.Dev,
FileEnable: true, FileEnable: true,
}) })
} }

44
main.go
View File

@ -1,15 +1,16 @@
package main package main
import ( import (
"fmt"
"gitee.ltd/lxh/logger/log" "gitee.ltd/lxh/logger/log"
"github.com/urfave/cli/v2" "github.com/gin-contrib/pprof"
"github.com/spf13/cast"
"math/rand" "math/rand"
"net/http"
"os" "os"
"sort"
"time" "time"
tui "wireguard-ui/cli" "wireguard-ui/config"
"wireguard-ui/cron" "wireguard-ui/http/router"
"wireguard-ui/http"
"wireguard-ui/initialize" "wireguard-ui/initialize"
"wireguard-ui/script" "wireguard-ui/script"
) )
@ -19,38 +20,23 @@ func init() {
if err := script.New().Do(); err != nil { if err := script.New().Do(); err != nil {
log.Errorf("执行脚本失败: %v", err.Error()) log.Errorf("执行脚本失败: %v", err.Error())
} }
cron.Task()
} }
func main() { func main() {
rand.New(rand.NewSource(time.Now().Local().UnixNano())) rand.New(rand.NewSource(time.Now().Local().UnixNano()))
router.Rooters()
handler := router.InitRouter()
app := &cli.App{ if cast.ToBool(os.Getenv("ENABLED_PPROF")) {
Name: "wireguard-ui", pprof.Register(handler, "/monitoring")
Usage: "wireguard-manager-ui",
} }
app.Commands = []*cli.Command{ httpServe := http.Server{
{ Addr: fmt.Sprintf(":%d", config.Config.Http.Port),
Name: "http:serve", Handler: handler,
Aliases: []string{"app:serve"},
Usage: "",
Action: func(ctx *cli.Context) error {
return http.Kernel()
},
},
{
Name: "cmd:serve",
Aliases: []string{"command:serve"},
Usage: "use command exec",
Action: func(ctx *cli.Context) error {
return tui.Kernel()
},
},
} }
sort.Sort(cli.CommandsByName(app.Commands)) if err := httpServe.ListenAndServe(); err != nil {
if err := app.Run(os.Args); err != nil { log.Panicf("启动http服务端失败: %v", err.Error())
log.Fatalf("服务启动失败: %v", err.Error())
} }
} }

16
model/oauth_client.go Normal file
View File

@ -0,0 +1,16 @@
package model
// AuthClient
// @description: 认证客户端
type AuthClient struct {
Base
Name string `json:"name" gorm:"type:varchar(255);not null;comment: '客户端名称'"`
ClientID string `json:"clientID" gorm:"type:varchar(255);not null;comment: '客户端ID'"`
ClientKey string `json:"clientKey" gorm:"type:varchar(255);not null;comment: '客户端key'"`
ExpireAt string `json:"expireAt" gorm:"type:varchar(255);not null;comment: '过期时间'"`
IsEnabled int `json:"isEnabled" gorm:"type:int(1);not null;comment: '是否启用'"`
}
func (AuthClient) TableName() string {
return "t_oauth_client"
}

View File

@ -22,16 +22,3 @@ 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"
}

View File

@ -3,7 +3,7 @@ package service
import "gorm.io/gorm" import "gorm.io/gorm"
func Paginate(current, size int64) func(db *gorm.DB) *gorm.DB { func Paginate(current, size int64) func(db *gorm.DB) *gorm.DB {
// 如果页码是-1就不分页👋 // 如果页码是-1就不分页
if current == -1 { if current == -1 {
return func(db *gorm.DB) *gorm.DB { return func(db *gorm.DB) *gorm.DB {
return db return db

View File

@ -2,16 +2,11 @@ package service
import ( import (
"encoding/json" "encoding/json"
"fmt"
slog "gitee.ltd/lxh/logger/log"
"gorm.io/gorm" "gorm.io/gorm"
"strings"
gdb "wireguard-ui/global/client" gdb "wireguard-ui/global/client"
"wireguard-ui/http/param"
"wireguard-ui/http/vo" "wireguard-ui/http/vo"
"wireguard-ui/model" "wireguard-ui/model"
"wireguard-ui/template/render_data" "wireguard-ui/template/render_data"
"wireguard-ui/utils"
) )
type setting struct{ *gorm.DB } type setting struct{ *gorm.DB }
@ -86,157 +81,6 @@ func (s setting) GetWGServerForConfig() (data *render_data.Server, err error) {
// @return data // @return data
// @return err // @return err
func (s setting) GetAllSetting(blackList []string) (data []vo.SettingItem, err error) { func (s setting) GetAllSetting(blackList []string) (data []vo.SettingItem, err error) {
err = s.Model(&model.Setting{}).Select("code, data, describe,created_at,updated_at").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
} }
// 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 := &param.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
}

View File

@ -1,7 +1,6 @@
package service package service
import ( import (
"errors"
"gorm.io/gorm" "gorm.io/gorm"
gdb "wireguard-ui/global/client" gdb "wireguard-ui/global/client"
"wireguard-ui/global/constant" "wireguard-ui/global/constant"
@ -40,11 +39,6 @@ func (s user) CreateUser(user *model.User) (err error) {
return s.Model(&model.User{}).Where("id = ?", user.Id).Updates(&updates).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") defaultPassword := utils.Password().GenerateHashPassword("admin123")
if user.Password == "" { // 没有密码给一个默认密码 if user.Password == "" { // 没有密码给一个默认密码
user.Password = defaultPassword user.Password = defaultPassword

View File

@ -1,28 +0,0 @@
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
}

View File

@ -2,35 +2,28 @@ package utils
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/jordan-wright/email" "github.com/jordan-wright/email"
"github.com/spf13/cast"
"mime" "mime"
"net/smtp" "net/smtp"
"net/textproto" "net/textproto"
"path/filepath" "path/filepath"
"wireguard-ui/model" "wireguard-ui/config"
) )
type mail struct { type mail struct {
*email.Email *email.Email
addr string addr string
auth smtp.Auth auth smtp.Auth
conf map[string]string
} }
func Mail(conf *model.Setting) *mail { func Mail() *mail {
// 解析配置文件
var mailConf = make(map[string]string)
_ = json.Unmarshal([]byte(conf.Data), &mailConf)
var m mail var m mail
em := email.NewEmail() em := email.NewEmail()
m.Email = em m.Email = em
m.auth = smtp.PlainAuth("", mailConf["user"], mailConf["password"], mailConf["host"]) m.auth = smtp.PlainAuth("", config.Config.Mail.User, config.Config.Mail.Password, config.Config.Mail.Host)
m.addr = fmt.Sprintf("%s:%s", mailConf["host"], mailConf["port"]) m.addr = fmt.Sprintf("%s:%d", config.Config.Mail.Host, config.Config.Mail.Port)
m.conf = mailConf
return &m return &m
} }
@ -54,7 +47,7 @@ func (m *mail) VerifyConfig() (err error) {
// @param attacheFilePath // @param attacheFilePath
// @return err // @return err
func (m *mail) SendMail(to, subject, content, attacheFilePath string) (err error) { func (m *mail) SendMail(to, subject, content, attacheFilePath string) (err error) {
m.From = fmt.Sprintf("wg-dashboard <%s>", m.conf["user"]) m.From = fmt.Sprintf("wg-dashboard <%s>", config.Config.Mail.User)
m.To = []string{to} m.To = []string{to}
m.Subject = subject m.Subject = subject
m.Text = []byte(content) m.Text = []byte(content)
@ -68,13 +61,13 @@ func (m *mail) SendMail(to, subject, content, attacheFilePath string) (err error
atch.Header = emHeader atch.Header = emHeader
} }
if cast.ToBool(m.conf["skipTls"]) { if config.Config.Mail.SkipTls {
return m.Send(m.addr, m.auth) return m.Send(m.addr, m.auth)
} }
tlsConfig := &tls.Config{} tlsConfig := &tls.Config{}
tlsConfig.InsecureSkipVerify = cast.ToBool(m.conf["skipTls"]) tlsConfig.InsecureSkipVerify = config.Config.Mail.SkipTls
tlsConfig.ServerName = m.conf["host"] tlsConfig.ServerName = config.Config.Mail.Host
return m.SendWithTLS(m.addr, m.auth, tlsConfig) return m.SendWithTLS(m.addr, m.auth, tlsConfig)
} }

View File

@ -1,65 +0,0 @@
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
}

View File

@ -1,3 +0,0 @@
VITE_TITLE = 'Wireguard-UI'
VITE_PORT = 3100

View File

@ -32,7 +32,6 @@
}, },
"dependencies": { "dependencies": {
"@unocss/eslint-config": "^0.55.7", "@unocss/eslint-config": "^0.55.7",
"@vicons/ionicons5": "^0.12.0",
"@vueuse/core": "^10.4.1", "@vueuse/core": "^10.4.1",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "5.1.12", "@wangeditor/editor-for-vue": "5.1.12",
@ -41,7 +40,6 @@
"echarts": "^5.4.3", "echarts": "^5.4.3",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"md-editor-v3": "^4.7.0", "md-editor-v3": "^4.7.0",
"mitt": "^3.0.1",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"pinia": "^2.1.6", "pinia": "^2.1.6",
"vite": "^4.4.11", "vite": "^4.4.11",

16
web/pnpm-lock.yaml generated
View File

@ -11,9 +11,6 @@ importers:
'@unocss/eslint-config': '@unocss/eslint-config':
specifier: ^0.55.7 specifier: ^0.55.7
version: 0.55.7(eslint@8.50.0)(typescript@5.2.2) version: 0.55.7(eslint@8.50.0)(typescript@5.2.2)
'@vicons/ionicons5':
specifier: ^0.12.0
version: 0.12.0
'@vueuse/core': '@vueuse/core':
specifier: ^10.4.1 specifier: ^10.4.1
version: 10.4.1(vue@3.3.4) version: 10.4.1(vue@3.3.4)
@ -38,9 +35,6 @@ importers:
md-editor-v3: md-editor-v3:
specifier: ^4.7.0 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) 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: mockjs:
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0 version: 1.1.0
@ -1052,9 +1046,6 @@ packages:
'@vavt/util@1.4.0': '@vavt/util@1.4.0':
resolution: {integrity: sha512-qLhaokwifMTFqoo4UE2JZUyaxCzX9T4WcIt2KzznbtBrCM4CG119pY/cKqq6jDa3c1phUvPCoIfjWfnF9nj4NA==} 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': '@vite-plugin-vue-devtools/core@1.0.0-rc.7':
resolution: {integrity: sha512-Tv9JeRZQ6KDwSkOQJvXc5TBcc4fkSazA96GDhi99v4VCthTgXjnhaah41CeZD3hFDKnNS0MHKFFqN+RHAgYDyQ==} resolution: {integrity: sha512-Tv9JeRZQ6KDwSkOQJvXc5TBcc4fkSazA96GDhi99v4VCthTgXjnhaah41CeZD3hFDKnNS0MHKFFqN+RHAgYDyQ==}
peerDependencies: peerDependencies:
@ -3017,9 +3008,6 @@ packages:
minimist@1.2.8: minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
mixin-deep@1.3.2: mixin-deep@1.3.2:
resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -5502,8 +5490,6 @@ snapshots:
'@vavt/util@1.4.0': {} '@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))': '@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: dependencies:
'@babel/parser': 7.23.0 '@babel/parser': 7.23.0
@ -7605,8 +7591,6 @@ snapshots:
minimist@1.2.8: {} minimist@1.2.8: {}
mitt@3.0.1: {}
mixin-deep@1.3.2: mixin-deep@1.3.2:
dependencies: dependencies:
for-in: 1.0.2 for-in: 1.0.2

View File

@ -1,10 +0,0 @@
import { request } from '@/utils'
export default {
exportConfig: () => request.get('/setting/export'), // 导出配置
importConfig: (data) => request.post('/setting/import',data,{
headers: {
'Content-Type': 'multipart/form-data'
}
}),
}

View File

@ -1,126 +0,0 @@
<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>

View File

@ -72,11 +72,8 @@
import { useUserStore } from '@/store' import { useUserStore } from '@/store'
import { renderIcon } from '@/utils' import { renderIcon } from '@/utils'
import api from '@/api/user' import api from '@/api/user'
import event from '@/utils/event/event'
import { router } from '@/router'
const userStore = useUserStore() const userStore = useUserStore()
const { $bus } = event();
const options = [ const options = [
{ {
@ -215,9 +212,6 @@ async function updateInfo() {
infoFormModel.value = ref(null) infoFormModel.value = ref(null)
showInfoModel.value = false showInfoModel.value = false
await useUserStore().getUserInfo() await useUserStore().getUserInfo()
if (router.options.history.location === '/user') {
$bus.emit('refreshUserInfo',true);
}
} }
}) })
} }

View File

@ -3,12 +3,7 @@
<MenuCollapse /> <MenuCollapse />
<BreadCrumb ml-15 hidden sm:block /> <BreadCrumb ml-15 hidden sm:block />
</div> </div>
<div ml-auto flex items-center v-if="loginUser.account === 'admin'"> <div ml-auto flex items-center>
<Export/>
<FullScreen />
<UserAvatar />
</div>
<div ml-auto flex items-center v-else>
<FullScreen /> <FullScreen />
<UserAvatar /> <UserAvatar />
</div> </div>
@ -19,7 +14,4 @@ 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>

View File

@ -9,14 +9,10 @@ import { setupRouter } from '@/router'
import { setupStore } from '@/store' import { setupStore } from '@/store'
import App from './App.vue' import App from './App.vue'
import { setupNaiveDiscreteApi } from './utils' import { setupNaiveDiscreteApi } from './utils'
import mitt from 'mitt'
const EventMitt = mitt();
async function setupApp() { async function setupApp() {
const app = createApp(App) const app = createApp(App)
setupStore(app) setupStore(app)
app.config.globalProperties.$bus = EventMitt;
await setupRouter(app) await setupRouter(app)
app.mount('#app') app.mount('#app')
setupNaiveDiscreteApi() setupNaiveDiscreteApi()

View File

@ -37,6 +37,7 @@ export async function addDynamicRoutes() {
// 有token的情况 // 有token的情况
const userStore = useUserStore() const userStore = useUserStore()
try { try {
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
!userStore.id && (await userStore.getUserInfo()) !userStore.id && (await userStore.getUserInfo())
@ -48,8 +49,8 @@ export async function addDynamicRoutes() {
router.addRoute(NOT_FOUND_ROUTE) router.addRoute(NOT_FOUND_ROUTE)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
userStore.logout()
$message.error('初始化用户信息失败: ' + error) $message.error('初始化用户信息失败: ' + error)
userStore.logout()
} }
} }

View File

@ -1,7 +0,0 @@
import { getCurrentInstance } from "vue";
export default function event() {
const instance = getCurrentInstance();
const globalProperties = instance?.appContext.config.globalProperties;
return { ...globalProperties };
}

View File

@ -1 +0,0 @@
export * from './event'

View File

@ -2,4 +2,3 @@ export * from './common'
export * from './storage' export * from './storage'
export * from './http' export * from './http'
export * from './auth' export * from './auth'
export * from './event'

View File

@ -85,14 +85,14 @@
{{ cip }} {{ cip }}
</n-button> </n-button>
</n-form-item> </n-form-item>
<!-- <n-form-item label="可访问IP:">--> <n-form-item label="可访问IP:">
<!-- <n-button v-if="row.allowedIps.length <= 0" dashed size="small">--> <n-button v-if="row.allowedIps.length <= 0" dashed size="small">
<!-- - --> -
<!-- </n-button>--> </n-button>
<!-- <n-button v-else dashed mr-2 type="warning" v-for="aip in row.allowedIps" size="small">--> <n-button v-else dashed mr-2 type="warning" v-for="aip in row.allowedIps" size="small">
<!-- {{ aip }}--> {{ aip }}
<!-- </n-button>--> </n-button>
<!-- </n-form-item>--> </n-form-item>
<n-form-item label="创建人:"> <n-form-item label="创建人:">
<n-button color="#54150F" dashed size="small"> <n-button color="#54150F" dashed size="small">
{{ row.createUser }} {{ row.createUser }}
@ -106,7 +106,7 @@
禁用 禁用
</n-button> </n-button>
</n-form-item> </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 v-if="row.offlineMonitoring === 1" color="#067748" round :bordered="false" size="small">
启用 启用
</n-button> </n-button>
@ -114,12 +114,12 @@
禁用 禁用
</n-button> </n-button>
</n-form-item> </n-form-item>
<!-- <n-form-item class="timeItem" label="时间:">--> <n-form-item class="timeItem" label="时间:">
<!-- <n-space vertical>--> <n-space vertical>
<!-- <span> 创建时间: {{ row.createdAt }}</span>--> <span> 创建时间: {{ row.createdAt }}</span>
<!-- <span> 更新时间: {{ row.updatedAt }}</span>--> <span> 更新时间: {{ row.updatedAt }}</span>
<!-- </n-space>--> </n-space>
<!-- </n-form-item>--> </n-form-item>
</n-form> </n-form>
</div> </div>
</n-card> </n-card>
@ -228,18 +228,12 @@
<n-radio :value="0" :checked="editModalForm.enabled === 0" @change="editModalForm.enabled = 0">禁用</n-radio> <n-radio :value="0" :checked="editModalForm.enabled === 0" @change="editModalForm.enabled = 0">禁用</n-radio>
</n-radio-group> </n-radio-group>
</n-form-item> </n-form-item>
<n-form-item label="离线通知" path="offlineMonitoring"> <n-form-item label="离线监听" path="offlineMonitoring">
<n-radio-group :value="editModalForm.offlineMonitoring"> <n-radio-group :value="editModalForm.offlineMonitoring">
<n-radio :value="1" :checked="editModalForm.offlineMonitoring === 1" @change="editModalForm.offlineMonitoring = 1">启用</n-radio> <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 :value="0" :checked="editModalForm.offlineMonitoring === 0" @change="editModalForm.offlineMonitoring = 0">禁用</n-radio>
</n-radio-group> </n-radio-group>
</n-form-item> </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-button type="info" style="margin-left: 40%" @click="updateClient()">确认</n-button>
</n-form> </n-form>
</n-modal> </n-modal>
@ -325,7 +319,7 @@
<n-radio :value="0" :checked="addModalForm.enabled === 0" @change="addModalForm.enabled = 0">禁用</n-radio> <n-radio :value="0" :checked="addModalForm.enabled === 0" @change="addModalForm.enabled = 0">禁用</n-radio>
</n-radio-group> </n-radio-group>
</n-form-item> </n-form-item>
<n-form-item label="离线通知" path="offlineMonitoring"> <n-form-item label="离线监听" path="offlineMonitoring">
<n-radio-group :value="addModalForm.offlineMonitoring"> <n-radio-group :value="addModalForm.offlineMonitoring">
<n-radio :value="1" :checked="addModalForm.offlineMonitoring === 1" @change="addModalForm.offlineMonitoring = 1">启用</n-radio> <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> <n-radio :value="0" :checked="addModalForm.offlineMonitoring === 0" @change="addModalForm.offlineMonitoring = 0">禁用</n-radio>
@ -341,9 +335,6 @@ import { NButton } from 'naive-ui'
import { debounce, ellipsis } from '@/utils' import { debounce, ellipsis } from '@/utils'
import clientApi from '@/views/client/api' import clientApi from '@/views/client/api'
import QueryBar from '@/components/query-bar/QueryBar.vue' import QueryBar from '@/components/query-bar/QueryBar.vue'
import event from '@/utils/event/event'
const { $bus } = event();
const selOptions = [ const selOptions = [
{ {
@ -446,9 +437,7 @@ const editModalForm = ref({
presharedKey: '' presharedKey: ''
}, },
enabled: 1, enabled: 1,
offlineMonitoring: 1, offlineMonitoring: 1
createdAt: '',
updatedAt: ''
}) })
// //
@ -584,8 +573,6 @@ function openEditModal(row) {
editModalForm.value.keys.presharedKey = row.keys.presharedKey editModalForm.value.keys.presharedKey = row.keys.presharedKey
editModalForm.value.enabled = row.enabled editModalForm.value.enabled = row.enabled
editModalForm.value.offlineMonitoring = row.offlineMonitoring editModalForm.value.offlineMonitoring = row.offlineMonitoring
editModalForm.value.createdAt = row.createdAt
editModalForm.value.updatedAt = row.updatedAt
} }
// //
@ -671,13 +658,6 @@ function search() {
getClientList() getClientList()
} }
//
$bus.on("refreshClients",value => {
if (value) {
getClientList();
}
})
getClientList() getClientList()
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -6,7 +6,6 @@ export default {
component: Layout, component: Layout,
redirect: '/client', redirect: '/client',
meta: { meta: {
title: '客户端',
order: 2, order: 2,
}, },
children: [ children: [

View File

@ -174,9 +174,6 @@
<n-radio :value="false" :checked="editFormModel.data[index] === false" @change="editFormModel.data[index] = false"></n-radio> <n-radio :value="false" :checked="editFormModel.data[index] === false" @change="editFormModel.data[index] = false"></n-radio>
</n-radio-group> </n-radio-group>
</n-form-item> </n-form-item>
<n-form-item label="配置描述">
<n-input v-model:value="editFormModel.describe" />
</n-form-item>
<n-form-item> <n-form-item>
<n-button type="info" @click="updateSetting">确认</n-button> <n-button type="info" @click="updateSetting">确认</n-button>
</n-form-item> </n-form-item>
@ -212,10 +209,6 @@ import AppPage from '@/components/page/AppPage.vue'
import api from '@/views/setting/api' import api from '@/views/setting/api'
import { NButton } from 'naive-ui' import { NButton } from 'naive-ui'
import { renderIcon } from '@/utils' import { renderIcon } from '@/utils'
import event from '@/utils/event/event'
const { $bus } = event();
const tabCode = ref("")
// //
const tableColumns = [ const tableColumns = [
@ -408,15 +401,12 @@ function tabChange(code) {
switch (code) { switch (code) {
case "Server": case "Server":
getServerConfig() getServerConfig()
tabCode.value = "Server"
break; break;
case "Global": case "Global":
getGlobalConfig() getGlobalConfig()
tabCode.value = "Global"
break; break;
case "Other": case "Other":
allSetting() allSetting()
tabCode.value = "Other"
break; break;
} }
} }
@ -490,22 +480,6 @@ 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() getServerConfig()
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -6,7 +6,6 @@ export default {
component: Layout, component: Layout,
redirect: '/setting', redirect: '/setting',
meta: { meta: {
title: '设置',
order: 3, order: 3,
}, },
children: [ children: [

View File

@ -106,9 +106,6 @@ import userApi from '@/api/user'
import { NAvatar,NTag,NButton } from 'naive-ui' import { NAvatar,NTag,NButton } from 'naive-ui'
import { renderIcon } from '@/utils' import { renderIcon } from '@/utils'
import { useUserStore } from '@/store' import { useUserStore } from '@/store'
import event from '@/utils/event/event'
const { $bus } = event();
const infoFormRef = ref() const infoFormRef = ref()
@ -403,12 +400,6 @@ function addUser() {
showInfoModel.value = true showInfoModel.value = true
} }
$bus.on('refreshUserInfo',value => {
if (value) {
getUserList();
}
})
getUserList() getUserList()
</script> </script>
<style></style> <style></style>

View File

@ -6,7 +6,6 @@ export default {
component: Layout, component: Layout,
redirect: '/user', redirect: '/user',
meta: { meta: {
title: '管理员',
order: 1, order: 1,
}, },
children: [ children: [

View File

@ -10,7 +10,7 @@
</div> </div>
</div> </div>
<p class="mt-40 text-14 opacity-60">{{ dailyPoetry.content || '莫向外求,但从心觅,行有不得,反求诸己。' }}</p> <p class="mt-40 text-14 opacity-60" style="cursor: pointer" @click="dailyPoe">{{ dailyPoetry.content || '莫向外求但从心觅行有不得反求诸己' }}</p>
<p class="mt-32 text-right text-12 opacity-40"> {{ dailyPoetry.author || '佚名' }}</p> <p class="mt-32 text-right text-12 opacity-40"> {{ dailyPoetry.author || '佚名' }}</p>
</n-card> </n-card>
<n-card class="ml-12 w-70%"> <n-card class="ml-12 w-70%">
@ -27,18 +27,6 @@
remote remote
:columns="connectionsColumns" :columns="connectionsColumns"
:data="connectionsData.data" :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> </n-card>
</AppPage> </AppPage>
@ -47,7 +35,7 @@
<script setup> <script setup>
import { useUserStore } from '@/store' import { useUserStore } from '@/store'
import api from '@/views/workbench/api' import api from '@/views/workbench/api'
import { debounce, renderIcon } from '@/utils' import { debounce } from '@/utils'
import { NTag } from 'naive-ui' import { NTag } from 'naive-ui'
const userStore = useUserStore() const userStore = useUserStore()
@ -164,49 +152,6 @@ 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({ const tableData = ref({
data: [] data: []
@ -288,7 +233,7 @@ async function getClientConnections() {
const initFunc = debounce(() => { const initFunc = debounce(() => {
getClientConnections() getClientConnections()
// dailyPoe() dailyPoe()
// connectionList() // connectionList()
},500) },500)

View File

@ -6,7 +6,6 @@ export default {
component: Layout, component: Layout,
redirect: '/workbench', redirect: '/workbench',
meta: { meta: {
title: '工作台',
order: 0, order: 0,
}, },
children: [ children: [

File diff suppressed because one or more lines are too long