This commit is contained in:
2026-10-03 13:22:11 +07:00
parent 05090722a0
commit 2caa2c1a88
74 changed files with 44 additions and 676 deletions
+1 -23
View File
@@ -6,17 +6,11 @@ import (
"strings"
)
// Письма содержат текстовую и HTML-версию; sender собирает multipart/alternative.
const (
// welcomeSubject — тема письма о регистрации: конкретика без КАПСА и
// кликбейта, до 50 символов (видно на мобильных целиком).
welcomeSubject = "Твой доступ к «Вечернему детективу»"
// resetSubject — тема письма о сбросе пароля.
resetSubject = "Новый пароль для «Вечернего детектива»"
resetSubject = "Новый пароль для «Вечернего детектива»"
)
// welcomePlain возвращает текстовую версию письма о регистрации.
func welcomePlain(username, email, password, loginURL string) string {
var b strings.Builder
b.WriteString(greeting(username))
@@ -39,7 +33,6 @@ func welcomePlain(username, email, password, loginURL string) string {
return sanitizeLine(b.String())
}
// welcomeHTML возвращает HTML-версию письма о регистрации.
func welcomeHTML(username, email, password, loginURL string) string {
var content strings.Builder
content.WriteString("<p class=\"email-text\" style=\"color:#211a14;\">Привет, <strong>")
@@ -54,7 +47,6 @@ func welcomeHTML(username, email, password, loginURL string) string {
return wrapHTML(content.String(), loginURL)
}
// resetPlain возвращает текстовую версию письма о сбросе пароля.
func resetPlain(username, email, password, loginURL string) string {
var b strings.Builder
b.WriteString(greeting(username))
@@ -77,7 +69,6 @@ func resetPlain(username, email, password, loginURL string) string {
return sanitizeLine(b.String())
}
// resetHTML возвращает HTML-версию письма о сбросе пароля.
func resetHTML(username, email, password, loginURL string) string {
var content strings.Builder
content.WriteString("<p class=\"email-text\" style=\"color:#211a14;\">Привет, <strong>")
@@ -93,8 +84,6 @@ func resetHTML(username, email, password, loginURL string) string {
return wrapHTML(content.String(), loginURL)
}
// greeting возвращает приветствие с именем пользователя; при пустом имени —
// без обращения, чтобы не было «Привет, !».
func greeting(username string) string {
username = sanitizeLine(strings.TrimSpace(username))
if username == "" {
@@ -103,8 +92,6 @@ func greeting(username string) string {
return fmt.Sprintf("Привет, %s!", username)
}
// credentialsBlockHTML — блок «Логин / Пароль» в табличной вёрстке,
// совместимой с Gmail.
func credentialsBlockHTML(email, password string) string {
return `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:24px 0; border-top:1px dashed #888888; border-bottom:1px dashed #888888; table-layout:fixed;">
<tr><td class="email-text" style="padding:16px 0 4px; font-family:Arial,Helvetica,sans-serif; font-size:12px; color:#211a14;">Логин</td></tr>
@@ -114,7 +101,6 @@ func credentialsBlockHTML(email, password string) string {
</table>`
}
// buttonHTML — кнопка входа с inline-стилями и табличным фоном.
func buttonHTML(link, label string) string {
return `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:20px 0;">
<tr>
@@ -125,8 +111,6 @@ func buttonHTML(link, label string) string {
</table>`
}
// wrapHTML — светлая палитра по умолчанию, тёмная через media query.
// Клиенты без поддержки CSS темы могут перекрашивать обычные цвета сами.
func wrapHTML(content, baseURL string) string {
return `<!DOCTYPE html>
<html lang="ru">
@@ -181,7 +165,6 @@ func wrapHTML(content, baseURL string) string {
</html>`
}
// Шрифт находится в public/email фронтенда.
func emailAssetURL(baseURL, filename string) string {
if baseURL == "" {
return ""
@@ -189,15 +172,10 @@ func emailAssetURL(baseURL, filename string) string {
return html.EscapeString(strings.TrimRight(baseURL, "/") + "/email/" + filename)
}
// sanitizeLine убирает переносы строк из пользовательского ввода,
// вставляемого в тело письма (защита от инъекции строк в DATA-фазу SMTP).
func sanitizeLine(s string) string {
return strings.NewReplacer("\r", "", "\n", "").Replace(s)
}
// loginURL нормализует адрес фронтенда для ссылки в письме: срезает
// завершающий слэш и допускает только http(s). Пустое или некорректное
// значение — ссылка в письма не добавляется.
func loginURL(baseURL string) string {
baseURL = strings.TrimSpace(baseURL)
baseURL = strings.TrimRight(baseURL, "/")