diff --git a/handler/routes.go b/handler/routes.go index 3ddbb2d..04a8710 100644 --- a/handler/routes.go +++ b/handler/routes.go @@ -314,7 +314,7 @@ func NewClient(db store.IStore) echo.HandlerFunc { } // EmailClient handler to send the configuration via email -func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailContent string) echo.HandlerFunc { +func EmailClient(db store.IStore) echo.HandlerFunc { type clientIdEmailPayload struct { ID string `json:"id"` Email string `json:"email"` @@ -337,6 +337,15 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"}) } + // init mailer + var mailer emailer.Emailer + emailSetting, _ := db.GetEmailSettings() + if emailSetting.SendgridApiKey != "" { + mailer = emailer.NewSendgridApiMail(emailSetting.SendgridApiKey, emailSetting.EmailFromName, emailSetting.EmailFrom) + } else { + mailer = emailer.NewSmtpMail(emailSetting.SmtpHostname, emailSetting.SmtpPort, emailSetting.SmtpUsername, emailSetting.SmtpPassword, emailSetting.SmtpNoTLSCheck, emailSetting.SmtpAuthType, emailSetting.EmailFromName, emailSetting.EmailFrom, emailSetting.SmtpEncryption) + } + // build config server, _ := db.GetServer() globalSettings, _ := db.GetGlobalSettings() @@ -357,8 +366,8 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon err = mailer.Send( clientData.Client.Name, payload.Email, - emailSubject, - emailContent, + emailSetting.DefaultEmailSubject, + emailSetting.DefaultEmailContent, attachments, ) @@ -596,6 +605,64 @@ func GlobalSettings(db store.IStore) echo.HandlerFunc { } } +// EmailSettings handler +func EmailSettings(db store.IStore) echo.HandlerFunc { + return func(c echo.Context) error { + + emailSettings, err := db.GetEmailSettings() + if err != nil { + log.Error("Cannot get email settings: ", err) + } + + return c.Render(http.StatusOK, "email_settings.html", map[string]interface{}{ + "baseData": model.BaseData{Active: "email-settings", CurrentUser: currentUser(c), Admin: isAdmin(c)}, + "emailSettings": emailSettings, + }) + } +} + +// EmailSettingsSubmit handler to update the email settings +func EmailSettingsSubmit(db store.IStore) echo.HandlerFunc { + return func(c echo.Context) error { + data := make(map[string]interface{}) + err := json.NewDecoder(c.Request().Body).Decode(&data) + + if err != nil { + return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, "Bad post data"}) + } + + emailSetting, err := db.GetEmailSettings() + if err != nil { + log.Error("Cannot get email settings: ", err) + } + + if len(data) == 2 { + emailSetting.DefaultEmailSubject = data["default_email_subject"].(string) + emailSetting.DefaultEmailContent = data["default_email_content"].(string) + } else { + emailSetting.SendgridApiKey = data["sendgrid_api_key"].(string) + emailSetting.EmailFromName = data["email_from_name"].(string) + emailSetting.EmailFrom = data["email_from"].(string) + emailSetting.SmtpHostname = data["smtp_hostname"].(string) + emailSetting.SmtpPort = int(data["smtp_port"].(float64)) + emailSetting.SmtpUsername = data["smtp_username"].(string) + emailSetting.SmtpPassword = data["smtp_password"].(string) + emailSetting.SmtpNoTLSCheck = data["smtp_no_tls_check"].(bool) + emailSetting.SmtpAuthType = data["smtp_auth_type"].(string) + emailSetting.SmtpEncryption = data["smtp_encryption"].(string) + } + + // write config to the database + if err := db.SaveEmailSettings(emailSetting); err != nil { + return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot update email settings"}) + } + + log.Infof("Updated email settings: %v", emailSetting) + + return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated email settings successfully"}) + } +} + // Status handler func Status(db store.IStore) echo.HandlerFunc { type PeerVM struct { diff --git a/main.go b/main.go index 3f0cd13..9d0008e 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,6 @@ import ( "time" rice "github.com/GeertJohan/go.rice" - "github.com/ngoduykhanh/wireguard-ui/emailer" "github.com/ngoduykhanh/wireguard-ui/handler" "github.com/ngoduykhanh/wireguard-ui/router" "github.com/ngoduykhanh/wireguard-ui/store/jsondb" @@ -42,18 +41,11 @@ var ( flagBasePath string ) -const ( - defaultEmailSubject = "Your wireguard configuration" - defaultEmailContent = `Hi, -
In this email you can find your personal configuration for our wireguard server.
- -Best
-` -) - func init() { // command-line flags and env variables + // TODO move ENV_VAR variables and default values to config.go + flag.BoolVar(&flagDisableLogin, "disable-login", util.LookupEnvOrBool("DISABLE_LOGIN", flagDisableLogin), "Disable authentication on the app. This is potentially dangerous.") 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") @@ -127,6 +119,34 @@ func main() { // create the wireguard config on start, if it doesn't exist initServerConfig(db, tmplBox) + // check and update email settings if necessary + if util.SendgridApiKey != "" { + emailSetting, _ := db.GetEmailSettings() + emailSetting.SendgridApiKey = util.SendgridApiKey + emailSetting.EmailFromName = util.EmailFromName + emailSetting.EmailFrom = util.EmailFrom + err := db.SaveEmailSettings(emailSetting) + if err != nil { + panic(err) + } + } + if util.SmtpUsername != "" { + emailSetting, _ := db.GetEmailSettings() + emailSetting.EmailFromName = util.EmailFromName + emailSetting.EmailFrom = util.EmailFrom + emailSetting.SmtpHostname = util.SmtpHostname + emailSetting.SmtpPort = util.SmtpPort + emailSetting.SmtpUsername = util.SmtpUsername + emailSetting.SmtpPassword = util.SmtpPassword + emailSetting.SmtpNoTLSCheck = util.SmtpNoTLSCheck + emailSetting.SmtpAuthType = util.SmtpAuthType + emailSetting.SmtpEncryption = util.SmtpEncryption + err := db.SaveEmailSettings(emailSetting) + if err != nil { + panic(err) + } + } + // register routes app := router.New(tmplBox, extraData, util.SessionSecret) @@ -140,17 +160,10 @@ func main() { app.POST(util.BasePath+"/profile", handler.UpdateProfile(db), handler.ValidSession) } - var sendmail emailer.Emailer - 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) - } - app.GET(util.BasePath+"/_health", handler.Health()) 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) + app.POST(util.BasePath+"/email-client", handler.EmailClient(db), handler.ValidSession, handler.ContentTypeJson) app.POST(util.BasePath+"/client/set-status", handler.SetClientStatus(db), handler.ValidSession, handler.ContentTypeJson) app.POST(util.BasePath+"/remove-client", handler.RemoveClient(db), handler.ValidSession, handler.ContentTypeJson) app.GET(util.BasePath+"/download", handler.DownloadClient(db), handler.ValidSession) @@ -159,6 +172,8 @@ func main() { app.POST(util.BasePath+"/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession, handler.ContentTypeJson) app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession) app.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson) + app.GET(util.BasePath+"/email-settings", handler.EmailSettings(db), handler.ValidSession) + app.POST(util.BasePath+"/email-settings", handler.EmailSettingsSubmit(db), handler.ValidSession) app.GET(util.BasePath+"/status", handler.Status(db), handler.ValidSession) app.GET(util.BasePath+"/api/clients", handler.GetClients(db), handler.ValidSession) app.GET(util.BasePath+"/api/client/:id", handler.GetClient(db), handler.ValidSession) diff --git a/model/setting.go b/model/setting.go index e871591..8e2d35f 100644 --- a/model/setting.go +++ b/model/setting.go @@ -14,3 +14,18 @@ type GlobalSetting struct { ConfigFilePath string `json:"config_file_path"` UpdatedAt time.Time `json:"updated_at"` } + +type EmailSetting struct { + SendgridApiKey string `json:"sendgrid_api_key"` + EmailFromName string `json:"email_from_name"` + EmailFrom string `json:"email_from"` + SmtpHostname string `json:"smtp_hostname"` + SmtpPort int `json:"smtp_port"` + SmtpUsername string `json:"smtp_username"` + SmtpPassword string `json:"smtp_password"` + SmtpNoTLSCheck bool `json:"smtp_no_tls_check"` + SmtpAuthType string `json:"smtp_auth_type"` + SmtpEncryption string `json:"smtp_encryption"` + DefaultEmailSubject string `json:"default_email_subject"` + DefaultEmailContent string `json:"default_email_content"` +} diff --git a/router/router.go b/router/router.go index 0f9facc..e2a540d 100644 --- a/router/router.go +++ b/router/router.go @@ -83,6 +83,11 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec log.Fatal(err) } + tmplEmailSettingsString, err := tmplBox.String("email_settings.html") + if err != nil { + log.Fatal(err) + } + tmplStatusString, err := tmplBox.String("status.html") if err != nil { log.Fatal(err) @@ -103,6 +108,7 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec templates["clients.html"] = template.Must(template.New("clients").Funcs(funcs).Parse(tmplBaseString + tmplClientsString)) templates["server.html"] = template.Must(template.New("server").Funcs(funcs).Parse(tmplBaseString + tmplServerString)) templates["global_settings.html"] = template.Must(template.New("global_settings").Funcs(funcs).Parse(tmplBaseString + tmplGlobalSettingsString)) + templates["email_settings.html"] = template.Must(template.New("email_settings").Funcs(funcs).Parse(tmplBaseString + tmplEmailSettingsString)) templates["status.html"] = template.Must(template.New("status").Funcs(funcs).Parse(tmplBaseString + tmplStatusString)) templates["wake_on_lan_hosts.html"] = template.Must(template.New("wake_on_lan_hosts").Funcs(funcs).Parse(tmplBaseString + tmplWakeOnLanHostsString)) diff --git a/store/jsondb/jsondb.go b/store/jsondb/jsondb.go index f39a452..ca1aba2 100644 --- a/store/jsondb/jsondb.go +++ b/store/jsondb/jsondb.go @@ -42,7 +42,9 @@ func (o *JsonDB) Init() error { var serverInterfacePath string = path.Join(serverPath, "interfaces.json") var serverKeyPairPath string = path.Join(serverPath, "keypair.json") var globalSettingPath string = path.Join(serverPath, "global_settings.json") + var emailSettingPath string = path.Join(serverPath, "email_settings.json") var userPath string = path.Join(serverPath, "users.json") + // create directories if they do not exist if _, err := os.Stat(clientPath); os.IsNotExist(err) { os.MkdirAll(clientPath, os.ModePerm) @@ -102,6 +104,27 @@ func (o *JsonDB) Init() error { o.conn.Write("server", "global_settings", globalSetting) } + // email settings + // TODO move ENV_VAR variables and default values to config.go + if _, err := os.Stat(emailSettingPath); os.IsNotExist(err) { + emailSetting := new(model.EmailSetting) + emailSetting.SendgridApiKey = util.LookupEnvOrString("SENDGRID_API_KEY", "") + emailSetting.EmailFromName = util.LookupEnvOrString("EMAIL_FROM_NAME", "WireGuard UI") + emailSetting.EmailFrom = util.LookupEnvOrString("EMAIL_FROM_ADDRESS", "") + emailSetting.SmtpHostname = util.LookupEnvOrString("SMTP_HOSTNAME", "127.0.0.1") + emailSetting.SmtpPort = util.LookupEnvOrInt("SMTP_PORT", 25) + emailSetting.SmtpUsername = util.LookupEnvOrString("SMTP_USERNAME", "") + emailSetting.SmtpPassword = util.LookupEnvOrString("SMTP_PASSWORD", "") + emailSetting.SmtpNoTLSCheck = util.LookupEnvOrBool("SMTP_NO_TLS_CHECK", false) + emailSetting.SmtpAuthType = util.LookupEnvOrString("SMTP_AUTH_TYPE", "NONE") + emailSetting.SmtpEncryption = util.LookupEnvOrString("SMTP_ENCRYPTION", "STARTTLS") + emailSetting.DefaultEmailSubject = "Your wireguard configuration" + emailSetting.DefaultEmailContent = `Hi, +In this email you can find your personal configuration for our wireguard server.
+Best
` + o.conn.Write("server", "email_settings", emailSetting) + } + // user info if _, err := os.Stat(userPath); os.IsNotExist(err) { user := new(model.User) @@ -255,3 +278,13 @@ func (o *JsonDB) SaveServerKeyPair(serverKeyPair model.ServerKeypair) error { func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error { return o.conn.Write("server", "global_settings", globalSettings) } + +// GetEmailSettings func to query email settings from the database +func (o *JsonDB) GetEmailSettings() (model.EmailSetting, error) { + settings := model.EmailSetting{} + return settings, o.conn.Read("server", "email_settings", &settings) +} + +func (o *JsonDB) SaveEmailSettings(emailSettings model.EmailSetting) error { + return o.conn.Write("server", "email_settings", emailSettings) +} diff --git a/store/store.go b/store/store.go index 86d6224..7dec9e0 100644 --- a/store/store.go +++ b/store/store.go @@ -9,6 +9,7 @@ type IStore interface { GetUser() (model.User, error) SaveUser(user model.User) error GetGlobalSettings() (model.GlobalSetting, error) + GetEmailSettings() (model.EmailSetting, error) GetServer() (model.Server, error) GetClients(hasQRCode bool) ([]model.ClientData, error) GetClientByID(clientID string, qrCode model.QRCodeSettings) (model.ClientData, error) @@ -17,6 +18,7 @@ type IStore interface { SaveServerInterface(serverInterface model.ServerInterface) error SaveServerKeyPair(serverKeyPair model.ServerKeypair) error SaveGlobalSettings(globalSettings model.GlobalSetting) error + SaveEmailSettings(emailSettings model.EmailSetting) error GetWakeOnLanHosts() ([]model.WakeOnLanHost, error) GetWakeOnLanHost(macAddress string) (*model.WakeOnLanHost, error) DeleteWakeOnHostLanHost(macAddress string) error diff --git a/templates/base.html b/templates/base.html index fd337a7..eadfbef 100644 --- a/templates/base.html +++ b/templates/base.html @@ -124,6 +124,14 @@ ++ Email Settings +
+ +