mirror of
https://github.com/ngoduykhanh/wireguard-ui.git
synced 2025-04-21 20:12:33 +03:00
Initial email settings UI commit
Added email settings page, settings now save in database, ability to send an email to client when it's created
This commit is contained in:
parent
f8a10417ea
commit
c31636b66e
9 changed files with 568 additions and 25 deletions
|
@ -20,7 +20,7 @@ type SmtpMail struct {
|
||||||
from string
|
from string
|
||||||
}
|
}
|
||||||
|
|
||||||
func authType(authType string) mail.AuthType {
|
func AuthType(authType string) mail.AuthType {
|
||||||
switch strings.ToUpper(authType) {
|
switch strings.ToUpper(authType) {
|
||||||
case "PLAIN":
|
case "PLAIN":
|
||||||
return mail.AuthPlain
|
return mail.AuthPlain
|
||||||
|
@ -31,7 +31,7 @@ func authType(authType string) mail.AuthType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func encryptionType(encryptionType string) mail.Encryption {
|
func EncryptionType(encryptionType string) mail.Encryption {
|
||||||
switch strings.ToUpper(encryptionType) {
|
switch strings.ToUpper(encryptionType) {
|
||||||
case "SSL":
|
case "SSL":
|
||||||
return mail.EncryptionSSL
|
return mail.EncryptionSSL
|
||||||
|
@ -45,7 +45,7 @@ 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 {
|
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)}
|
ans := SmtpMail{hostname: hostname, port: port, username: username, password: password, noTLSCheck: noTLSCheck, fromName: fromName, from: from, authType: AuthType(auth), encryption: EncryptionType(encryption)}
|
||||||
return &ans
|
return &ans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -466,7 +466,7 @@ func NewClient(db store.IStore) echo.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmailClient handler to send the configuration via email
|
// 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 {
|
type clientIdEmailPayload struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
@ -489,6 +489,15 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon
|
||||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
|
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
|
// build config
|
||||||
server, _ := db.GetServer()
|
server, _ := db.GetServer()
|
||||||
globalSettings, _ := db.GetGlobalSettings()
|
globalSettings, _ := db.GetGlobalSettings()
|
||||||
|
@ -509,10 +518,11 @@ func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailCon
|
||||||
err = mailer.Send(
|
err = mailer.Send(
|
||||||
clientData.Client.Name,
|
clientData.Client.Name,
|
||||||
payload.Email,
|
payload.Email,
|
||||||
emailSubject,
|
emailSetting.DefaultEmailSubject,
|
||||||
emailContent,
|
emailSetting.DefaultEmailContent,
|
||||||
attachments,
|
attachments,
|
||||||
)
|
)
|
||||||
|
fmt.Println("\n\n\n %s \n\n\n", err)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
|
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
|
||||||
|
@ -748,6 +758,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
|
// Status handler
|
||||||
func Status(db store.IStore) echo.HandlerFunc {
|
func Status(db store.IStore) echo.HandlerFunc {
|
||||||
type PeerVM struct {
|
type PeerVM struct {
|
||||||
|
|
52
main.go
52
main.go
|
@ -11,7 +11,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
rice "github.com/GeertJohan/go.rice"
|
rice "github.com/GeertJohan/go.rice"
|
||||||
"github.com/ngoduykhanh/wireguard-ui/emailer"
|
|
||||||
"github.com/ngoduykhanh/wireguard-ui/handler"
|
"github.com/ngoduykhanh/wireguard-ui/handler"
|
||||||
"github.com/ngoduykhanh/wireguard-ui/router"
|
"github.com/ngoduykhanh/wireguard-ui/router"
|
||||||
"github.com/ngoduykhanh/wireguard-ui/store/jsondb"
|
"github.com/ngoduykhanh/wireguard-ui/store/jsondb"
|
||||||
|
@ -42,18 +41,11 @@ var (
|
||||||
flagBasePath string
|
flagBasePath string
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
defaultEmailSubject = "Your wireguard configuration"
|
|
||||||
defaultEmailContent = `Hi,</br>
|
|
||||||
<p>In this email you can find your personal configuration for our wireguard server.</p>
|
|
||||||
|
|
||||||
<p>Best</p>
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
// command-line flags and env variables
|
// 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.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(&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.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
|
// create the wireguard config on start, if it doesn't exist
|
||||||
initServerConfig(db, tmplBox)
|
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
|
// register routes
|
||||||
app := router.New(tmplBox, extraData, util.SessionSecret)
|
app := router.New(tmplBox, extraData, util.SessionSecret)
|
||||||
|
|
||||||
|
@ -145,17 +165,10 @@ func main() {
|
||||||
app.GET(util.BasePath+"/api/user/:username", handler.GetUser(db), handler.ValidSession)
|
app.GET(util.BasePath+"/api/user/:username", handler.GetUser(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.GET(util.BasePath+"/_health", handler.Health())
|
||||||
app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson)
|
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+"/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+"/client/set-status", handler.SetClientStatus(db), handler.ValidSession, handler.ContentTypeJson)
|
||||||
app.POST(util.BasePath+"/remove-client", handler.RemoveClient(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)
|
app.GET(util.BasePath+"/download", handler.DownloadClient(db), handler.ValidSession)
|
||||||
|
@ -163,7 +176,8 @@ func main() {
|
||||||
app.POST(util.BasePath+"/wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
app.POST(util.BasePath+"/wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||||
app.POST(util.BasePath+"/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
app.POST(util.BasePath+"/wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||||
app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession, handler.NeedsAdmin)
|
app.GET(util.BasePath+"/global-settings", handler.GlobalSettings(db), handler.ValidSession, handler.NeedsAdmin)
|
||||||
|
app.GET(util.BasePath+"/email-settings", handler.EmailSettings(db), handler.ValidSession, handler.NeedsAdmin)
|
||||||
|
app.POST(util.BasePath+"/email-settings", handler.EmailSettingsSubmit(db), handler.ValidSession, handler.NeedsAdmin)
|
||||||
app.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
app.POST(util.BasePath+"/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession, handler.ContentTypeJson, handler.NeedsAdmin)
|
||||||
app.GET(util.BasePath+"/status", handler.Status(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/clients", handler.GetClients(db), handler.ValidSession)
|
||||||
|
|
|
@ -14,3 +14,18 @@ type GlobalSetting struct {
|
||||||
ConfigFilePath string `json:"config_file_path"`
|
ConfigFilePath string `json:"config_file_path"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
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"`
|
||||||
|
}
|
||||||
|
|
|
@ -83,6 +83,11 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tmplEmailSettingsString, err := tmplBox.String("email_settings.html")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
tmplUsersSettingsString, err := tmplBox.String("users_settings.html")
|
tmplUsersSettingsString, err := tmplBox.String("users_settings.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -108,6 +113,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["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["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["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["users_settings.html"] = template.Must(template.New("users_settings").Funcs(funcs).Parse(tmplBaseString + tmplUsersSettingsString))
|
templates["users_settings.html"] = template.Must(template.New("users_settings").Funcs(funcs).Parse(tmplBaseString + tmplUsersSettingsString))
|
||||||
templates["status.html"] = template.Must(template.New("status").Funcs(funcs).Parse(tmplBaseString + tmplStatusString))
|
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))
|
templates["wake_on_lan_hosts.html"] = template.Must(template.New("wake_on_lan_hosts").Funcs(funcs).Parse(tmplBaseString + tmplWakeOnLanHostsString))
|
||||||
|
|
|
@ -42,6 +42,7 @@ func (o *JsonDB) Init() error {
|
||||||
var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
|
var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
|
||||||
var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
|
var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
|
||||||
var globalSettingPath string = path.Join(serverPath, "global_settings.json")
|
var globalSettingPath string = path.Join(serverPath, "global_settings.json")
|
||||||
|
var emailSettingPath string = path.Join(serverPath, "email_settings.json")
|
||||||
var userPath string = path.Join(o.dbPath, "users")
|
var userPath string = path.Join(o.dbPath, "users")
|
||||||
// create directories if they do not exist
|
// create directories if they do not exist
|
||||||
if _, err := os.Stat(clientPath); os.IsNotExist(err) {
|
if _, err := os.Stat(clientPath); os.IsNotExist(err) {
|
||||||
|
@ -105,6 +106,27 @@ func (o *JsonDB) Init() error {
|
||||||
o.conn.Write("server", "global_settings", globalSetting)
|
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,</br>
|
||||||
|
<p>In this email you can find your personal configuration for our wireguard server.</p>
|
||||||
|
<p>Best</p>`
|
||||||
|
o.conn.Write("server", "email_settings", emailSetting)
|
||||||
|
}
|
||||||
|
|
||||||
// user info
|
// user info
|
||||||
results, err := o.conn.ReadAll("users")
|
results, err := o.conn.ReadAll("users")
|
||||||
if err != nil || len(results) < 1 {
|
if err != nil || len(results) < 1 {
|
||||||
|
@ -295,3 +317,13 @@ func (o *JsonDB) SaveServerKeyPair(serverKeyPair model.ServerKeypair) error {
|
||||||
func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error {
|
func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error {
|
||||||
return o.conn.Write("server", "global_settings", globalSettings)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ type IStore interface {
|
||||||
SaveUser(user model.User) error
|
SaveUser(user model.User) error
|
||||||
DeleteUser(username string) error
|
DeleteUser(username string) error
|
||||||
GetGlobalSettings() (model.GlobalSetting, error)
|
GetGlobalSettings() (model.GlobalSetting, error)
|
||||||
|
GetEmailSettings() (model.EmailSetting, error)
|
||||||
GetServer() (model.Server, error)
|
GetServer() (model.Server, error)
|
||||||
GetClients(hasQRCode bool) ([]model.ClientData, error)
|
GetClients(hasQRCode bool) ([]model.ClientData, error)
|
||||||
GetClientByID(clientID string, qrCode model.QRCodeSettings) (model.ClientData, error)
|
GetClientByID(clientID string, qrCode model.QRCodeSettings) (model.ClientData, error)
|
||||||
|
@ -19,6 +20,7 @@ type IStore interface {
|
||||||
SaveServerInterface(serverInterface model.ServerInterface) error
|
SaveServerInterface(serverInterface model.ServerInterface) error
|
||||||
SaveServerKeyPair(serverKeyPair model.ServerKeypair) error
|
SaveServerKeyPair(serverKeyPair model.ServerKeypair) error
|
||||||
SaveGlobalSettings(globalSettings model.GlobalSetting) error
|
SaveGlobalSettings(globalSettings model.GlobalSetting) error
|
||||||
|
SaveEmailSettings(emailSettings model.EmailSetting) error
|
||||||
GetWakeOnLanHosts() ([]model.WakeOnLanHost, error)
|
GetWakeOnLanHosts() ([]model.WakeOnLanHost, error)
|
||||||
GetWakeOnLanHost(macAddress string) (*model.WakeOnLanHost, error)
|
GetWakeOnLanHost(macAddress string) (*model.WakeOnLanHost, error)
|
||||||
DeleteWakeOnHostLanHost(macAddress string) error
|
DeleteWakeOnHostLanHost(macAddress string) error
|
||||||
|
|
|
@ -134,6 +134,14 @@
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="{{.basePath}}/email-settings" class="nav-link {{if eq .baseData.Active "email-settings" }}active{{end}}">
|
||||||
|
<i class="nav-icon fas fa-cog"></i>
|
||||||
|
<p>
|
||||||
|
Email Settings
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a href="{{.basePath}}/users-settings" class="nav-link {{if eq .baseData.Active "users-settings" }}active{{end}}">
|
<a href="{{.basePath}}/users-settings" class="nav-link {{if eq .baseData.Active "users-settings" }}active{{end}}">
|
||||||
<i class="nav-icon fas fa-cog"></i>
|
<i class="nav-icon fas fa-cog"></i>
|
||||||
|
@ -226,6 +234,14 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="icheck-primary d-inline">
|
||||||
|
<input type="checkbox" id="send_email_after_creation" {{ if .client_defaults.EnableAfterCreation }}checked{{ end }}>
|
||||||
|
<label for="send_email_after_creation">
|
||||||
|
Send email after creation
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>Public and Preshared Keys</strong>
|
<summary><strong>Public and Preshared Keys</strong>
|
||||||
<i class="fas fa-info-circle" data-toggle="tooltip"
|
<i class="fas fa-info-circle" data-toggle="tooltip"
|
||||||
|
@ -399,6 +415,7 @@
|
||||||
success: function(resp) {
|
success: function(resp) {
|
||||||
$("#modal_new_client").modal('hide');
|
$("#modal_new_client").modal('hide');
|
||||||
toastr.success('Created new client successfully');
|
toastr.success('Created new client successfully');
|
||||||
|
sendEmailAfterCreation(resp.id, resp.email);
|
||||||
// Update the home page (clients page) after adding successfully
|
// Update the home page (clients page) after adding successfully
|
||||||
if (window.location.pathname === "{{.basePath}}/") {
|
if (window.location.pathname === "{{.basePath}}/") {
|
||||||
populateClient(resp.id);
|
populateClient(resp.id);
|
||||||
|
@ -431,6 +448,31 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendEmailAfterCreation(client_id, client_email) {
|
||||||
|
console.log("Do you even try?");
|
||||||
|
if ($("#send_email_after_creation").is(':checked')) {
|
||||||
|
if (client_email !== "" && client_id) {
|
||||||
|
const data = {"id": client_id, "email": client_email};
|
||||||
|
$.ajax({
|
||||||
|
cache: false,
|
||||||
|
method: 'POST',
|
||||||
|
url: '{{.basePath}}/email-client',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
success: function (resp) {
|
||||||
|
toastr.success('Sent email to client successfully');
|
||||||
|
},
|
||||||
|
error: function (jqXHR, exception) {
|
||||||
|
const responseJson = jQuery.parseJSON(jqXHR.responseText);
|
||||||
|
toastr.error(responseJson['message']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
//Initialize Select2 Elements
|
//Initialize Select2 Elements
|
||||||
|
|
364
templates/email_settings.html
Normal file
364
templates/email_settings.html
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
{{define "title"}}
|
||||||
|
Email Settings
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "top_css"}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "username"}}
|
||||||
|
{{ .username }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "page_title"}}
|
||||||
|
Email Settings
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "page_content"}}
|
||||||
|
<section class="content">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<!-- <h5 class="mt-4 mb-2">Global Settings</h5> -->
|
||||||
|
<div class="row">
|
||||||
|
<!-- left column -->
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card card-success">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Email Settings</h3>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-header -->
|
||||||
|
<!-- form start -->
|
||||||
|
<form role="form" id="frm_email_settings" name="frm_email_settings">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="sendgrid_api_key">Sendgrid API Key (If provided, overrules SMTP
|
||||||
|
settings)</label>
|
||||||
|
<input type="text" class="form-control" id="sendgrid_api_key"
|
||||||
|
name="sendgrid_api_key" value="{{ .emailSettings.SendgridApiKey }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email_from_name">Email From Name</label>
|
||||||
|
<input type="text" class="form-control" id="email_from_name"
|
||||||
|
name="email_from_name" placeholder="E.g. WireGuard UI"
|
||||||
|
value="{{ .emailSettings.EmailFromName }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email_from">Email From</label>
|
||||||
|
<input type="text" class="form-control" id="email_from"
|
||||||
|
name="email_from" placeholder="E.g. system@wireguard.com"
|
||||||
|
value="{{ .emailSettings.EmailFrom }}">
|
||||||
|
</div>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<div class="form-group" style="width: 48%;">
|
||||||
|
<label for="smtp_hostname">SMTP Hostname</label>
|
||||||
|
<input type="text" class="form-control" id="smtp_hostname"
|
||||||
|
name="smtp_hostname" placeholder="E.g. 127.0.0.1"
|
||||||
|
value="{{ .emailSettings.SmtpHostname }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="width: 48%; margin-left: 4%">
|
||||||
|
<label for="smtp_port">SMTP Port</label>
|
||||||
|
<input type="text" class="form-control" id="smtp_port"
|
||||||
|
name="smtp_port" placeholder="E.g. 25"
|
||||||
|
value="{{ .emailSettings.SmtpPort }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<div class="form-group" style="width: 48%;">
|
||||||
|
<label for="smtp_username">SMTP Username</label>
|
||||||
|
<input type="text" class="form-control" id="smtp_username"
|
||||||
|
name="smtp_username" placeholder="E.g. system@wireguard.com"
|
||||||
|
value="{{ .emailSettings.SmtpUsername }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="width: 48%; margin-left: 4%">
|
||||||
|
<label for="smtp_password">SMTP Password</label>
|
||||||
|
<input type="text" class="form-control" id="smtp_password"
|
||||||
|
name="smtp_password" placeholder="E.g. *******"
|
||||||
|
value="{{ .emailSettings.SmtpPassword }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<div class="form-group" style="width: 48%;">
|
||||||
|
<label for="smtp_auth_type">SMTP Auth Type</label>
|
||||||
|
<input type="text" class="form-control" id="smtp_auth_type"
|
||||||
|
name="smtp_auth_type" placeholder="NONE/LOGIN/PLAIN"
|
||||||
|
value="{{ .emailSettings.SmtpAuthType }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group" style="width: 48%; margin-left: 4%">
|
||||||
|
<label for="smtp_encryption">SMTP Encryption</label>
|
||||||
|
<input type="text" class="form-control" id="smtp_encryption"
|
||||||
|
name="smtp_encryption" placeholder="SSL/SSLTLS/TLS/STARTTLS"
|
||||||
|
value="{{ .emailSettings.SmtpEncryption }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="icheck-primary d-inline">
|
||||||
|
<input type="checkbox" id="smtp_no_tls_check" {{if .emailSettings.SmtpNoTLSCheck
|
||||||
|
}}checked{{end}}>
|
||||||
|
<label for="smtp_no_tls_check">
|
||||||
|
SMTP No TLS Check
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- /.card-body -->
|
||||||
|
|
||||||
|
<div class="card-footer">
|
||||||
|
<button type="submit" class="btn btn-success">Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card card-success">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Email Template</h3>
|
||||||
|
</div>
|
||||||
|
<!-- /.card-header -->
|
||||||
|
<form role="form" id="frm_email_settings2" name="frm_email_settings2">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="default_email_subject">Email Subject</label>
|
||||||
|
<input type="text" class="form-control" id="default_email_subject"
|
||||||
|
name="default_email_subject" placeholder="Your Wireguard configuration"
|
||||||
|
value="{{ .emailSettings.DefaultEmailSubject }}">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="default_email_content">Email Content</label>
|
||||||
|
<textarea class="form-control" id="default_email_content"
|
||||||
|
name="default_email_content" rows="10" style="white-space: pre-wrap;">{{ .emailSettings.DefaultEmailContent }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<button type="submit" class="btn btn-success">Save</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<!-- /.card -->
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "bottom_js"}}
|
||||||
|
<script>
|
||||||
|
function submitEmailSettings() {
|
||||||
|
|
||||||
|
const sendgrid_api_key = $("#sendgrid_api_key").val();
|
||||||
|
const email_from_name = $("#email_from_name").val();
|
||||||
|
const email_from = $("#email_from").val();
|
||||||
|
const smtp_hostname = $("#smtp_hostname").val();
|
||||||
|
let smtp_port = 0;
|
||||||
|
if ($("#smtp_port").val().length !== 0) {
|
||||||
|
smtp_port = parseInt($("#smtp_port").val());
|
||||||
|
}
|
||||||
|
const smtp_username = $("#smtp_username").val();
|
||||||
|
const smtp_password = $("#smtp_password").val();
|
||||||
|
const smtp_auth_type = $("#smtp_auth_type").val();
|
||||||
|
const smtp_encryption = $("#smtp_encryption").val();
|
||||||
|
let smtp_no_tls_check = false;
|
||||||
|
if ($("#smtp_no_tls_check").is(':checked')) {
|
||||||
|
smtp_no_tls_check = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
"sendgrid_api_key": sendgrid_api_key,
|
||||||
|
"email_from_name": email_from_name,
|
||||||
|
"email_from": email_from,
|
||||||
|
"smtp_hostname": smtp_hostname,
|
||||||
|
"smtp_port": smtp_port,
|
||||||
|
"smtp_username": smtp_username,
|
||||||
|
"smtp_password": smtp_password,
|
||||||
|
"smtp_no_tls_check": smtp_no_tls_check,
|
||||||
|
"smtp_auth_type": smtp_auth_type,
|
||||||
|
"smtp_encryption": smtp_encryption
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
cache: false,
|
||||||
|
method: 'POST',
|
||||||
|
url: '{{.basePath}}/email-settings',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
success: function (data) {
|
||||||
|
toastr.success('Updated email settings successfully');
|
||||||
|
},
|
||||||
|
error: function (jqXHR, exception) {
|
||||||
|
const responseJson = jQuery.parseJSON(jqXHR.responseText);
|
||||||
|
toastr.error(responseJson['message']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitEmailSettings2() {
|
||||||
|
|
||||||
|
const default_email_subject = $("#default_email_subject").val();
|
||||||
|
const default_email_content = $("#default_email_content").val();
|
||||||
|
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
"default_email_content": default_email_content,
|
||||||
|
"default_email_subject": default_email_subject
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
cache: false,
|
||||||
|
method: 'POST',
|
||||||
|
url: '{{.basePath}}/email-settings',
|
||||||
|
dataType: 'json',
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
success: function (data) {
|
||||||
|
toastr.success('Updated email default message settings successfully');
|
||||||
|
},
|
||||||
|
error: function (jqXHR, exception) {
|
||||||
|
const responseJson = jQuery.parseJSON(jqXHR.responseText);
|
||||||
|
toastr.error(responseJson['message']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
// submitHandler
|
||||||
|
function submitHandler(form) {
|
||||||
|
const formId = $(form).attr('id');
|
||||||
|
if (formId === "frm_email_settings") {
|
||||||
|
submitEmailSettings();
|
||||||
|
} else if (formId === "frm_email_settings2") {
|
||||||
|
submitEmailSettings2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global setting form validation
|
||||||
|
$(document).ready(function () {
|
||||||
|
$.validator.setDefaults({
|
||||||
|
submitHandler: function (form) {
|
||||||
|
submitHandler(form);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#frm_email_settings").validate({
|
||||||
|
rules: {
|
||||||
|
email_from_name: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
email_from: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
smtp_hostname: {
|
||||||
|
required: function () {
|
||||||
|
return $("#sendgrid_api_key").val() === "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
smtp_port: {
|
||||||
|
required: function () {
|
||||||
|
return $("#sendgrid_api_key").val() === "";
|
||||||
|
},
|
||||||
|
digits: true,
|
||||||
|
range: [0, 65535]
|
||||||
|
},
|
||||||
|
smtp_username: {
|
||||||
|
required: function () {
|
||||||
|
return $("#sendgrid_api_key").val() === "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
smtp_password: {
|
||||||
|
required: function () {
|
||||||
|
return $("#sendgrid_api_key").val() === "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
smtp_no_tls_check: {
|
||||||
|
digits: true
|
||||||
|
},
|
||||||
|
smtp_auth_type: {
|
||||||
|
required: function () {
|
||||||
|
return $("#sendgrid_api_key").val() === "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
smtp_encryption: {
|
||||||
|
required: function () {
|
||||||
|
return $("#sendgrid_api_key").val() === "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
email_from_name: {
|
||||||
|
required: "Field is mandatory"
|
||||||
|
},
|
||||||
|
email_from: {
|
||||||
|
required: "Field is mandatory"
|
||||||
|
},
|
||||||
|
smtp_hostname: {
|
||||||
|
required: "No Sendgrid API key, this field is mandatory"
|
||||||
|
},
|
||||||
|
smtp_port: {
|
||||||
|
required: "No Sendgrid API key, this field is mandatory",
|
||||||
|
digits: "Port must be an integer",
|
||||||
|
range: "Port must be in range 0..65535"
|
||||||
|
},
|
||||||
|
smtp_username: {
|
||||||
|
required: "No Sendgrid API key, this field is mandatory"
|
||||||
|
},
|
||||||
|
smtp_password: {
|
||||||
|
required: "No Sendgrid API key, this field is mandatory"
|
||||||
|
},
|
||||||
|
smtp_auth_type: {
|
||||||
|
required: "No Sendgrid API key, this field is mandatory"
|
||||||
|
},
|
||||||
|
smtp_encryption: {
|
||||||
|
required: "No Sendgrid API key, this field is mandatory"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
errorElement: 'span',
|
||||||
|
errorPlacement: function (error, element) {
|
||||||
|
error.addClass('invalid-feedback');
|
||||||
|
element.closest('.form-group').append(error);
|
||||||
|
},
|
||||||
|
highlight: function (element, errorClass, validClass) {
|
||||||
|
$(element).addClass('is-invalid');
|
||||||
|
},
|
||||||
|
unhighlight: function (element, errorClass, validClass) {
|
||||||
|
$(element).removeClass('is-invalid');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#frm_email_settings2").validate({
|
||||||
|
rules: {
|
||||||
|
default_email_subject: {
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
default_email_content: {
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
default_email_subject: {
|
||||||
|
required: "Field is mandatory"
|
||||||
|
},
|
||||||
|
default_email_content: {
|
||||||
|
required: "Field is mandatory"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
errorElement: 'span',
|
||||||
|
errorPlacement: function (error, element) {
|
||||||
|
error.addClass('invalid-feedback');
|
||||||
|
element.closest('.form-group').append(error);
|
||||||
|
},
|
||||||
|
highlight: function (element, errorClass, validClass) {
|
||||||
|
$(element).addClass('is-invalid');
|
||||||
|
},
|
||||||
|
unhighlight: function (element, errorClass, validClass) {
|
||||||
|
$(element).removeClass('is-invalid');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{{end}}
|
Loading…
Add table
Reference in a new issue