diff --git a/README.md b/README.md index 0da4652..eafa86b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + # wireguard-ui @@ -53,7 +53,11 @@ docker-compose up | `WGUI_TABLE` | The default WireGuard table value settings | `auto` | | `WGUI_CONFIG_FILE_PATH` | The default WireGuard config file path used in global settings | `/etc/wireguard/wg0.conf` | | `WGUI_LOG_LEVEL` | The default log level. Possible values: `DEBUG`, `INFO`, `WARN`, `ERROR`, `OFF` | `INFO` | -| `WG_CONF_TEMPLATE` | The custom `wg.conf` config file template. Please refer to our [default template](https://github.com/ngoduykhanh/wireguard-ui/blob/master/templates/wg.conf) | N/A | +| `WGUI_BRAND_TEXT` | The brand text of the web application | `WireGuard UI` | +| `WGUI_ACCENT_COLOR` | The color of the interface sidebar | `#343a40` | +| `WGUI_LOGO_FILE_PATH` | The file path of the website logo | Embedded WireGuard logo | +| `WGUI_PAGE_TITLE_PREFIX` | The HTML title prefix for all pages | N/A | +| `WG_CONF_TEMPLATE` | The custom `wg.conf` config file template. Please refer to our [default template](https://github.com/idressos/wireguard-ui/blob/master/templates/wg.conf) | N/A | | `EMAIL_FROM_ADDRESS` | The sender email address | N/A | | `EMAIL_FROM_NAME` | The sender name | `WireGuard UI` | | `SENDGRID_API_KEY` | The SendGrid api key | N/A | @@ -63,6 +67,7 @@ docker-compose up | `SMTP_PASSWORD` | The SMTP user password | N/A | | `SMTP_AUTH_TYPE` | The SMTP authentication type. Possible values: `PLAIN`, `LOGIN`, `NONE` | `NONE` | | `SMTP_ENCRYPTION` | the encryption method. Possible values: `NONE`, `SSL`, `SSLTLS`, `TLS`, `STARTTLS` | `STARTTLS` | +| `HELLO_HOSTNAME` | Hostname to use for the hello message. smtp-relay.gmail.com needs this set to anything but `localhost` | `localhost` | ### Defaults for server configuration @@ -181,9 +186,9 @@ rc-update add wgui default ### Using Docker -Set `WGUI_MANAGE_RESTART=true` to manage Wireguard interface restarts. -Using `WGUI_MANAGE_START=true` can also replace the function of `wg-quick@wg0` service, to start Wireguard at boot, by -running the container with `restart: unless-stopped`. These settings can also pick up changes to Wireguard Config File +Set `WGUI_MANAGE_RESTART=true` to manage WireGuard interface restarts. +Using `WGUI_MANAGE_START=true` can also replace the function of `wg-quick@wg0` service, to start WireGuard at boot, by +running the container with `restart: unless-stopped`. These settings can also pick up changes to WireGuard Config File Path, after restarting the container. Please make sure you have `--cap-add=NET_ADMIN` in your container config to make this feature work. @@ -224,7 +229,7 @@ go build -o wireguard-ui ## License -MIT. See [LICENSE](https://github.com/ngoduykhanh/wireguard-ui/blob/master/LICENSE). +MIT. See [LICENSE](https://github.com/idressos/wireguard-ui/blob/master/LICENSE). ## Support diff --git a/custom/img/logo.png b/custom/img/logo.png new file mode 100644 index 0000000..5425633 Binary files /dev/null and b/custom/img/logo.png differ diff --git a/emailer/smtp.go b/emailer/smtp.go index f77db7c..7bc22fe 100644 --- a/emailer/smtp.go +++ b/emailer/smtp.go @@ -13,6 +13,7 @@ type SmtpMail struct { port int username string password string + helloHostName string authType mail.AuthType encryption mail.Encryption noTLSCheck bool @@ -46,8 +47,8 @@ func encryptionType(encryptionType string) mail.Encryption { } } -func NewSmtpMail(hostname string, port int, username string, password string, noTLSCheck bool, auth string, fromName, from string, encryption string) *SmtpMail { - ans := SmtpMail{hostname: hostname, port: port, username: username, password: password, noTLSCheck: noTLSCheck, fromName: fromName, from: from, authType: authType(auth), encryption: encryptionType(encryption)} +func NewSmtpMail(hostname string, port int, username string, password string, helloHostName string, noTLSCheck bool, auth string, fromName, from string, encryption string) *SmtpMail { + ans := SmtpMail{hostname: hostname, port: port, username: username, password: password, helloHostName: helloHostName, noTLSCheck: noTLSCheck, fromName: fromName, from: from, authType: authType(auth), encryption: encryptionType(encryption)} return &ans } @@ -66,6 +67,7 @@ func (o *SmtpMail) Send(toName string, to string, subject string, content string server.Authentication = o.authType server.Username = o.username server.Password = o.password + server.Helo = o.helloHostName server.Encryption = o.encryption server.KeepAlive = false server.ConnectTimeout = 10 * time.Second diff --git a/examples/docker-compose/README.md b/examples/docker-compose/README.md index 951df08..4b645a3 100644 --- a/examples/docker-compose/README.md +++ b/examples/docker-compose/README.md @@ -2,7 +2,7 @@ ### Kernel Module -Depending on if the Wireguard kernel module is available on your system you have more or less choices which example to use. +Depending on if the WireGuard kernel module is available on your system you have more or less choices which example to use. You can check if the kernel modules are available via the following command: ```shell @@ -21,10 +21,10 @@ For security reasons it's highly recommended to change them before the first sta ## Examples - **[system](system.yml)** - If you have Wireguard already installed on your system and only want to run the UI in docker this might fit the most. + If you have WireGuard already installed on your system and only want to run the UI in docker this might fit the most. - **[linuxserver](linuxserver.yml)** - If you have the Wireguard kernel modules installed (included in the mainline kernel since version 5.6) but want it running inside of docker, this might fit the most. + If you have the WireGuard kernel modules installed (included in the mainline kernel since version 5.6) but want it running inside of docker, this might fit the most. - **[boringtun](boringtun.yml)** - If Wireguard kernel modules are not available, you can switch to an userspace implementation like [boringtun](https://github.com/cloudflare/boringtun). + If WireGuard kernel modules are not available, you can switch to an userspace implementation like [boringtun](https://github.com/cloudflare/boringtun). diff --git a/handler/routes.go b/handler/routes.go index a2ceeaf..53cd8d7 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -2,6 +2,7 @@ package handler import ( "crypto/subtle" + "path/filepath" "encoding/base64" "encoding/json" "fmt" @@ -42,6 +43,15 @@ func Favicon() echo.HandlerFunc { } } +func Logo() echo.HandlerFunc { + return func(c echo.Context) error { + if logo, ok := os.LookupEnv(util.LogoFilePathEnvVar); ok { + return c.File(logo) + } + return c.Redirect(http.StatusFound, util.BasePath+"/static/custom/img/logo.png") + } +} + // LoginPage handler func LoginPage() echo.HandlerFunc { return func(c echo.Context) error { @@ -337,7 +347,7 @@ func WireGuardClients(db store.IStore) echo.HandlerFunc { } } -// GetClients handler return a JSON list of Wireguard client data +// GetClients handler return a JSON list of WireGuard client data func GetClients(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { @@ -352,7 +362,7 @@ func GetClients(db store.IStore) echo.HandlerFunc { } } -// GetClient handler returns a JSON object of Wireguard client data +// GetClient handler returns a JSON object of WireGuard client data func GetClient(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { @@ -409,12 +419,12 @@ func NewClient(db store.IStore) echo.HandlerFunc { guid := xid.New() client.ID = guid.String() - // gen Wireguard key pair + // gen WireGuard key pair if client.PublicKey == "" { key, err := wgtypes.GeneratePrivateKey() if err != nil { log.Error("Cannot generate wireguard key pair: ", err) - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"}) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate WireGuard key pair"}) } client.PrivateKey = key.String() client.PublicKey = key.PublicKey().String() @@ -422,7 +432,7 @@ func NewClient(db store.IStore) echo.HandlerFunc { _, err := wgtypes.ParseKey(client.PublicKey) if err != nil { log.Error("Cannot verify wireguard public key: ", err) - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify Wireguard public key"}) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify WireGuard public key"}) } // check for duplicates clients, err := db.GetClients(false) @@ -444,7 +454,7 @@ func NewClient(db store.IStore) echo.HandlerFunc { if err != nil { log.Error("Cannot generated preshared key: ", err) return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{ - false, "Cannot generate Wireguard preshared key", + false, "Cannot generate WireGuard preshared key", }) } client.PresharedKey = presharedKey.String() @@ -455,7 +465,7 @@ func NewClient(db store.IStore) echo.HandlerFunc { _, err := wgtypes.ParseKey(client.PresharedKey) if err != nil { log.Error("Cannot verify wireguard preshared key: ", err) - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify Wireguard preshared key"}) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify WireGuard preshared key"}) } } client.CreatedAt = time.Now().UTC() @@ -567,12 +577,12 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Extra Allowed IPs must be in CIDR format"}) } - // update Wireguard Client PublicKey + // update WireGuard Client PublicKey if client.PublicKey != _client.PublicKey && _client.PublicKey != "" { _, err := wgtypes.ParseKey(_client.PublicKey) if err != nil { - log.Error("Cannot verify provided Wireguard public key: ", err) - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify provided Wireguard public key"}) + log.Error("Cannot verify provided WireGuard public key: ", err) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify provided WireGuard public key"}) } // check for duplicates clients, err := db.GetClients(false) @@ -587,7 +597,7 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { } } - // When replacing any PublicKey, discard any locally stored Wireguard Client PrivateKey + // When replacing any PublicKey, discard any locally stored WireGuard Client PrivateKey // Client PubKey no longer corresponds to locally stored PrivKey. // QR code (needs PrivateKey) for this client is no longer possible now. @@ -597,12 +607,12 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { } - // update Wireguard Client PresharedKey + // update WireGuard Client PresharedKey if client.PresharedKey != _client.PresharedKey && _client.PresharedKey != "" { _, err := wgtypes.ParseKey(_client.PresharedKey) if err != nil { - log.Error("Cannot verify provided Wireguard preshared key: ", err) - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify provided Wireguard preshared key"}) + log.Error("Cannot verify provided WireGuard preshared key: ", err) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot verify provided WireGuard preshared key"}) } } @@ -614,6 +624,7 @@ func UpdateClient(db store.IStore) echo.HandlerFunc { client.AllocatedIPs = _client.AllocatedIPs client.AllowedIPs = _client.AllowedIPs client.ExtraAllowedIPs = _client.ExtraAllowedIPs + client.Endpoint = _client.Endpoint client.PublicKey = _client.PublicKey client.PresharedKey = _client.PresharedKey client.UpdatedAt = time.Now().UTC() @@ -689,7 +700,7 @@ func DownloadClient(db store.IStore) echo.HandlerFunc { // set response header for downloading c.Response().Header().Set(echo.HeaderContentDisposition, fmt.Sprintf("attachment; filename=%s.conf", clientData.Client.Name)) - return c.Stream(http.StatusOK, "text/plain", reader) + return c.Stream(http.StatusOK, "text/conf", reader) } } @@ -759,11 +770,11 @@ func WireGuardServerInterfaces(db store.IStore) echo.HandlerFunc { func WireGuardServerKeyPair(db store.IStore) echo.HandlerFunc { return func(c echo.Context) error { - // gen Wireguard key pair + // gen WireGuard key pair key, err := wgtypes.GeneratePrivateKey() if err != nil { log.Error("Cannot generate wireguard key pair: ", err) - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"}) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate WireGuard key pair"}) } var serverKeyPair model.ServerKeypair @@ -772,7 +783,7 @@ func WireGuardServerKeyPair(db store.IStore) echo.HandlerFunc { serverKeyPair.UpdatedAt = time.Now().UTC() if err := db.SaveServerKeyPair(serverKeyPair); err != nil { - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"}) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate WireGuard key pair"}) } log.Infof("Updated wireguard server interfaces settings: %v", serverKeyPair) @@ -796,6 +807,16 @@ func GlobalSettings(db store.IStore) echo.HandlerFunc { } } +func extractDeviceNameFromConfigPath(db store.IStore) string { + settings, err := db.GetGlobalSettings() + if err != nil { + log.Error("Cannot get global settings: ", err) + } + + base := filepath.Base(settings.ConfigFilePath) + return strings.TrimSuffix(base, filepath.Ext(base)) +} + // Status handler func Status(db store.IStore) echo.HandlerFunc { type PeerVM struct { @@ -826,6 +847,8 @@ func Status(db store.IStore) echo.HandlerFunc { }) } + deviceName := extractDeviceNameFromConfigPath(db) + devices, err := wgClient.Devices() if err != nil { return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{ @@ -854,35 +877,37 @@ func Status(db store.IStore) echo.HandlerFunc { conv := map[bool]int{true: 1, false: 0} for i := range devices { - devVm := DeviceVM{Name: devices[i].Name} - for j := range devices[i].Peers { - var allocatedIPs string - for _, ip := range devices[i].Peers[j].AllowedIPs { - if len(allocatedIPs) > 0 { - allocatedIPs += "" + if devices[i].Name == deviceName { + devVm := DeviceVM{Name: devices[i].Name} + for j := range devices[i].Peers { + var allocatedIPs string + for _, ip := range devices[i].Peers[j].AllowedIPs { + if len(allocatedIPs) > 0 { + allocatedIPs += "" + } + allocatedIPs += ip.String() } - allocatedIPs += ip.String() - } - pVm := PeerVM{ - PublicKey: devices[i].Peers[j].PublicKey.String(), - ReceivedBytes: devices[i].Peers[j].ReceiveBytes, - TransmitBytes: devices[i].Peers[j].TransmitBytes, - LastHandshakeTime: devices[i].Peers[j].LastHandshakeTime, - LastHandshakeRel: time.Since(devices[i].Peers[j].LastHandshakeTime), - AllocatedIP: allocatedIPs, - Endpoint: devices[i].Peers[j].Endpoint.String(), - } - pVm.Connected = pVm.LastHandshakeRel.Minutes() < 3. + pVm := PeerVM{ + PublicKey: devices[i].Peers[j].PublicKey.String(), + ReceivedBytes: devices[i].Peers[j].ReceiveBytes, + TransmitBytes: devices[i].Peers[j].TransmitBytes, + LastHandshakeTime: devices[i].Peers[j].LastHandshakeTime, + LastHandshakeRel: time.Since(devices[i].Peers[j].LastHandshakeTime), + AllocatedIP: allocatedIPs, + Endpoint: devices[i].Peers[j].Endpoint.String(), + } + pVm.Connected = pVm.LastHandshakeRel.Minutes() < 3. - if _client, ok := m[pVm.PublicKey]; ok { - pVm.Name = _client.Name - pVm.Email = _client.Email + if _client, ok := m[pVm.PublicKey]; ok { + pVm.Name = _client.Name + pVm.Email = _client.Email + } + devVm.Peers = append(devVm.Peers, pVm) } - devVm.Peers = append(devVm.Peers, pVm) + sort.SliceStable(devVm.Peers, func(i, j int) bool { return devVm.Peers[i].Name < devVm.Peers[j].Name }) + sort.SliceStable(devVm.Peers, func(i, j int) bool { return conv[devVm.Peers[i].Connected] > conv[devVm.Peers[j].Connected] }) + devicesVm = append(devicesVm, devVm) } - sort.SliceStable(devVm.Peers, func(i, j int) bool { return devVm.Peers[i].Name < devVm.Peers[j].Name }) - sort.SliceStable(devVm.Peers, func(i, j int) bool { return conv[devVm.Peers[i].Connected] > conv[devVm.Peers[j].Connected] }) - devicesVm = append(devicesVm, devVm) } } @@ -911,7 +936,7 @@ func GlobalSettingSubmit(db store.IStore) echo.HandlerFunc { // write config to the database if err := db.SaveGlobalSettings(globalSettings); err != nil { - return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"}) + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate WireGuard key pair"}) } log.Infof("Updated global settings: %v", globalSettings) @@ -985,7 +1010,7 @@ func SuggestIPAllocation(db store.IStore) echo.HandlerFunc { } } -// ApplyServerConfig handler to write config file and restart Wireguard server +// ApplyServerConfig handler to write config file and restart WireGuard server func ApplyServerConfig(db store.IStore, tmplDir fs.FS) echo.HandlerFunc { return func(c echo.Context) error { diff --git a/main.go b/main.go index 0131208..a94bb84 100644 --- a/main.go +++ b/main.go @@ -30,6 +30,7 @@ var ( flagBindAddress string = "0.0.0.0:5000" flagSmtpHostname string = "127.0.0.1" flagSmtpPort int = 25 + flagHelloHostName string = "localhost" flagSmtpUsername string flagSmtpPassword string flagSmtpAuthType string = "NONE" @@ -41,14 +42,18 @@ var ( flagSessionSecret string = util.RandomString(32) flagWgConfTemplate string flagBasePath string + flagBrandText string = "WireGuard UI" + flagAccentColor string = "#343a40" + flagPageTitlePrefix string ) const ( - defaultEmailSubject = "Your wireguard configuration" - defaultEmailContent = `Hi, -
In this email you can find your personal configuration for our wireguard server.
+ defaultEmailSubject = "Your VPN configuration" -Best
+ defaultEmailContent = ` +Greetings,
+Please find attached your personal configuration for our VPN server.
You may find instructions on how to install the WireGuard VPN client here.
Best regards.
` ) @@ -69,6 +74,7 @@ func init() { flag.StringVar(&flagBindAddress, "bind-address", util.LookupEnvOrString("BIND_ADDRESS", flagBindAddress), "Address:Port to which the app will be bound.") flag.StringVar(&flagSmtpHostname, "smtp-hostname", util.LookupEnvOrString("SMTP_HOSTNAME", flagSmtpHostname), "SMTP Hostname") flag.IntVar(&flagSmtpPort, "smtp-port", util.LookupEnvOrInt("SMTP_PORT", flagSmtpPort), "SMTP Port") + flag.StringVar(&flagHelloHostName, "hello-hostname", util.LookupEnvOrString("HELLO_HOSTNAME", flagHelloHostName), "Hello HostName") flag.StringVar(&flagSmtpUsername, "smtp-username", util.LookupEnvOrString("SMTP_USERNAME", flagSmtpUsername), "SMTP Username") flag.StringVar(&flagSmtpPassword, "smtp-password", util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword), "SMTP Password") flag.BoolVar(&flagSmtpNoTLSCheck, "smtp-no-tls-check", util.LookupEnvOrBool("SMTP_NO_TLS_CHECK", flagSmtpNoTLSCheck), "Disable TLS verification for SMTP. This is potentially dangerous.") @@ -80,6 +86,10 @@ func init() { flag.StringVar(&flagSessionSecret, "session-secret", util.LookupEnvOrString("SESSION_SECRET", flagSessionSecret), "The key used to encrypt session cookies.") flag.StringVar(&flagWgConfTemplate, "wg-conf-template", util.LookupEnvOrString("WG_CONF_TEMPLATE", flagWgConfTemplate), "Path to custom wg.conf template.") flag.StringVar(&flagBasePath, "base-path", util.LookupEnvOrString("BASE_PATH", flagBasePath), "The base path of the URL") + flag.StringVar(&flagBrandText, "brand-text", util.LookupEnvOrString("WGUI_BRAND_TEXT", flagBrandText), "The UI brand text or name") + flag.StringVar(&flagAccentColor, "accent-color", util.LookupEnvOrString("WGUI_ACCENT_COLOR", flagAccentColor), "The UI accent color") + flag.StringVar(&flagPageTitlePrefix, "page-title-prefix", util.LookupEnvOrString("WGUI_PAGE_TITLE_PREFIX", flagPageTitlePrefix), "The prefix of the page title") + flag.Parse() // update runtime config @@ -87,6 +97,7 @@ func init() { util.BindAddress = flagBindAddress util.SmtpHostname = flagSmtpHostname util.SmtpPort = flagSmtpPort + util.HelloHostName = flagHelloHostName util.SmtpUsername = flagSmtpUsername util.SmtpPassword = flagSmtpPassword util.SmtpAuthType = flagSmtpAuthType @@ -98,16 +109,19 @@ func init() { util.SessionSecret = []byte(flagSessionSecret) util.WgConfTemplate = flagWgConfTemplate util.BasePath = util.ParseBasePath(flagBasePath) - + util.BrandText = flagBrandText + util.AccentColor = flagAccentColor + util.PageTitlePrefix = flagPageTitlePrefix + // print only if log level is INFO or lower if lvl, _ := util.ParseLogLevel(util.LookupEnvOrString(util.LogLevel, "INFO")); lvl <= log.INFO { // print app information - fmt.Println("Wireguard UI") + fmt.Println("WireGuard UI") fmt.Println("App Version\t:", appVersion) fmt.Println("Git Commit\t:", gitCommit) fmt.Println("Git Ref\t\t:", gitRef) fmt.Println("Build Time\t:", buildTime) - fmt.Println("Git Repo\t:", "https://github.com/ngoduykhanh/wireguard-ui") + fmt.Println("Git Repo\t:", "https://github.com/idressos/wireguard-ui") fmt.Println("Authentication\t:", !util.DisableLogin) fmt.Println("Bind address\t:", util.BindAddress) //fmt.Println("Sendgrid key\t:", util.SendgridApiKey) @@ -133,6 +147,9 @@ func main() { extraData["gitCommit"] = gitCommit extraData["basePath"] = util.BasePath extraData["loginDisabled"] = flagDisableLogin + extraData["brandText"] = flagBrandText; + extraData["accentColor"] = flagAccentColor; + extraData["pageTitlePrefix"] = flagPageTitlePrefix; // strip the "templates/" prefix from the embedded directory so files can be read by their direct name (e.g. // "base.html" instead of "templates/base.html") @@ -163,13 +180,14 @@ func main() { if util.SendgridApiKey != "" { sendmail = emailer.NewSendgridApiMail(util.SendgridApiKey, util.EmailFromName, util.EmailFrom) } else { - sendmail = emailer.NewSmtpMail(util.SmtpHostname, util.SmtpPort, util.SmtpUsername, util.SmtpPassword, util.SmtpNoTLSCheck, util.SmtpAuthType, util.EmailFromName, util.EmailFrom, util.SmtpEncryption) + sendmail = emailer.NewSmtpMail(util.SmtpHostname, util.SmtpPort, util.SmtpUsername, util.SmtpPassword, util.HelloHostName, util.SmtpNoTLSCheck, util.SmtpAuthType, util.EmailFromName, util.EmailFrom, util.SmtpEncryption) } app.GET(util.BasePath+"/test-hash", handler.GetHashesChanges(db), handler.ValidSession) app.GET(util.BasePath+"/about", handler.AboutPage()) app.GET(util.BasePath+"/_health", handler.Health()) app.GET(util.BasePath+"/favicon", handler.Favicon()) + app.GET(util.BasePath+"/logo", handler.Logo()) app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson) app.POST(util.BasePath+"/update-client", handler.UpdateClient(db), handler.ValidSession, handler.ContentTypeJson) app.POST(util.BasePath+"/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession, handler.ContentTypeJson) diff --git a/model/client.go b/model/client.go index be5fa76..95342f0 100644 --- a/model/client.go +++ b/model/client.go @@ -15,6 +15,7 @@ type Client struct { AllocatedIPs []string `json:"allocated_ips"` AllowedIPs []string `json:"allowed_ips"` ExtraAllowedIPs []string `json:"extra_allowed_ips"` + Endpoint string `json:"endpoint"` UseServerDNS bool `json:"use_server_dns"` Enabled bool `json:"enabled"` CreatedAt time.Time `json:"created_at"` diff --git a/package.json b/package.json index a0cda64..4644748 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "wireguard-ui", "version": "1.0.0", - "description": "Wireguard web interface", + "description": "WireGuard web interface", "main": "index.js", "repository": "git@github.com:ngoduykhanh/wireguard-ui.git", "author": "Khanh Ngo