From 5fec9d6a0282ec6a83771fcdb8e7bdaf110a528d Mon Sep 17 00:00:00 2001 From: Giorgos Komninos Date: Sun, 8 Aug 2021 11:34:47 +0300 Subject: [PATCH 01/11] Option to sent configuration by email --- custom/js/helper.js | 3 + docker-compose.yaml | 7 ++- emailer/interface.go | 10 +++ emailer/sendgrid.go | 54 ++++++++++++++++ go.mod | 2 + go.sum | 4 ++ handler/routes.go | 48 ++++++++++++++ main.go | 9 +++ templates/clients.html | 139 +++++++++++++++++++++++++++++++++++++---- util/config.go | 7 ++- 10 files changed, 269 insertions(+), 14 deletions(-) create mode 100644 emailer/interface.go create mode 100644 emailer/sendgrid.go diff --git a/custom/js/helper.js b/custom/js/helper.js index a624591b..feab9acc 100644 --- a/custom/js/helper.js +++ b/custom/js/helper.js @@ -29,6 +29,9 @@ function renderClientList(data) {
+ diff --git a/docker-compose.yaml b/docker-compose.yaml index 72da0966..dae14ce1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,8 +2,13 @@ version: '3' services: wg: - image: ngoduykhanh/wireguard-ui:latest + build: . + #image: ngoduykhanh/wireguard-ui:latest container_name: wgui + environment: + - SENDGRID_API_KEY + - EMAIL_FROM + - EMAIL_FROM_NAME ports: - 5000:5000 logging: diff --git a/emailer/interface.go b/emailer/interface.go new file mode 100644 index 00000000..5a486fc6 --- /dev/null +++ b/emailer/interface.go @@ -0,0 +1,10 @@ +package emailer + +type Attachment struct { + Name string + Data []byte +} + +type Emailer interface { + Send(toName string, to string, subject string, content string, attachments []Attachment) error +} diff --git a/emailer/sendgrid.go b/emailer/sendgrid.go new file mode 100644 index 00000000..864c9535 --- /dev/null +++ b/emailer/sendgrid.go @@ -0,0 +1,54 @@ +package emailer + +import ( + "encoding/base64" + + "github.com/sendgrid/sendgrid-go" + "github.com/sendgrid/sendgrid-go/helpers/mail" +) + +type SendgridApiMail struct { + apiKey string + fromName string + from string +} + +func NewSendgridApiMail(apiKey, fromName, from string) *SendgridApiMail { + ans := SendgridApiMail{apiKey: apiKey, fromName: fromName, from: from} + return &ans +} + +func (o *SendgridApiMail) Send(toName string, to string, subject string, content string, attachments []Attachment) error { + m := mail.NewV3Mail() + + mailFrom := mail.NewEmail(o.fromName, o.from) + mailContent := mail.NewContent("text/html", content) + mailTo := mail.NewEmail(toName, to) + + m.SetFrom(mailFrom) + m.AddContent(mailContent) + + personalization := mail.NewPersonalization() + personalization.AddTos(mailTo) + personalization.Subject = subject + + m.AddPersonalizations(personalization) + + toAdd := make([]*mail.Attachment, 0, len(attachments)) + for i := range attachments { + var att mail.Attachment + encoded := base64.StdEncoding.EncodeToString(attachments[i].Data) + att.SetContent(encoded) + att.SetType("text/plain") + att.SetFilename(attachments[i].Name) + att.SetDisposition("attachment") + toAdd = append(toAdd, &att) + } + + m.AddAttachment(toAdd...) + request := sendgrid.GetRequest(o.apiKey, "/v3/mail/send", "https://api.sendgrid.com") + request.Method = "POST" + request.Body = mail.GetRequestBody(m) + _, err := sendgrid.API(request) + return err +} diff --git a/go.mod b/go.mod index 1678c527..282cb028 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ require ( github.com/leodido/go-urn v1.2.0 // indirect github.com/rs/xid v1.2.1 github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba + github.com/sendgrid/rest v2.6.4+incompatible // indirect + github.com/sendgrid/sendgrid-go v3.10.0+incompatible github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf gopkg.in/go-playground/assert.v1 v1.2.1 // indirect diff --git a/go.sum b/go.sum index 9a0e4353..224f330f 100644 --- a/go.sum +++ b/go.sum @@ -103,6 +103,10 @@ github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba h1:8QAc9wFAf2b/9cAXskm0wBylObZ0bTpRcaP7ThjLPVQ= github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba/go.mod h1:W6zxGUBCXRR5QugSd/nFcFVmwoGnvpjiNY/JwT03Wew= +github.com/sendgrid/rest v2.6.4+incompatible h1:lq6gAQxLwVBf3mVyCCSHI6mgF+NfaJFJHjT0kl6SSo8= +github.com/sendgrid/rest v2.6.4+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= +github.com/sendgrid/sendgrid-go v3.10.0+incompatible h1:aSYyurHxEZSDy7kxhvZ4fH0inNkEEmRssZNbAmETR2c= +github.com/sendgrid/sendgrid-go v3.10.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 h1:RYiqpb2ii2Z6J4x0wxK46kvPBbFuZcdhS+CIztmYgZs= github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= diff --git a/handler/routes.go b/handler/routes.go index d3029e1b..f4f4b18b 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -1,6 +1,7 @@ package handler import ( + "encoding/base64" "encoding/json" "fmt" "net/http" @@ -13,6 +14,7 @@ import ( "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" "github.com/labstack/gommon/log" + "github.com/ngoduykhanh/wireguard-ui/emailer" "github.com/ngoduykhanh/wireguard-ui/model" "github.com/ngoduykhanh/wireguard-ui/util" "github.com/rs/xid" @@ -194,6 +196,52 @@ func NewClient() echo.HandlerFunc { } } +// EmailClient handler to sent the configuration via email +func EmailClient(mailer emailer.Emailer) echo.HandlerFunc { + type clientIdEmailPayload struct { + ID string `json:"id"` + Email string `json:"email"` + } + + return func(c echo.Context) error { + // access validation + validSession(c) + var payload clientIdEmailPayload + c.Bind(&payload) + // TODO validate email + + clientData, err := util.GetClientByID(payload.ID, true) + if err != nil { + log.Errorf("Cannot generate client id %s config file for downloading: %v", payload.ID, err) + return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"}) + } + + // build config + server, _ := util.GetServer() + globalSettings, _ := util.GetGlobalSettings() + config := util.BuildClientConfig(*clientData.Client, server, globalSettings) + + cfg_att := emailer.Attachment{"wg0.conf", []byte(config)} + qrdata, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(clientData.QRCode, "data:image/png;base64,")) + if err != nil { + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "decoding: " + err.Error()}) + } + qr_att := emailer.Attachment{"wg.png", qrdata} + err = mailer.Send( + clientData.Client.Name, + payload.Email, + "Your Wireguard configuration", + "instructions here", + []emailer.Attachment{cfg_att, qr_att}, + ) + if err != nil { + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()}) + } + + return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Email sent successfully"}) + } +} + // UpdateClient handler to update client information func UpdateClient() echo.HandlerFunc { return func(c echo.Context) error { diff --git a/main.go b/main.go index 05532720..e7a1ab8b 100644 --- a/main.go +++ b/main.go @@ -4,10 +4,13 @@ import ( "flag" "fmt" "net/http" + "os" "time" rice "github.com/GeertJohan/go.rice" "github.com/labstack/echo/v4" + + "github.com/ngoduykhanh/wireguard-ui/emailer" "github.com/ngoduykhanh/wireguard-ui/handler" "github.com/ngoduykhanh/wireguard-ui/router" "github.com/ngoduykhanh/wireguard-ui/util" @@ -30,6 +33,9 @@ func init() { // update runtime config util.DisableLogin = *flagDisableLogin util.BindAddress = *flagBindAddress + util.SendgridApiKey = os.Getenv("SENDGRID_API_KEY") + util.EmailFrom = os.Getenv("EMAIL_FROM") + util.EmailFromName = os.Getenv("EMAIL_FROM_NAME") // print app information fmt.Println("Wireguard UI") @@ -69,9 +75,12 @@ func main() { app.POST("/login", handler.Login()) } + sendmail := emailer.NewSendgridApiMail(util.SendgridApiKey, util.EmailFromName, util.EmailFrom) + app.GET("/logout", handler.Logout()) app.POST("/new-client", handler.NewClient()) app.POST("/update-client", handler.UpdateClient()) + app.POST("/email-client", handler.EmailClient(sendmail)) app.POST("/client/set-status", handler.SetClientStatus()) app.POST("/remove-client", handler.RemoveClient()) app.GET("/download", handler.DownloadClient()) diff --git a/templates/clients.html b/templates/clients.html index b4e26bd4..cc6b2876 100644 --- a/templates/clients.html +++ b/templates/clients.html @@ -30,6 +30,34 @@
+ +