Created Turnpike, event attendee and volunteer management
Built after prototype, Traverse, an attendee and volunteer list maintainer.
This commit is contained in:
commit
1033cdb29b
59 changed files with 8663 additions and 0 deletions
140
email.go
Normal file
140
email.go
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SMTPConfig struct {
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
From string
|
||||
FromName string
|
||||
}
|
||||
|
||||
// loadSMTPConfig reads SMTP settings from the config table, overlaying any
|
||||
// values set via CLI flags (which take priority).
|
||||
func (app *App) loadSMTPConfig() SMTPConfig {
|
||||
get := func(key string) string {
|
||||
var v string
|
||||
app.db.QueryRow(`SELECT value FROM config WHERE key = ?`, key).Scan(&v)
|
||||
return v
|
||||
}
|
||||
|
||||
cfg := SMTPConfig{
|
||||
Host: app.smtpHost,
|
||||
Port: app.smtpPort,
|
||||
User: app.smtpUser,
|
||||
Password: app.smtpPassword,
|
||||
From: app.smtpFrom,
|
||||
FromName: app.smtpFromName,
|
||||
}
|
||||
|
||||
if cfg.Host == "" {
|
||||
cfg.Host = get("smtp_host")
|
||||
}
|
||||
if cfg.Port == 0 {
|
||||
fmt.Sscanf(get("smtp_port"), "%d", &cfg.Port)
|
||||
}
|
||||
if cfg.User == "" {
|
||||
cfg.User = get("smtp_user")
|
||||
}
|
||||
if cfg.Password == "" {
|
||||
cfg.Password = get("smtp_password")
|
||||
}
|
||||
if cfg.From == "" {
|
||||
cfg.From = get("smtp_from")
|
||||
}
|
||||
if cfg.FromName == "" {
|
||||
cfg.FromName = get("smtp_from_name")
|
||||
}
|
||||
if cfg.Port == 0 {
|
||||
cfg.Port = 587
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
// sendEmail delivers a plain-text email.
|
||||
// Uses implicit TLS on port 465, STARTTLS on all other ports.
|
||||
func sendEmail(cfg SMTPConfig, to, subject, body string) error {
|
||||
fromHeader := cfg.From
|
||||
if cfg.FromName != "" {
|
||||
fromHeader = fmt.Sprintf("%s <%s>", cfg.FromName, cfg.From)
|
||||
}
|
||||
msg := fmt.Sprintf(
|
||||
"From: %s\r\nTo: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n%s",
|
||||
fromHeader, to, subject, body,
|
||||
)
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)
|
||||
auth := smtp.PlainAuth("", cfg.User, cfg.Password, cfg.Host)
|
||||
|
||||
if cfg.Port == 465 {
|
||||
tlsCfg := &tls.Config{ServerName: cfg.Host}
|
||||
conn, err := tls.Dial("tcp", addr, tlsCfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tls dial: %w", err)
|
||||
}
|
||||
c, err := smtp.NewClient(conn, cfg.Host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("smtp client: %w", err)
|
||||
}
|
||||
defer c.Close()
|
||||
if err = c.Auth(auth); err != nil {
|
||||
return fmt.Errorf("smtp auth: %w", err)
|
||||
}
|
||||
if err = c.Mail(cfg.From); err != nil {
|
||||
return fmt.Errorf("smtp mail from: %w", err)
|
||||
}
|
||||
if err = c.Rcpt(to); err != nil {
|
||||
return fmt.Errorf("smtp rcpt: %w", err)
|
||||
}
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return fmt.Errorf("smtp data: %w", err)
|
||||
}
|
||||
if _, err = fmt.Fprint(w, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
return w.Close()
|
||||
}
|
||||
|
||||
return smtp.SendMail(addr, auth, cfg.From, []string{to}, []byte(msg))
|
||||
}
|
||||
|
||||
// sendTokenEmail sends a volunteer token link to the attendee's email address.
|
||||
func (app *App) sendTokenEmail(a Attendee) error {
|
||||
if a.Email == "" {
|
||||
return fmt.Errorf("attendee has no email address")
|
||||
}
|
||||
if a.VolunteerToken == nil || *a.VolunteerToken == "" {
|
||||
return fmt.Errorf("attendee has no volunteer token")
|
||||
}
|
||||
|
||||
cfg := app.loadSMTPConfig()
|
||||
|
||||
baseURL := app.baseURL
|
||||
if baseURL == "" {
|
||||
app.db.QueryRow(`SELECT value FROM config WHERE key = 'base_url'`).Scan(&baseURL)
|
||||
}
|
||||
baseURL = strings.TrimRight(baseURL, "/")
|
||||
|
||||
event, _ := app.getEvent()
|
||||
eventName := "the event"
|
||||
if event != nil && event.Name != "" {
|
||||
eventName = event.Name
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("%s/#/v/%s", baseURL, *a.VolunteerToken)
|
||||
subject := fmt.Sprintf("Your volunteer link for %s", eventName)
|
||||
body := fmt.Sprintf(
|
||||
"Hi %s,\n\nThank you for volunteering at %s!\n\nYour volunteer token: %s\nYour signup link: %s\n\nUse this link to sign up for available shifts in your department.\n\nSee you there!\n",
|
||||
a.Name, eventName, *a.VolunteerToken, link,
|
||||
)
|
||||
|
||||
return sendEmail(cfg, a.Email, subject, body)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue