restructure project, add claudemd

This commit is contained in:
2026-07-08 16:36:17 -04:00
parent a7964f9410
commit 2a5fbffaa2
315 changed files with 81075 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
package snailmail
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
type cloudflareSender struct{}
type cfEmailRequest struct {
To string `json:"to"`
From string `json:"from"`
Subject string `json:"subject"`
HTML string `json:"html,omitempty"`
Text string `json:"text,omitempty"`
}
type cfEmailResponse struct {
Success bool `json:"success"`
Errors []struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"errors"`
}
func (c *cloudflareSender) Send(message Email, mailtype int) error {
if len(message.Recipients) == 0 {
return fmt.Errorf("cloudflare email: no recipients")
}
req := cfEmailRequest{
To: message.Recipients[0],
From: settings.Cloudflare.FromAddress,
Subject: message.Subject,
}
if mailtype == TYPE_HTML {
req.HTML = message.Body.String()
} else {
req.Text = message.Body.String()
}
body, err := json.Marshal(req)
if err != nil {
return fmt.Errorf("cloudflare email: failed to marshal request: %w", err)
}
url := fmt.Sprintf("https://api.cloudflare.com/client/v4/accounts/%s/email/sending/send", settings.Cloudflare.AccountID)
httpReq, err := http.NewRequest("POST", url, bytes.NewReader(body))
if err != nil {
return fmt.Errorf("cloudflare email: failed to create request: %w", err)
}
httpReq.Header.Set("Authorization", "Bearer "+settings.Cloudflare.APIToken)
httpReq.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(httpReq)
if err != nil {
log.Println("cloudflare email: request failed:", err)
return fmt.Errorf("cloudflare email: request failed: %w", err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("cloudflare email: failed to read response: %w", err)
}
var cfResp cfEmailResponse
if err := json.Unmarshal(respBody, &cfResp); err != nil {
return fmt.Errorf("cloudflare email: failed to parse response: %w", err)
}
if !cfResp.Success {
errMsg := "unknown error"
if len(cfResp.Errors) > 0 {
errMsg = cfResp.Errors[0].Message
}
log.Printf("cloudflare email: send failed: %s (HTTP %d)", errMsg, resp.StatusCode)
return fmt.Errorf("cloudflare email: %s", errMsg)
}
return nil
}

53
go/snailmail/smtp.go Normal file
View File

@@ -0,0 +1,53 @@
package snailmail
import (
"encoding/base64"
"fmt"
"log"
"mime"
"net/mail"
"net/smtp"
"strings"
"time"
)
type smtpSender struct{}
func (s *smtpSender) Send(message Email, mailtype int) error {
recipientString := strings.Join(message.Recipients, ",")
from := mail.Address{Name: settings.SMTP.DisplayFrom, Address: settings.SMTP.Username}
header := make(map[string]string)
header["To"] = recipientString
header["From"] = from.String()
header["Subject"] = mime.QEncoding.Encode("UTF-8", message.Subject)
header["MIME-Version"] = "1.0"
header["Content-Transfer-Encoding"] = "base64"
header["Date"] = time.Now().Format(time.RFC1123)
if mailtype == TYPE_HTML {
header["Content-Type"] = "text/html; charset=\"utf-8\""
} else {
header["Content-Type"] = "text/plain; charset=\"utf-8\""
}
email := ""
for k, v := range header {
email += fmt.Sprintf("%s: %s\r\n", k, v)
}
email += "\r\n" + base64.StdEncoding.EncodeToString(message.Body.Bytes())
var auth smtp.Auth = nil
if settings.SMTP.RequireAuth {
auth = smtp.PlainAuth("", settings.SMTP.Username, settings.SMTP.Password, settings.SMTP.Server)
}
err := smtp.SendMail(settings.SMTP.Server+":"+settings.SMTP.Port, auth, settings.SMTP.Username, message.Recipients, []byte(email))
if err != nil {
log.Println(err)
return err
}
return nil
}

81
go/snailmail/snailmail.go Normal file
View File

@@ -0,0 +1,81 @@
// package snailmail sends email through a pluggable provider (SMTP or Cloudflare).
// The active provider and its credentials are injected via Configure(Settings),
// so the framework never reads application config. Branded message composition
// (templates, logos, copy) stays app-side: apps build an Email and call SendMail.
package snailmail
import (
"bytes"
"log"
"strings"
)
const (
TYPE_TEXT = iota
TYPE_HTML
)
// Email is a composed message ready to send. Body holds the already-rendered
// text or HTML.
type Email struct {
Recipients []string
Subject string
Body *bytes.Buffer
}
// SMTPSettings holds credentials for the SMTP provider.
type SMTPSettings struct {
Server string
Port string
Username string
Password string
DisplayFrom string
RequireAuth bool
}
// CloudflareSettings holds credentials for the Cloudflare email provider.
type CloudflareSettings struct {
AccountID string
APIToken string
FromAddress string
}
// Settings selects and configures the active email provider. Provider is "smtp"
// (default) or "cloudflare".
type Settings struct {
Provider string
SMTP SMTPSettings
Cloudflare CloudflareSettings
}
type sender interface {
Send(message Email, mailtype int) error
}
var (
settings Settings
activeSender sender
)
// Configure stores the provider credentials and selects the active provider.
// Apps call this once at startup with values from their own config.
func Configure(s Settings) {
settings = s
switch strings.ToLower(strings.TrimSpace(s.Provider)) {
case "cloudflare":
activeSender = &cloudflareSender{}
log.Println("mailer: using Cloudflare email provider")
default:
activeSender = &smtpSender{}
log.Println("mailer: using SMTP email provider")
}
}
// SendMail sends message using the configured provider (defaulting to SMTP if
// Configure was never called).
func SendMail(message Email, mailtype int) error {
if activeSender == nil {
activeSender = &smtpSender{}
}
return activeSender.Send(message, mailtype)
}