This commit is contained in:
2026-09-13 14:06:30 +07:00
parent 06eef98cc6
commit 30bb456eef
2 changed files with 31 additions and 19 deletions
+31 -19
View File
@@ -50,8 +50,8 @@ func welcomeHTML(username, email, password, loginURL string) string {
if loginURL != "" {
content.WriteString(buttonHTML(loginURL, "Войти в игру"))
}
content.WriteString("<p style=\"color:#614a16; font-size:13px;\">Если ты не регистрировался — просто проигнорируй это письмо.</p>")
return wrapHTML(content.String())
content.WriteString("<p style=\"color:black; font-size:13px;\">Если ты не регистрировался — просто проигнорируй это письмо.</p>")
return wrapHTML(content.String(), loginURL)
}
// resetPlain возвращает текстовую версию письма о сбросе пароля.
@@ -89,8 +89,8 @@ func resetHTML(username, email, password, loginURL string) string {
content.WriteString(buttonHTML(loginURL, "Войти в игру"))
}
content.WriteString("<p>Никому не сообщай пароль и не пересылай это письмо.</p>\n")
content.WriteString("<p style=\"color:#614a16; font-size:13px;\">Если это был не ты — ответь на это письмо: мы поможем защитить аккаунт.</p>")
return wrapHTML(content.String())
content.WriteString("<p style=\"color:black; font-size:13px;\">Если это был не ты — ответь на это письмо: мы поможем защитить аккаунт.</p>")
return wrapHTML(content.String(), loginURL)
}
// greeting возвращает приветствие с именем пользователя; при пустом имени —
@@ -106,11 +106,11 @@ func greeting(username string) string {
// 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 #614a16; border-bottom:1px dashed #614a16; table-layout:fixed;">
<tr><td style="padding:16px 0 4px; font-family:Arial,Helvetica,sans-serif; font-size:12px; color:#614a16;">Логин</td></tr>
<tr><td style="padding:0 0 16px; font-size:16px; color:#111; overflow-wrap:anywhere; word-break:break-all;">` + html.EscapeString(email) + `</td></tr>
<tr><td style="padding:16px 0 4px; border-top:1px dashed #614a16; font-family:Arial,Helvetica,sans-serif; font-size:12px; color:#614a16;">Пароль</td></tr>
<tr><td style="padding:0 0 16px; font-size:20px; color:#111; overflow-wrap:anywhere; word-break:break-all;"><strong>` + html.EscapeString(password) + `</strong></td></tr>
return `<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="margin:24px 0; border-top:1px dashed black; border-bottom:1px dashed black; table-layout:fixed;">
<tr><td style="padding:16px 0 4px; font-family:Arial,Helvetica,sans-serif; font-size:12px; color:black;">Логин</td></tr>
<tr><td style="padding:0 0 16px; font-size:16px; color:black; overflow-wrap:anywhere; word-break:break-all;">` + html.EscapeString(email) + `</td></tr>
<tr><td style="padding:16px 0 4px; border-top:1px dashed black; font-family:Arial,Helvetica,sans-serif; font-size:12px; color:black;">Пароль</td></tr>
<tr><td style="padding:0 0 16px; font-size:20px; color:black; overflow-wrap:anywhere; word-break:break-all;"><strong>` + html.EscapeString(password) + `</strong></td></tr>
</table>`
}
@@ -118,39 +118,43 @@ func credentialsBlockHTML(email, password string) string {
func buttonHTML(link, label string) string {
return `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:20px 0;">
<tr>
<td style="border-radius:5px; background-color:#222; border:1px solid #614a16;">
<a href="` + html.EscapeString(link) + `" style="display:inline-block; padding:12px 28px; color:#bfa07d; font-family:Arial,Helvetica,sans-serif; font-weight:bold; text-decoration:none; font-size:15px; line-height:24px; border-radius:5px;">` + html.EscapeString(label) + `</a>
<td style="border-radius:5px; background-color:#222; background-image:url('` + emailAssetURL(link, "belt.png") + `'); background-size:cover;">
<a href="` + html.EscapeString(link) + `" style="display:inline-block; padding:8px 16px; color:#bfa07d; font-family:'font_old_typer','Courier New',serif; font-weight:400; text-decoration:none; font-size:18px; line-height:24px; border-radius:5px;">` + html.EscapeString(label) + `</a>
</td>
</tr>
</table>`
}
// wrapHTML — каркас письма: тёмная шапка с названием сервиса, контент,
// подвал с дисклеймером. Только табличная вёрстка и inline-стили: внешние
// таблицы стилей Gmail вырезает.
func wrapHTML(content string) string {
// подвал. Основное оформление — inline; при блокировке текстур и шрифта
// остаются фон bisque и системный шрифт.
func wrapHTML(content, baseURL string) string {
return `<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
@font-face { font-family:'font_old_typer'; src:url('` + emailAssetURL(baseURL, "a_OldTyper.ttf") + `') format('truetype'); }
a:hover { color:#63e2b7 !important; }
</style>
</head>
<body style="margin:0; padding:0; background-color:#111; font-family:Arial, Helvetica, sans-serif;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#111; padding:24px 12px;">
<tr><td align="center">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px; width:100%; background-color:#222; border:1px solid #614a16; border-radius:10px;">
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px; width:100%;">
<tr>
<td style="background-color:#222; padding:24px; border-bottom:3px solid #614a16;">
<span style="color:#bfa07d; font-family:'Courier New',Courier,monospace; font-size:22px; line-height:1.4; font-weight:normal;">ВЕЧЕРНИЙ ДЕТЕКТИВ</span>
<td style="padding:16px 0 24px;">
<span style="color:#bfa07d; font-family:'font_old_typer','Courier New',serif; font-size:22px; line-height:1.4; font-weight:normal;">ВЕЧЕРНИЙ ДЕТЕКТИВ</span>
</td>
</tr>
<tr>
<td style="padding:24px; background-color:#f4ead5; color:#111; font-family:'Courier New',Courier,monospace; font-size:16px; line-height:1.7;">
<td style="padding:16px; background-color:bisque; background-image:url('` + emailAssetURL(baseURL, "paper.jpg") + `'); background-size:cover; border-radius:5px; box-shadow:0 0 5px black; color:black; font-family:'font_old_typer','Courier New',serif; font-size:18px; line-height:1.5; font-weight:400;">
` + content + `
</td>
</tr>
<tr>
<td style="padding:20px 24px; background-color:#222; color:#aaa; font-size:12px; line-height:1.6; border-top:3px solid #614a16;">
<td style="padding:24px 0; color:#aaa; font-size:12px; line-height:1.6;">
Это автоматическое письмо сервиса «Вечерний детектив».<br>
Если ты получил его по ошибке — просто ответь на это письмо.
</td>
@@ -162,6 +166,14 @@ func wrapHTML(content string) string {
</html>`
}
// Статические копии игровых текстур и шрифта находятся в public/email фронтенда.
func emailAssetURL(baseURL, filename string) string {
if baseURL == "" {
return ""
}
return html.EscapeString(strings.TrimRight(baseURL, "/") + "/email/" + filename)
}
// sanitizeLine убирает переносы строк из пользовательского ввода,
// вставляемого в тело письма (защита от инъекции строк в DATA-фазу SMTP).
func sanitizeLine(s string) string {