chore: add mailtest tool and local config template
This commit is contained in:
103
cmd/mailtest/main.go
Normal file
103
cmd/mailtest/main.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/smtp"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
smtpHost := getenv("SMTP_HOST", "smtp.163.com")
|
||||
smtpPort := getenv("SMTP_PORT", "465")
|
||||
smtpUser := strings.TrimSpace(os.Getenv("SMTP_USER"))
|
||||
smtpPass := strings.TrimSpace(os.Getenv("SMTP_PASS"))
|
||||
mailTo := strings.TrimSpace(os.Getenv("MAIL_TO"))
|
||||
subject := getenv("MAIL_SUBJECT", "SuperTodo SMTP test")
|
||||
body := getenv("MAIL_BODY", "This is a SMTP connectivity test email from SuperTodo.")
|
||||
|
||||
if smtpUser == "" || smtpPass == "" || mailTo == "" {
|
||||
log.Fatal("missing required env: SMTP_USER, SMTP_PASS, MAIL_TO")
|
||||
}
|
||||
|
||||
if err := sendMailTLS(smtpHost, smtpPort, smtpUser, smtpPass, mailTo, subject, body); err != nil {
|
||||
log.Fatalf("send mail failed: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("mail sent successfully: from=%s to=%s via %s:%s", smtpUser, mailTo, smtpHost, smtpPort)
|
||||
}
|
||||
|
||||
func sendMailTLS(host, port, user, pass, to, subject, body string) error {
|
||||
addr := net.JoinHostPort(host, port)
|
||||
dialer := &net.Dialer{Timeout: 10 * time.Second}
|
||||
conn, err := tls.DialWithDialer(dialer, "tcp", addr, &tls.Config{
|
||||
ServerName: host,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("tls dial failed: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
client, err := smtp.NewClient(conn, host)
|
||||
if err != nil {
|
||||
return fmt.Errorf("smtp client failed: %w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
auth := smtp.PlainAuth("", user, pass, host)
|
||||
if err := client.Auth(auth); err != nil {
|
||||
return fmt.Errorf("smtp auth failed: %w", err)
|
||||
}
|
||||
if err := client.Mail(user); err != nil {
|
||||
return fmt.Errorf("mail from failed: %w", err)
|
||||
}
|
||||
if err := client.Rcpt(to); err != nil {
|
||||
return fmt.Errorf("rcpt to failed: %w", err)
|
||||
}
|
||||
|
||||
w, err := client.Data()
|
||||
if err != nil {
|
||||
return fmt.Errorf("data failed: %w", err)
|
||||
}
|
||||
|
||||
msg := buildMessage(user, to, subject, body)
|
||||
if _, err := w.Write([]byte(msg)); err != nil {
|
||||
return fmt.Errorf("write message failed: %w", err)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
return fmt.Errorf("close writer failed: %w", err)
|
||||
}
|
||||
if err := client.Quit(); err != nil {
|
||||
return fmt.Errorf("smtp quit failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildMessage(from, to, subject, body string) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("From: " + from + "\r\n")
|
||||
b.WriteString("To: " + to + "\r\n")
|
||||
b.WriteString("Subject: " + subject + "\r\n")
|
||||
b.WriteString("MIME-Version: 1.0\r\n")
|
||||
b.WriteString("Content-Type: text/plain; charset=UTF-8\r\n")
|
||||
b.WriteString("\r\n")
|
||||
b.WriteString(body)
|
||||
if !strings.HasSuffix(body, "\n") {
|
||||
b.WriteString("\r\n")
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func getenv(key, fallback string) string {
|
||||
v := strings.TrimSpace(os.Getenv(key))
|
||||
if v == "" {
|
||||
return fallback
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user