Initial commit of Descrybe v2 without local scratch artifacts.

Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
2026-08-09 22:47:43 +02:00
commit 8580c996c3
1285 changed files with 325780 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
package campaigns
import (
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"net/mail"
"regexp"
"strings"
"unicode"
"github.com/descrybe/descrybe-v2/apps/api/internal/security"
)
const (
MaxPromptLen = security.MaxCampaignPromptRunes
MaxSubjectLen = 200
MaxHTMLBodyLen = security.MaxEmailHTMLRunes
unsubscribeMark = "data-descrybe-unsubscribe"
)
var emailLoose = regexp.MustCompile(`(?i)^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`)
// NormalizeEmail lowercases and trims; returns ErrInvalidEmail when invalid.
func NormalizeEmail(raw string) (string, error) {
raw = strings.TrimSpace(strings.ToLower(raw))
if raw == "" || len(raw) > 254 {
return "", ErrInvalidEmail
}
addr, err := mail.ParseAddress(raw)
if err != nil {
return "", ErrInvalidEmail
}
e := strings.TrimSpace(strings.ToLower(addr.Address))
if !emailLoose.MatchString(e) {
return "", ErrInvalidEmail
}
return e, nil
}
func EmailHash(email string) string {
sum := sha256.Sum256([]byte(strings.ToLower(strings.TrimSpace(email))))
return hex.EncodeToString(sum[:])
}
func NewToken() (string, error) {
b := make([]byte, 24)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
func ValidatePrompt(prompt string) error {
if security.CapPromptLength(prompt, MaxPromptLen) {
return ErrPromptTooLong
}
return nil
}
// SanitizePrompt bounds and soft-filters campaign prompts before AI / storage.
func SanitizePrompt(prompt string) string {
return security.SanitizePrompt(prompt, MaxPromptLen)
}
// SanitizeHTMLBody strips dangerous markup from generated/stored campaign HTML.
func SanitizeHTMLBody(html string) string {
return security.SanitizeEmailHTML(html)
}
func HasUnsubscribeFooter(html string) bool {
lower := strings.ToLower(html)
if strings.Contains(lower, unsubscribeMark) {
return true
}
if strings.Contains(lower, "unsubscribe") && (strings.Contains(lower, "href=") || strings.Contains(lower, "/unsubscribe")) {
return true
}
return false
}
func EnsureUnsubscribeFooter(html, plain, unsubscribeURL string) (string, string) {
if HasUnsubscribeFooter(html) {
if plain == "" {
plain = stripTags(html)
}
return html, plain
}
footerHTML := `<hr style="border:none;border-top:1px solid #ddd;margin:24px 0"/>` +
`<p style="font-size:12px;color:#666" ` + unsubscribeMark + `="1">` +
`You are receiving this because you opted in to marketing emails. ` +
`<a href="` + unsubscribeURL + `">Unsubscribe</a>.</p>`
footerPlain := "\n\n---\nUnsubscribe: " + unsubscribeURL + "\n"
if strings.TrimSpace(html) == "" {
html = "<div></div>"
}
html = html + footerHTML
if plain == "" {
plain = stripTags(html)
} else {
plain = plain + footerPlain
}
return html, plain
}
func stripTags(s string) string {
var b strings.Builder
inTag := false
for _, r := range s {
switch {
case r == '<':
inTag = true
case r == '>':
inTag = false
case !inTag:
if unicode.IsSpace(r) {
if b.Len() > 0 && b.String()[b.Len()-1] != ' ' {
b.WriteByte(' ')
}
} else {
b.WriteRune(r)
}
}
}
return strings.TrimSpace(b.String())
}