* Introduced wedding-themed CSS variables for styling. * Created layout component for the wedding site with navigation and countdown. * Added RSVP and photo upload pages with form handling. * Implemented QR code page for wedding site access. * Configured TypeScript and Vite for the project. * Added robots.txt for search engine crawling.
78 lines
1.9 KiB
Go
78 lines
1.9 KiB
Go
package mail
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/smtp"
|
|
"strings"
|
|
|
|
"wedding-server/internal/config"
|
|
"wedding-server/internal/store"
|
|
)
|
|
|
|
type Mailer struct {
|
|
cfg config.EmailConfig
|
|
}
|
|
|
|
func New(cfg config.EmailConfig) *Mailer {
|
|
return &Mailer{cfg: cfg}
|
|
}
|
|
|
|
func (m *Mailer) send(subject, body string) error {
|
|
if m.cfg.SMTPHost == "" {
|
|
log.Printf("mail: SMTP not configured, skipping email %q", subject)
|
|
return nil
|
|
}
|
|
|
|
addr := fmt.Sprintf("%s:%d", m.cfg.SMTPHost, m.cfg.SMTPPort)
|
|
auth := smtp.PlainAuth("", m.cfg.SMTPUser, m.cfg.SMTPPass, m.cfg.SMTPHost)
|
|
|
|
msg := fmt.Sprintf(
|
|
"From: %s\r\nTo: %s\r\nSubject: %s\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\n%s",
|
|
m.cfg.From, m.cfg.To, subject, body,
|
|
)
|
|
|
|
return smtp.SendMail(addr, auth, m.cfg.From, []string{m.cfg.To}, []byte(msg))
|
|
}
|
|
|
|
func (m *Mailer) SendRSVP(r store.RSVP) error {
|
|
names := make([]string, len(r.Guests))
|
|
adults, children := 0, 0
|
|
for i, g := range r.Guests {
|
|
if g.IsChild {
|
|
names[i] = g.Name + " (child)"
|
|
children++
|
|
} else {
|
|
names[i] = g.Name
|
|
adults++
|
|
}
|
|
}
|
|
joined := strings.Join(names, ", ")
|
|
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "New RSVP for %d guest(s) — %d adult(s), %d child(ren): %s\n", len(r.Guests), adults, children, joined)
|
|
if r.Message != "" {
|
|
fmt.Fprintf(&b, "\nMessage:\n%s\n", r.Message)
|
|
}
|
|
return m.send(fmt.Sprintf("RSVP from %s", joined), b.String())
|
|
}
|
|
|
|
func (m *Mailer) SendPhotoUpload(u store.PhotoUpload, files []store.PhotoFile) error {
|
|
var b strings.Builder
|
|
fmt.Fprintf(&b, "%s uploaded %d photo(s)\n\n", u.Name, len(files))
|
|
if u.Email != "" {
|
|
fmt.Fprintf(&b, "Email: %s\n", u.Email)
|
|
}
|
|
if u.Phone != "" {
|
|
fmt.Fprintf(&b, "Phone: %s\n", u.Phone)
|
|
}
|
|
if u.Message != "" {
|
|
fmt.Fprintf(&b, "\nMessage:\n%s\n", u.Message)
|
|
}
|
|
b.WriteString("\nPhotos:\n")
|
|
for _, f := range files {
|
|
fmt.Fprintf(&b, "- %s\n", f.URL)
|
|
}
|
|
return m.send(fmt.Sprintf("%s uploaded photos", u.Name), b.String())
|
|
}
|