Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9497729378 | ||
|
48a58759da | ||
|
36f0202c57 | ||
|
904907f796 | ||
|
9674796f0a | ||
|
b245c26515 | ||
62cdfc6839 | |||
|
014c97f877 | ||
|
1c2bfb0a29 | ||
|
78bd62a964 | ||
|
eba3e841c3 | ||
|
628c09fdde | ||
|
96999be84a | ||
|
fc0a1a04b0 | ||
|
19a025e888 | ||
|
dfe89d465c | ||
|
93911536d9 | ||
|
f09d0d2994 | ||
|
ca42c72e0f | ||
|
683e7b2cc3 | ||
|
accb060e27 | ||
|
8884d945aa | ||
|
788def6dc4 | ||
|
29c1c791d4 | ||
|
40019568f0 |
10
Dockerfile
10
Dockerfile
@ -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"]
|
ENTRYPOINT ["./wgui","http:serve"]
|
10
cli/kernal.go
Normal file
10
cli/kernal.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"wireguard-ui/cli/tui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Kernel() error {
|
||||||
|
tui.NewApp().Run()
|
||||||
|
return nil
|
||||||
|
}
|
165
cli/tui/app.go
Normal file
165
cli/tui/app.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
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
|
||||||
|
}
|
520
cli/tui/client.go
Normal file
520
cli/tui/client.go
Normal file
@ -0,0 +1,520 @@
|
|||||||
|
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 = ¶m.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
|
||||||
|
}
|
62
cli/tui/server.go
Normal file
62
cli/tui/server.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
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, "")
|
||||||
|
}
|
440
cli/tui/setting.go
Normal file
440
cli/tui/setting.go
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
}
|
136
cli/tui/utils.go
Normal file
136
cli/tui/utils.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
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()
|
||||||
|
}
|
@ -36,7 +36,7 @@ func JWT() JwtComponent {
|
|||||||
// @return token
|
// @return token
|
||||||
// @return expireTime
|
// @return expireTime
|
||||||
// @return err
|
// @return err
|
||||||
func (JwtComponent) GenerateToken(userId, secret string, times ...time.Time) (token string, expireTime *jwt.NumericDate, err error) {
|
func (JwtComponent) GenerateToken(userId, secret, source 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,10 +68,19 @@ func (JwtComponent) GenerateToken(userId, secret string, times ...time.Time) (to
|
|||||||
return "", nil, errors.New("生成token失败")
|
return "", nil, errors.New("生成token失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Redis.Set(context.Background(),
|
switch source {
|
||||||
fmt.Sprintf("%s:%s", constant.UserToken, userId),
|
case "http":
|
||||||
token,
|
client.Redis.Set(context.Background(),
|
||||||
time.Duration(expireTime.Sub(time.Now()).Abs().Seconds())*time.Second)
|
fmt.Sprintf("%s:%s", constant.UserToken, userId),
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +90,7 @@ func (JwtComponent) GenerateToken(userId, secret string, times ...time.Time) (to
|
|||||||
// @param token
|
// @param token
|
||||||
// @return *JwtComponent
|
// @return *JwtComponent
|
||||||
// @return error
|
// @return error
|
||||||
func (JwtComponent) ParseToken(token, secret string) (*JwtComponent, error) {
|
func (JwtComponent) ParseToken(token, secret, source 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) {
|
||||||
@ -89,10 +98,20 @@ func (JwtComponent) ParseToken(token, secret string) (*JwtComponent, error) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if claims, ok := t.Claims.(*JwtComponent); ok && t.Valid {
|
if claims, ok := t.Claims.(*JwtComponent); ok && t.Valid {
|
||||||
userToken, err := client.Redis.Get(context.Background(), fmt.Sprintf("%s:%s", constant.UserToken, claims.ID)).Result()
|
var userToken string
|
||||||
if err != nil {
|
switch source {
|
||||||
log.Errorf("缓存中用户[%s]的token查找失败: %v", claims.ID, err.Error())
|
case "http":
|
||||||
return nil, errors.New("token不存在")
|
userToken, err = client.Redis.Get(context.Background(), fmt.Sprintf("%s:%s", constant.UserToken, claims.ID)).Result()
|
||||||
|
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 {
|
||||||
|
18
cron/cron.go
18
cron/cron.go
@ -1 +1,19 @@
|
|||||||
package cron
|
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()
|
||||||
|
}
|
||||||
|
119
cron/task/client.go
Normal file
119
cron/task/client.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
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
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
package constant
|
package constant
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Captcha = "captcha"
|
Captcha = "captcha"
|
||||||
UserToken = "token"
|
UserToken = "token"
|
||||||
|
TUIUserToken = "tui:token"
|
||||||
|
ClientOffline = "client:offline:"
|
||||||
)
|
)
|
||||||
|
46
go.mod
46
go.mod
@ -3,13 +3,16 @@ module wireguard-ui
|
|||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitee.ltd/lxh/logger v1.0.15
|
gitee.ltd/lxh/logger v1.0.18
|
||||||
|
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/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/locales v0.14.1
|
||||||
github.com/go-playground/universal-translator v0.18.1
|
github.com/go-playground/universal-translator v0.18.1
|
||||||
github.com/go-playground/validator/v10 v10.22.0
|
github.com/go-playground/validator/v10 v10.22.0
|
||||||
@ -19,28 +22,36 @@ require (
|
|||||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/mojocn/base64Captcha v1.3.6
|
github.com/mojocn/base64Captcha v1.3.6
|
||||||
github.com/redis/go-redis/v9 v9.5.3
|
github.com/redis/go-redis/v9 v9.7.0
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
github.com/spf13/cast v1.6.0
|
github.com/spf13/cast v1.6.0
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
golang.org/x/crypto v0.23.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
|
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.9
|
gorm.io/driver/postgres v1.5.11
|
||||||
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.11.6 // indirect
|
github.com/bytedance/sonic v1.12.5 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
github.com/bytedance/sonic/loader v0.2.0 // 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/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // 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
|
||||||
@ -61,13 +72,17 @@ 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/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/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
|
||||||
@ -75,8 +90,12 @@ 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/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
|
||||||
@ -85,6 +104,9 @@ 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/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
@ -93,17 +115,17 @@ require (
|
|||||||
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/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
|
||||||
golang.org/x/image v0.18.0 // indirect
|
golang.org/x/image v0.18.0 // indirect
|
||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/oauth2 v0.18.0 // indirect
|
golang.org/x/oauth2 v0.18.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
golang.org/x/text v0.16.0 // indirect
|
golang.org/x/text v0.21.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
|
||||||
|
105
go.sum
105
go.sum
@ -35,8 +35,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
|||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
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.15 h1:x/HIHujq01ofltBK6WduYs890hiJVlU2ES6ytKusVZA=
|
gitee.ltd/lxh/logger v1.0.18 h1:LKc0Glk+jx08qDaX1kYIEHZUxUJtV3uqP5ceYK352YY=
|
||||||
gitee.ltd/lxh/logger v1.0.15/go.mod h1:Ef3o9duDaGApRUlibvkG92QBFM3HcndI0U30n/SMfYk=
|
gitee.ltd/lxh/logger v1.0.18/go.mod h1:hzd8prYXbac9YMaEDFMHYHMKKm1LqiFVtHjENWi+qQE=
|
||||||
github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/Azure/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 v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
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,6 +106,10 @@ 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=
|
||||||
@ -122,10 +126,11 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
|||||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
github.com/bsm/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.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
github.com/bytedance/sonic v1.12.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w=
|
||||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
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=
|
||||||
@ -138,6 +143,18 @@ 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=
|
||||||
@ -159,6 +176,8 @@ 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=
|
||||||
@ -186,6 +205,8 @@ 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=
|
||||||
@ -193,6 +214,8 @@ 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=
|
||||||
@ -210,8 +233,6 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq
|
|||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
github.com/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/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=
|
||||||
@ -224,6 +245,8 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0
|
|||||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/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=
|
||||||
@ -508,6 +531,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
|
|||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/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=
|
||||||
@ -567,6 +592,8 @@ 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=
|
||||||
@ -590,8 +617,12 @@ 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=
|
||||||
@ -633,6 +664,12 @@ 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=
|
||||||
@ -676,6 +713,8 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ
|
|||||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
github.com/openzipkin/zipkin-go v0.2.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=
|
||||||
@ -745,12 +784,17 @@ 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.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
|
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||||
github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||||
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=
|
||||||
@ -760,6 +804,8 @@ 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=
|
||||||
@ -824,6 +870,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.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=
|
||||||
@ -841,6 +888,8 @@ 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=
|
||||||
@ -848,6 +897,8 @@ 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=
|
||||||
@ -873,8 +924,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.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.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=
|
||||||
@ -885,7 +936,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
go.uber.org/zap v1.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=
|
||||||
@ -907,8 +957,9 @@ 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=
|
||||||
@ -922,8 +973,8 @@ 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-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
||||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
golang.org/x/image v0.0.0-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=
|
||||||
@ -999,8 +1050,9 @@ 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=
|
||||||
@ -1025,8 +1077,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-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.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
|
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=
|
||||||
@ -1090,6 +1143,7 @@ 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=
|
||||||
@ -1098,8 +1152,9 @@ 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=
|
||||||
@ -1119,8 +1174,8 @@ 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/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
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=
|
||||||
@ -1344,8 +1399,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.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8=
|
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314=
|
||||||
gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI=
|
gorm.io/driver/postgres v1.5.11/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=
|
||||||
|
@ -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)
|
token, expireAt, err := component.JWT().GenerateToken(user.Id, secret, "http")
|
||||||
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("登陆失败!")
|
||||||
|
@ -67,7 +67,7 @@ func (setting) Delete(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := service.Setting().Model(&model.Setting{}).Where("code = ?", code).Delete(&model.Setting{}).Error; err != nil {
|
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 {
|
||||||
response.R(c).FailedWithError("删除失败")
|
response.R(c).FailedWithError("删除失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
26
http/kernel.go
Normal file
26
http/kernel.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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()
|
||||||
|
}
|
@ -33,7 +33,7 @@ func Authorization() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userClaims, err := component.JWT().ParseToken(token, hashPassword)
|
userClaims, err := component.JWT().ParseToken(token, hashPassword, "http")
|
||||||
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, userClaims.ExpiresAt.Time, time.Now().Local())
|
tokenStr, _, err := component.JWT().GenerateToken(user.Id, secret, "http", userClaims.ExpiresAt.Time, time.Now().Local())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.R(c).AuthorizationFailed("校验失败")
|
response.R(c).AuthorizationFailed("校验失败")
|
||||||
c.Abort()
|
c.Abort()
|
||||||
|
@ -1,21 +1,29 @@
|
|||||||
package vo
|
package vo
|
||||||
|
|
||||||
import "wireguard-ui/global/constant"
|
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 {
|
type Export struct {
|
||||||
Global *Global `json:"global" label:"全局配置" binding:"required"`
|
Global *Global `json:"global" label:"全局配置" binding:"required"`
|
||||||
Server *Server `json:"server" label:"服务端配置" binding:"required"`
|
Server *Server `json:"server" label:"服务端配置" binding:"required"`
|
||||||
Clients []Client `json:"clients" label:"客户端" binding:"omitempty"`
|
Clients []Client `json:"clients" label:"客户端" binding:"omitempty"`
|
||||||
|
Other []Other `json:"other" label:"其他" binding:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global
|
||||||
|
// @description: 全局配置
|
||||||
type Global struct {
|
type Global struct {
|
||||||
MTU int `json:"MTU" label:"MTU" binding:"required"`
|
MTU int `json:"MTU" label:"MTU" binding:"required"`
|
||||||
ConfigFilePath string `json:"configFilePath" label:"配置文件路径" binding:"required"`
|
ConfigFilePath string `json:"configFilePath" label:"配置文件路径" binding:"required"`
|
||||||
@ -26,6 +34,8 @@ type Global struct {
|
|||||||
Table string `json:"table" label:"table" binding:"omitempty"`
|
Table string `json:"table" label:"table" binding:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Server
|
||||||
|
// @description: 服务端信息
|
||||||
type Server struct {
|
type Server struct {
|
||||||
IpScope []string `json:"ipScope" label:"ipScope" binding:"min=1,dive,required"`
|
IpScope []string `json:"ipScope" label:"ipScope" binding:"min=1,dive,required"`
|
||||||
ListenPort int `json:"listenPort" label:"listenPort" binding:"required"`
|
ListenPort int `json:"listenPort" label:"listenPort" binding:"required"`
|
||||||
@ -35,6 +45,8 @@ type Server struct {
|
|||||||
PostDownScript string `json:"postDownScript" label:"postDownScript" binding:"omitempty"`
|
PostDownScript string `json:"postDownScript" label:"postDownScript" binding:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client
|
||||||
|
// @description: 客户端信息
|
||||||
type Client struct {
|
type Client struct {
|
||||||
Name string `json:"name" label:"name" binding:"required"`
|
Name string `json:"name" label:"name" binding:"required"`
|
||||||
Email string `json:"email" label:"email" binding:"omitempty"`
|
Email string `json:"email" label:"email" binding:"omitempty"`
|
||||||
@ -52,3 +64,11 @@ type Client struct {
|
|||||||
Enabled *constant.Status `json:"enabled" label:"enabled" binding:"required"`
|
Enabled *constant.Status `json:"enabled" label:"enabled" binding:"required"`
|
||||||
OfflineMonitoring *constant.Status `json:"offlineMonitoring" label:"offlineMonitoring" 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"`
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ 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"
|
||||||
@ -15,6 +16,7 @@ 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"
|
||||||
@ -140,8 +142,14 @@ 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: logger.Dev,
|
Mode: mode,
|
||||||
FileEnable: true,
|
FileEnable: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
44
main.go
44
main.go
@ -1,16 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"gitee.ltd/lxh/logger/log"
|
"gitee.ltd/lxh/logger/log"
|
||||||
"github.com/gin-contrib/pprof"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/spf13/cast"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
"wireguard-ui/config"
|
tui "wireguard-ui/cli"
|
||||||
"wireguard-ui/http/router"
|
"wireguard-ui/cron"
|
||||||
|
"wireguard-ui/http"
|
||||||
"wireguard-ui/initialize"
|
"wireguard-ui/initialize"
|
||||||
"wireguard-ui/script"
|
"wireguard-ui/script"
|
||||||
)
|
)
|
||||||
@ -20,23 +19,38 @@ 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()
|
|
||||||
|
|
||||||
if cast.ToBool(os.Getenv("ENABLED_PPROF")) {
|
app := &cli.App{
|
||||||
pprof.Register(handler, "/monitoring")
|
Name: "wireguard-ui",
|
||||||
|
Usage: "wireguard-manager-ui",
|
||||||
}
|
}
|
||||||
|
|
||||||
httpServe := http.Server{
|
app.Commands = []*cli.Command{
|
||||||
Addr: fmt.Sprintf(":%d", config.Config.Http.Port),
|
{
|
||||||
Handler: handler,
|
Name: "http:serve",
|
||||||
|
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()
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := httpServe.ListenAndServe(); err != nil {
|
sort.Sort(cli.CommandsByName(app.Commands))
|
||||||
log.Panicf("启动http服务端失败: %v", err.Error())
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
log.Fatalf("服务启动失败: %v", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
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"
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -86,7 +86,7 @@ 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").Where("code not in ?", blackList).Find(&data).Error
|
err = s.Model(&model.Setting{}).Select("code, data, describe,created_at,updated_at").Where("code not in ?", blackList).Find(&data).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +152,13 @@ func (s setting) Export() (data vo.Export, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询其他配置
|
||||||
|
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)
|
cj, _ := json.Marshal(clients)
|
||||||
_ = json.Unmarshal(cj, &data.Clients)
|
_ = json.Unmarshal(cj, &data.Clients)
|
||||||
|
|
||||||
@ -219,5 +226,17 @@ func (s setting) Import(data *vo.Export, loginUser *vo.User) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 其他配置写入
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
65
utils/wechat_notify.go
Normal file
65
utils/wechat_notify.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gitee.ltd/lxh/logger/log"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"strings"
|
||||||
|
"wireguard-ui/global/client"
|
||||||
|
"wireguard-ui/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// wxid_472vas3av5ug22 /api/sendTextMsg
|
||||||
|
type wechatNotify struct {
|
||||||
|
Addr string
|
||||||
|
Path string
|
||||||
|
Method string
|
||||||
|
toUserId string
|
||||||
|
}
|
||||||
|
|
||||||
|
func WechatNotify(setting *model.Setting) wechatNotify {
|
||||||
|
var sm = make(map[string]string)
|
||||||
|
_ = json.Unmarshal([]byte(setting.Data), &sm)
|
||||||
|
|
||||||
|
return wechatNotify{
|
||||||
|
Addr: sm["addr"],
|
||||||
|
Path: sm["path"],
|
||||||
|
Method: sm["method"],
|
||||||
|
toUserId: sm["toUserWxId"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendTextMessage
|
||||||
|
// @description: 发送文字通知
|
||||||
|
// @receiver website
|
||||||
|
// @param msg
|
||||||
|
// @return error
|
||||||
|
func (w wechatNotify) SendTextMessage(msg string) error {
|
||||||
|
log.Debugf("发送通知到微信: %v", msg)
|
||||||
|
req := client.HttpClient.R()
|
||||||
|
|
||||||
|
req.SetHeader("Content-Type", "application/json")
|
||||||
|
req.SetBody(map[string]string{
|
||||||
|
"wxid": w.toUserId,
|
||||||
|
"msg": msg,
|
||||||
|
})
|
||||||
|
|
||||||
|
req.URL = fmt.Sprintf("%s:%s", w.Addr, w.Path)
|
||||||
|
switch strings.ToUpper(w.Method) {
|
||||||
|
case "POST":
|
||||||
|
req.Method = "POST"
|
||||||
|
}
|
||||||
|
result, err := req.SetDebug(true).Send()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsoniter.Get(result.Body(), "code").ToInt() != 1 {
|
||||||
|
log.Errorf("发送通知到微信失败: %v", jsoniter.Get(result.Body(), "msg").ToString())
|
||||||
|
return errors.New("发送通知到微信失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -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,12 +228,18 @@
|
|||||||
<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>
|
||||||
@ -319,7 +325,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>
|
||||||
@ -440,7 +446,9 @@ const editModalForm = ref({
|
|||||||
presharedKey: ''
|
presharedKey: ''
|
||||||
},
|
},
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
offlineMonitoring: 1
|
offlineMonitoring: 1,
|
||||||
|
createdAt: '',
|
||||||
|
updatedAt: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
// 添加模态框的数据集
|
// 添加模态框的数据集
|
||||||
@ -576,6 +584,8 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更改客户端信息
|
// 更改客户端信息
|
||||||
|
Loading…
Reference in New Issue
Block a user