diff --git a/backend/src/email.ts b/backend/src/email.ts index 5da98e2..23c213a 100644 --- a/backend/src/email.ts +++ b/backend/src/email.ts @@ -10,93 +10,118 @@ const transporter = nodemailer.createTransport({ }, }) -export async function sendPasswordResetEmail(to: string, name: string, token: string): Promise { - const appUrl = process.env.APP_URL ?? 'https://app.hotelsync.ru' - const resetUrl = `${appUrl}/reset-password?token=${token}` - const fromAddr = process.env.SMTP_USER ?? 'noreply@hotelsync.ru' +const fromAddr = () => process.env.SMTP_USER ?? 'noreply@hotelsync.ru' +const appUrl = () => process.env.APP_URL ?? 'https://app.hotelsync.ru' +const apiUrl = () => process.env.API_URL ?? 'https://api.hotelsync.ru' - await transporter.sendMail({ - from: `"HotelSync" <${fromAddr}>`, - to, - subject: 'Сброс пароля — HotelSync', - html: ` - - +const unsubscribeHeader = `` + +function baseHtml(headerColor: string, icon: string, title: string, body: string): string { + return ` + + + + + + -
-
-
🔑
-

HotelSync

+
+
+
${icon}
+

HotelSync

Современная PMS-система

-

Сброс пароля

-

Привет, ${name}!

-

- Мы получили запрос на сброс пароля для вашего аккаунта HotelSync. Нажмите на кнопку ниже, чтобы создать новый пароль. -

- -

- ⏱ Ссылка действительна 1 час.
- Если вы не запрашивали сброс пароля — просто проигнорируйте это письмо. -

+ ${body}
-

- Кнопка не работает? Скопируйте ссылку в браузер:
- ${resetUrl} +

+ © 2026 HotelSync · app.hotelsync.ru

-`, +` +} + +export async function sendPasswordResetEmail(to: string, name: string, token: string): Promise { + const resetUrl = `${appUrl()}/reset-password?token=${token}` + + const html = baseHtml( + 'linear-gradient(135deg,#4f46e5,#6366f1)', + '🔑', + 'Сброс пароля', + `

Сброс пароля

+

Привет, ${name}!

+

+ Мы получили запрос на сброс пароля для вашего аккаунта HotelSync. Нажмите на кнопку ниже, чтобы создать новый пароль. +

+ +

+ Ссылка действительна 1 час.
+ Если вы не запрашивали сброс пароля — просто проигнорируйте это письмо. +

+

+ Кнопка не работает? Скопируйте ссылку в браузер:
+ ${resetUrl} +

`, + ) + + const text = `Сброс пароля — HotelSync\n\nПривет, ${name}!\n\nМы получили запрос на сброс пароля для вашего аккаунта HotelSync.\n\nСсылка для сброса пароля (действительна 1 час):\n${resetUrl}\n\nЕсли вы не запрашивали сброс пароля — проигнорируйте это письмо.\n\n© 2026 HotelSync` + + await transporter.sendMail({ + from: `"HotelSync" <${fromAddr()}>`, + to, + subject: 'Сброс пароля — HotelSync', + html, + text, + headers: { + 'List-Unsubscribe': unsubscribeHeader, + 'Content-Language': 'ru', + }, }) } export async function sendConfirmationEmail(to: string, name: string, token: string): Promise { - const apiUrl = process.env.API_URL ?? 'https://api.hotelsync.ru' - const confirmUrl = `${apiUrl}/api/auth/confirm-email?token=${token}` - const fromAddr = process.env.SMTP_USER ?? 'noreply@hotelsync.ru' + const confirmUrl = `${apiUrl()}/api/auth/confirm-email?token=${token}` - await transporter.sendMail({ - from: `"HotelSync" <${fromAddr}>`, - to, - subject: 'Подтвердите email — HotelSync', - html: ` - - - -
-
-
🏨
-

HotelSync

-

Современная PMS-система

-
-
-

Добро пожаловать, ${name}!

-

+ const html = baseHtml( + 'linear-gradient(135deg,#4f46e5,#6366f1)', + '🏨', + 'Подтверждение email', + `

Добро пожаловать, ${name}!

+

Спасибо за регистрацию в HotelSync. Для активации аккаунта нажмите на кнопку ниже.

-

- ⏱ Ссылка действительна 24 часа.
+

+ Ссылка действительна 24 часа.
Если вы не регистрировались — просто проигнорируйте это письмо.

-
-

+

Кнопка не работает? Скопируйте ссылку в браузер:
${confirmUrl} -

-
-
- -`, +

`, + ) + + const text = `Подтверждение email — HotelSync\n\nДобро пожаловать, ${name}!\n\nСпасибо за регистрацию в HotelSync.\n\nДля активации аккаунта перейдите по ссылке (действительна 24 часа):\n${confirmUrl}\n\nЕсли вы не регистрировались — проигнорируйте это письмо.\n\n© 2026 HotelSync` + + await transporter.sendMail({ + from: `"HotelSync" <${fromAddr()}>`, + to, + subject: 'Подтвердите email — HotelSync', + html, + text, + headers: { + 'List-Unsubscribe': unsubscribeHeader, + 'Content-Language': 'ru', + }, }) }