2026-04-15 18:08:26 +05:00
|
|
|
|
import nodemailer from 'nodemailer';
|
2026-04-15 21:32:55 +05:00
|
|
|
|
import type { EmailOptions } from '../globalInterfaces';
|
2026-04-15 18:08:26 +05:00
|
|
|
|
|
2026-05-04 02:42:45 +05:00
|
|
|
|
const isDev = import.meta.env.DEV;
|
2026-05-04 02:45:06 +05:00
|
|
|
|
const isProduction = import.meta.env.PROD === 'true' || !isDev;
|
2026-05-04 02:42:45 +05:00
|
|
|
|
|
2026-05-04 02:45:06 +05:00
|
|
|
|
const SMTP_HOST = isProduction
|
|
|
|
|
|
? (import.meta.env.SMTP_HOST || 'smtp.resend.com')
|
|
|
|
|
|
: 'localhost';
|
|
|
|
|
|
const SMTP_PORT = isProduction
|
|
|
|
|
|
? (import.meta.env.SMTP_PORT || '465')
|
|
|
|
|
|
: '1025';
|
|
|
|
|
|
const SMTP_AUTH_USER = isProduction ? (import.meta.env.SMTP_AUTH_USER || '') : '';
|
|
|
|
|
|
const SMTP_AUTH_PASS = isProduction ? (import.meta.env.SMTP_AUTH_PASS || '') : '';
|
|
|
|
|
|
const FROM_EMAIL = isProduction
|
|
|
|
|
|
? (import.meta.env.FROM_EMAIL || 'onboarding@resend.onlinemail.me')
|
|
|
|
|
|
: 'noreply@localhost';
|
|
|
|
|
|
const FROM_NAME = isProduction ? (import.meta.env.FROM_NAME || 'Автоюрист Сургут') : 'Dev';
|
|
|
|
|
|
const SITE_URL = isProduction
|
|
|
|
|
|
? (import.meta.env.SITE_URL || 'https://avtourist-surgut.ru')
|
|
|
|
|
|
: 'http://localhost:4321';
|
2026-04-15 18:08:26 +05:00
|
|
|
|
|
|
|
|
|
|
let transporter: nodemailer.Transporter | null = null;
|
|
|
|
|
|
|
|
|
|
|
|
function getTransporter() {
|
|
|
|
|
|
if (!transporter) {
|
|
|
|
|
|
transporter = nodemailer.createTransport({
|
|
|
|
|
|
host: SMTP_HOST,
|
2026-05-04 02:42:45 +05:00
|
|
|
|
port: parseInt(SMTP_PORT),
|
2026-05-04 02:45:06 +05:00
|
|
|
|
secure: isProduction,
|
|
|
|
|
|
requireTLS: isProduction,
|
|
|
|
|
|
auth: isProduction && SMTP_AUTH_USER ? {
|
2026-05-04 02:42:45 +05:00
|
|
|
|
user: SMTP_AUTH_USER,
|
|
|
|
|
|
pass: SMTP_AUTH_PASS,
|
2026-05-04 02:45:06 +05:00
|
|
|
|
} : undefined,
|
2026-04-15 18:08:26 +05:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
return transporter;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function sendEmail(options: EmailOptions): Promise<boolean> {
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log('Sending email to:', options.to);
|
|
|
|
|
|
console.log('SMTP config:', { host: SMTP_HOST, port: SMTP_PORT });
|
|
|
|
|
|
|
|
|
|
|
|
const info = await getTransporter().sendMail({
|
|
|
|
|
|
from: `${FROM_NAME} <${FROM_EMAIL}>`,
|
|
|
|
|
|
to: options.to,
|
|
|
|
|
|
subject: options.subject,
|
|
|
|
|
|
html: options.html,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
console.log('Email sent:', info.messageId);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Email send error:', error);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function getSiteUrl(): string {
|
|
|
|
|
|
return SITE_URL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function generateVerifyEmailHtml(firstName: string, verifyLink: string): string {
|
|
|
|
|
|
return `
|
|
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
<html>
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
<title>Подтверждение регистрации</title>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body style="margin: 0; padding: 0; background-color: #f5f7fa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;">
|
|
|
|
|
|
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f5f7fa; padding: 40px 20px;">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td align="center">
|
|
|
|
|
|
<table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; background-color: #ffffff; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.08);">
|
|
|
|
|
|
<!-- Header -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="background: linear-gradient(135deg, #1e3050 0%, #2d4a6f 100%); padding: 30px 40px; text-align: center;">
|
|
|
|
|
|
<h1 style="color: #ffffff; margin: 0; font-size: 28px; font-weight: 700;">Автоюрист Сургут</h1>
|
|
|
|
|
|
<p style="color: rgba(255,255,255,0.8); margin: 10px 0 0 0; font-size: 16px;">Юридические услуги для автовладельцев</p>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Content -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="padding: 40px;">
|
|
|
|
|
|
<h2 style="color: #1e3050; margin: 0 0 20px 0; font-size: 24px; font-weight: 700;">Добро пожаловать, ${firstName}!</h2>
|
|
|
|
|
|
|
|
|
|
|
|
<p style="color: #64748b; font-size: 16px; line-height: 1.6; margin: 0 0 20px 0;">
|
|
|
|
|
|
Благодарим за регистрацию на сайте <strong>avtourist-surgut.ru</strong>. Мы рады, что вы выбрали нас для решения юридических вопросов, связанных с автомобилем.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<p style="color: #64748b; font-size: 16px; line-height: 1.6; margin: 0 0 30px 0;">
|
|
|
|
|
|
Для завершения регистрации и активации вашего аккаунта, пожалуйста, подтвердите ваш email, нажав на кнопку ниже:
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Button -->
|
|
|
|
|
|
<table width="100%" cellpadding="0" cellspacing="0">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td align="center">
|
|
|
|
|
|
<a href="${verifyLink}" style="display: inline-block; background: linear-gradient(135deg, #eac26e 0%, #ce9f40 100%); color: #ffffff; text-decoration: none; padding: 16px 32px; border-radius: 8px; font-size: 16px; font-weight: 700;">
|
|
|
|
|
|
Подтвердить регистрацию
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Features -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="background-color: #f8fafc; padding: 30px 40px;">
|
|
|
|
|
|
<table width="100%" cellpadding="0" cellspacing="0">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td align="center" style="padding: 0 10px;">
|
|
|
|
|
|
<div style="width: 48px; height: 48px; background: linear-gradient(135deg, #eac26e 0%, #ce9f40 100%); border-radius: 12px; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 12px;">
|
|
|
|
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
|
|
|
|
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p style="color: #1e3050; font-size: 13px; font-weight: 600; margin: 0;">Бесплатная консультация</p>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td align="center" style="padding: 0 10px;">
|
|
|
|
|
|
<div style="width: 48px; height: 48px; background: linear-gradient(135deg, #eac26e 0%, #ce9f40 100%); border-radius: 12px; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 12px;">
|
|
|
|
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
|
|
|
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
|
|
|
|
|
<polyline points="14 2 14 8 20 8"></polyline>
|
|
|
|
|
|
<line x1="16" y1="13" x2="8" y2="13"></line>
|
|
|
|
|
|
<line x1="16" y1="17" x2="8" y2="17"></line>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p style="color: #1e3050; font-size: 13px; font-weight: 600; margin: 0;">Оплата за результат</p>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
<td align="center" style="padding: 0 10px;">
|
|
|
|
|
|
<div style="width: 48px; height: 48px; background: linear-gradient(135deg, #eac26e 0%, #ce9f40 100%); border-radius: 12px; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 12px;">
|
|
|
|
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
|
|
|
|
<circle cx="12" cy="12" r="10"></circle>
|
|
|
|
|
|
<polyline points="12 6 12 12 16 14"></polyline>
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p style="color: #1e3050; font-size: 13px; font-weight: 600; margin: 0;">Работаем 24/7</p>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Footer -->
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td style="background-color: #1e3050; padding: 24px 40px; text-align: center;">
|
|
|
|
|
|
<p style="color: rgba(255,255,255,0.6); font-size: 14px; margin: 0;">
|
|
|
|
|
|
© 2026 Автоюрист Сургут. Все права защищены.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p style="color: rgba(255,255,255,0.4); font-size: 12px; margin: 10px 0 0 0;">
|
|
|
|
|
|
Это письмо отправлено автоматически, пожалуйста, не отвечайте на него.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|
|
|
|
|
|
`;
|
|
|
|
|
|
}
|