diff --git a/.gitignore b/.gitignore index abeb34f..07a9688 100644 --- a/.gitignore +++ b/.gitignore @@ -101,6 +101,165 @@ fabric.properties go.work .idea +web/.idea + +web/node_modules +web/.DS_Store +web/dist +web/dist-ssr +web/*.local +web/.eslintcache +web/report.html +web/vite.config.*.timestamp* + +web/yarn.lock +web/npm-debug.log* +web/.pnpm-error.log* +web/.pnpm-debug.log +web/tests/**/coverage/ +web/.vscode/ + +# Editor directories and files +web/*.suo +web/*.ntvs* +web/*.njsproj +web/*.sln +web/tsconfig.tsbuildinfo + +template/tmp/* +logs/* +app.yaml +*.db +.env +*.env + +### GoLand+all template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Go template +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +.idea +web/.idea + +web/node_modules +web/.DS_Store +web/dist +web/dist-ssr +web/*.local +web/.eslintcache +web/report.html +web/vite.config.*.timestamp* + +web/yarn.lock +web/npm-debug.log* +web/.pnpm-error.log* +web/.pnpm-debug.log +web/tests/**/coverage/ +web/.vscode/ + +# Editor directories and files +web/*.suo +web/*.ntvs* +web/*.njsproj +web/*.sln +web/tsconfig.tsbuildinfo template/tmp/* logs/* diff --git a/command/wireguard.go b/command/wireguard.go index d721de3..a0b343b 100644 --- a/command/wireguard.go +++ b/command/wireguard.go @@ -3,15 +3,22 @@ package command import ( "fmt" "gitee.ltd/lxh/logger/log" + "os" "os/exec" "strings" "wireguard-ui/service" ) +// 分隔符 // getConfigFileName // @description: 获取服务端配置文件名称 // @return string -func getConfigFileName() string { +func getConfigFileName(filePath string) string { + if filePath != "" { + filePath = strings.Split(filePath, string(os.PathSeparator))[len(strings.Split(filePath, string(os.PathSeparator)))-1] // 这里取到的是wg0.conf + filePath = strings.Split(filePath, ".conf")[0] // 这里取到的就是wg0 + return filePath + } data, err := service.Setting().GetWGSetForConfig() if err != nil { log.Errorf("获取服务端配置失败: %v", err.Error()) @@ -29,23 +36,23 @@ func getConfigFileName() string { // RestartWireguard // @description: 是否重启 // @param isAsync // 是否异步执行 -func RestartWireguard(isAsync bool) { +func RestartWireguard(isAsync bool, filePath string) { if isAsync { go func() { - StopWireguard() // 停止 - StartWireguard() // 启动 + StopWireguard(filePath) // 停止 + StartWireguard(filePath) // 启动 }() } else { - StopWireguard() // 停止 - StartWireguard() // 启动 + StopWireguard(filePath) // 停止 + StartWireguard(filePath) // 启动 } return } // StopWireguard // @description: 停止服务端 -func StopWireguard() { - configFileName := getConfigFileName() +func StopWireguard(filePath string) { + configFileName := getConfigFileName(filePath) cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("wg-quick down %s", configFileName)) if err := cmd.Run(); err == nil { @@ -57,12 +64,13 @@ func StopWireguard() { // StartWireguard // @description: 启动服务端 -func StartWireguard() { - configFileName := getConfigFileName() +func StartWireguard(filePath string) { + configFileName := getConfigFileName(filePath) cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("wg-quick up %s", configFileName)) if err := cmd.Run(); err == nil { log.Infof("启动wireguard[%s]服务端成功", configFileName) } + return } diff --git a/component/wireguard.go b/component/wireguard.go index dc8c971..cefd323 100644 --- a/component/wireguard.go +++ b/component/wireguard.go @@ -14,6 +14,7 @@ import ( "wireguard-ui/config" "wireguard-ui/global/client" "wireguard-ui/model" + "wireguard-ui/service" "wireguard-ui/template/render_data" ) @@ -113,20 +114,22 @@ func (w WireguardComponent) GenerateClientFile(clientInfo *model.Client, server // @receiver w // @return error func (w WireguardComponent) ServerControl(filePath string) { - switch config.Config.Wireguard.RestartMode { - case "NOW": // 立即执行 - w.watchConfigFile(filePath) - case "DELAY": // 延迟执行 - time.Sleep(time.Duration(config.Config.Wireguard.DelayTime) * time.Second) - w.watchConfigFile(filePath) + if filePath == "" { + data, err := service.Setting().GetWGSetForConfig() + if err != nil { + log.Errorf("获取服务端配置失败: %v", err.Error()) + return + } + filePath = data.ConfigFilePath } + w.watchConfigFile(filePath, config.Config.Wireguard.RestartMode, config.Config.Wireguard.DelayTime) } // watchConfigFile // @description: 监听并重新操作配置文件 // @receiver w // @param filepath -func (w WireguardComponent) watchConfigFile(filepath string) { +func (w WireguardComponent) watchConfigFile(filepath string, mode string, delay int64) { go func() { watcher, err := fsnotify.NewWatcher() if err != nil { @@ -146,7 +149,14 @@ func (w WireguardComponent) watchConfigFile(filepath string) { } if event.Op == fsnotify.Write { - command.RestartWireguard(true) + switch mode { + case "NOW": + command.RestartWireguard(false, filepath) + case "DELAY": + time.Sleep(time.Duration(delay) * time.Second) + command.RestartWireguard(true, filepath) + } + } // 打印监听事件 diff --git a/http/api/client.go b/http/api/client.go index 05493a8..89ea579 100644 --- a/http/api/client.go +++ b/http/api/client.go @@ -15,6 +15,7 @@ import ( "wireguard-ui/http/response" "wireguard-ui/http/vo" "wireguard-ui/model" + "wireguard-ui/script" "wireguard-ui/service" "wireguard-ui/utils" ) @@ -44,7 +45,12 @@ func (ClientApi) Save(c *gin.Context) { return } - component.Wireguard().ServerControl("E:\\Workspace\\Go\\wireguard-ui\\template\\tmp\\wg0.conf") + go func() { + if err := script.New().GenerateConfig(); err != nil { + log.Errorf("执行脚本失败") + } + }() + response.R(c).OK() } @@ -64,7 +70,12 @@ func (ClientApi) Delete(c *gin.Context) { return } - component.Wireguard().ServerControl("E:\\Workspace\\Go\\wireguard-ui\\template\\tmp\\wg0.conf") + go func() { + if err := script.New().GenerateConfig(); err != nil { + log.Errorf("执行脚本失败") + } + }() + response.R(c).OK() } diff --git a/http/api/setting.go b/http/api/setting.go index 4f3e755..67b5646 100644 --- a/http/api/setting.go +++ b/http/api/setting.go @@ -1,10 +1,13 @@ package api import ( + "gitee.ltd/lxh/logger/log" "github.com/gin-gonic/gin" + "slices" "wireguard-ui/http/param" "wireguard-ui/http/response" "wireguard-ui/model" + "wireguard-ui/script" "wireguard-ui/service" "wireguard-ui/utils" ) @@ -35,6 +38,15 @@ func (setting) Set(c *gin.Context) { return } + var whiteCodes = []string{"WG_SETTING", "WG_SERVER"} + if slices.Contains(whiteCodes, p.Code) { + go func() { + if err := script.New().GenerateConfig(); err != nil { + log.Errorf("执行脚本失败") + } + }() + } + response.R(c).OK() } diff --git a/script/conf_script.go b/script/conf_script.go new file mode 100644 index 0000000..5151f2c --- /dev/null +++ b/script/conf_script.go @@ -0,0 +1,81 @@ +package script + +import ( + "github.com/spf13/cast" + "os" + "time" + "wireguard-ui/component" + "wireguard-ui/http/param" + "wireguard-ui/service" + "wireguard-ui/template/render_data" +) + +// GenerateConfig +// @description: 生成配置文件 +// @receiver script +// @return error +func (script) GenerateConfig() error { + // 获取服务端相关配置 + serverEnt, err := service.Setting().GetWGServerForConfig() + if err != nil { + return err + } + + global, err := service.Setting().GetWGSetForConfig() + if err != nil { + return err + } + + // 获取客户端 + clientsEnt, _, err := service.Client().List(param.ClientList{ + Page: param.Page{ + Current: -1, + Size: -1, + }, + }) + if err != nil { + return err + } + + var clients []render_data.Client + for _, client := range clientsEnt { + clients = append(clients, render_data.Client{ + ID: client.Id, + Name: client.Name, + Email: client.Email, + PublicKey: client.Keys.PublicKey, + PresharedKey: client.Keys.PresharedKey, + AllowedIPS: client.AllowedIpsStr, + Endpoint: client.Endpoint, + CreateUser: client.CreateUser, + Enabled: cast.ToBool(client.Enabled), + PersistentKeepalive: global.PersistentKeepalive, + CreatedAt: client.CreatedAt.Format("2006-01-02 15:04:05"), + UpdatedAt: client.UpdatedAt.Format("2006-01-02 15:04:05"), + SyncAt: time.Now().Format("2006-01-02 15:04:05"), + }) + } + + execData := map[string]any{ + "Server": serverEnt, + "Clients": clients, + } + + var templatePath, outFilePath string + if os.Getenv("GIN_MODE") != "release" { + templatePath = "E:\\Workspace\\Go\\wireguard-ui\\template\\conf\\wg.conf" + outFilePath = "E:\\Workspace\\Go\\wireguard-ui\\template\\tmp\\wg0.conf" + } else { + templatePath = "./template/wg.conf" + outFilePath = cast.ToString(global.ConfigFilePath) + } + + // 先渲染模板 + if err = component.Template().Execute(templatePath, outFilePath, execData); err != nil { + return nil + } + + // 模板渲染成功,开始执行服务端控制 + component.Wireguard().ServerControl(outFilePath) + return nil +} diff --git a/template/conf/wg.conf b/template/conf/wg.conf index 88c1ad3..d4f515a 100644 --- a/template/conf/wg.conf +++ b/template/conf/wg.conf @@ -15,6 +15,7 @@ Table = {{ .Server.Table|html }} # CreatedAt: {{ .CreatedAt|html }} # UpdatedAt: {{ .UpdatedAt|html }} # CreateUser: {{ .CreateUser|html }} +# SyncAt: {{ .SyncAt|html }} [Peer] PublicKey = {{ .PublicKey|html }} PresharedKey = {{ .PresharedKey|html }} diff --git a/template/render_data/render_data.go b/template/render_data/render_data.go index d2f68bf..16f6fd9 100644 --- a/template/render_data/render_data.go +++ b/template/render_data/render_data.go @@ -30,12 +30,13 @@ type Client struct { PublicKey string `json:"publicKey"` PresharedKey string `json:"presharedKey"` AllowedIPS string `json:"allowedIps"` - PersistentKeepalive string `json:"persistentKeepalive"` + PersistentKeepalive int `json:"persistentKeepalive"` Endpoint string `json:"endpoint"` CreateUser string `json:"createUser"` Enabled bool `json:"enabled"` CreatedAt string `json:"createdAt"` UpdatedAt string `json:"updatedAt"` + SyncAt string `json:"syncAt"` } type Keys struct {