Email и Telegram уведомления о заявках

This commit is contained in:
DeepAgent
2025-10-27 11:09:50 +00:00
parent cc072700be
commit 70fde77a5e
7 changed files with 246 additions and 9 deletions

View File

@@ -1 +1,7 @@
DATABASE_URL="postgresql://role_ac7b0eb9d:3wQrvEGI1mSQvd4jrlAusxaHTDC6hOr1@db-ac7b0eb9d.db002.hosteddb.reai.io:5432/ac7b0eb9d?connect_timeout=15"
DATABASE_URL='postgresql://role_ac7b0eb9d:3wQrvEGI1mSQvd4jrlAusxaHTDC6hOr1@db-ac7b0eb9d.db002.hosteddb.reai.io:5432/ac7b0eb9d?connect_timeout=15'
SMTP_HOST=smtp.mail.ru
SMTP_USER=info@global-it24.ru
SMTP_PASSWORD=Nibexg7AX8WWVZag9nqA
NOTIFICATION_EMAIL=info@global-it24.ru
TELEGRAM_BOT_TOKEN=7337244212:AAGRVHJQqwF6lfhbQCOEdn9muRksjgp9PKk
TELEGRAM_CHAT_ID=131517337

16
nextjs_space/.env.example Normal file
View File

@@ -0,0 +1,16 @@
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/global_it24?schema=public"
# SMTP Configuration for Email Notifications
SMTP_HOST=smtp.mail.ru
SMTP_USER=your-email@mail.ru
SMTP_PASSWORD=your-smtp-password
NOTIFICATION_EMAIL=your-email@mail.ru
# Telegram Bot Configuration (optional)
TELEGRAM_BOT_TOKEN=your-bot-token
TELEGRAM_CHAT_ID=your-chat-id
# Application
NEXT_PUBLIC_SITE_URL=https://video.mscsrv.ru

View File

@@ -1,6 +1,8 @@
import { NextRequest, NextResponse } from 'next/server'
import { PrismaClient } from '@prisma/client'
import { sendEmailNotification } from '@/lib/email'
import { sendTelegramNotification } from '@/lib/telegram'
export const dynamic = "force-dynamic"
@@ -19,15 +21,29 @@ export async function POST(request: NextRequest) {
)
}
// Prepare data
const submissionData = {
name: name.trim(),
phone: phone.trim(),
email: email?.trim() || null,
serviceType: serviceType?.trim() || null,
message: message?.trim() || null,
}
// Save to database
const submission = await prisma.contactSubmission.create({
data: {
name: name.trim(),
phone: phone.trim(),
email: email?.trim() || null,
serviceType: serviceType?.trim() || null,
message: message?.trim() || null,
},
data: submissionData,
})
// Send notifications (don't wait for them, run in background)
Promise.all([
sendEmailNotification(submissionData),
sendTelegramNotification(submissionData),
]).then(([emailResult, telegramResult]) => {
console.log('Email notification:', emailResult.success ? 'sent' : 'failed')
console.log('Telegram notification:', telegramResult.success ? 'sent' : 'failed')
}).catch((error) => {
console.error('Error sending notifications:', error)
})
return NextResponse.json(

114
nextjs_space/lib/email.ts Normal file
View File

@@ -0,0 +1,114 @@
import nodemailer from 'nodemailer'
interface EmailParams {
name: string
phone: string
email?: string | null
serviceType?: string | null
message?: string | null
}
export async function sendEmailNotification(params: EmailParams) {
try {
// Создаем транспорт для Mail.ru
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST || 'smtp.mail.ru',
port: 465,
secure: true, // использовать SSL
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD,
},
})
// Формируем текст письма
const serviceTypeText = params.serviceType
? getServiceTypeLabel(params.serviceType)
: 'Не указан'
const emailBody = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: #2563eb; color: white; padding: 20px; text-align: center; border-radius: 5px 5px 0 0; }
.content { background: #f9fafb; padding: 20px; border: 1px solid #e5e7eb; }
.field { margin-bottom: 15px; }
.label { font-weight: bold; color: #1f2937; }
.value { color: #4b5563; margin-top: 5px; }
.footer { margin-top: 20px; padding: 15px; text-align: center; font-size: 12px; color: #6b7280; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>🔔 Новая заявка с сайта Global-IT24</h2>
</div>
<div class="content">
<div class="field">
<div class="label">👤 Имя клиента:</div>
<div class="value">${params.name}</div>
</div>
<div class="field">
<div class="label">📱 Телефон:</div>
<div class="value"><a href="tel:${params.phone}">${params.phone}</a></div>
</div>
${params.email ? `
<div class="field">
<div class="label">📧 Email:</div>
<div class="value"><a href="mailto:${params.email}">${params.email}</a></div>
</div>
` : ''}
<div class="field">
<div class="label">⚙️ Тип услуги:</div>
<div class="value">${serviceTypeText}</div>
</div>
${params.message ? `
<div class="field">
<div class="label">💬 Сообщение:</div>
<div class="value">${params.message}</div>
</div>
` : ''}
</div>
<div class="footer">
Это автоматическое уведомление с сайта video.mscsrv.ru
</div>
</div>
</body>
</html>
`
// Отправляем письмо
const info = await transporter.sendMail({
from: `"Global-IT24 Website" <${process.env.SMTP_USER}>`,
to: process.env.NOTIFICATION_EMAIL || process.env.SMTP_USER,
subject: `🔔 Новая заявка от ${params.name}`,
html: emailBody,
})
console.log('Email sent:', info.messageId)
return { success: true, messageId: info.messageId }
} catch (error) {
console.error('Error sending email:', error)
return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }
}
}
function getServiceTypeLabel(type: string): string {
const labels: Record<string, string> = {
'general': 'Общая консультация',
'residential': 'Видеонаблюдение для дома',
'commercial': 'Коммерческие системы',
'industrial': 'Промышленное видеонаблюдение',
'maintenance': 'Обслуживание систем',
}
return labels[type] || type
}

View File

@@ -0,0 +1,83 @@
interface TelegramParams {
name: string
phone: string
email?: string | null
serviceType?: string | null
message?: string | null
}
export async function sendTelegramNotification(params: TelegramParams) {
try {
const botToken = process.env.TELEGRAM_BOT_TOKEN
const chatId = process.env.TELEGRAM_CHAT_ID
if (!botToken || !chatId) {
console.log('Telegram credentials not configured, skipping notification')
return { success: false, error: 'Telegram not configured' }
}
const serviceTypeText = params.serviceType
? getServiceTypeLabel(params.serviceType)
: 'Не указан'
// Формируем текст сообщения
let text = `🔔 <b>Новая заявка с сайта Global-IT24</b>\n\n`
text += `👤 <b>Имя:</b> ${escapeHtml(params.name)}\n`
text += `📱 <b>Телефон:</b> <code>${params.phone}</code>\n`
if (params.email) {
text += `📧 <b>Email:</b> ${escapeHtml(params.email)}\n`
}
text += `⚙️ <b>Тип услуги:</b> ${escapeHtml(serviceTypeText)}\n`
if (params.message) {
text += `\n💬 <b>Сообщение:</b>\n${escapeHtml(params.message)}`
}
// Отправляем сообщение через Telegram Bot API
const response = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
chat_id: chatId,
text: text,
parse_mode: 'HTML',
}),
})
const result = await response.json()
if (result.ok) {
console.log('Telegram notification sent successfully')
return { success: true, messageId: result.result.message_id }
} else {
console.error('Telegram API error:', result)
return { success: false, error: result.description || 'Unknown error' }
}
} catch (error) {
console.error('Error sending Telegram notification:', error)
return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }
}
}
function getServiceTypeLabel(type: string): string {
const labels: Record<string, string> = {
'general': 'Общая консультация',
'residential': 'Видеонаблюдение для дома',
'commercial': 'Коммерческие системы',
'industrial': 'Промышленное видеонаблюдение',
'maintenance': 'Обслуживание систем',
}
return labels[type] || type
}
function escapeHtml(text: string): string {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
}

View File

@@ -13,6 +13,7 @@
"devDependencies": {
"@next/swc-wasm-nodejs": "13.5.1",
"@types/node": "20.6.2",
"@types/nodemailer": "^7",
"@types/react": "18.2.22",
"@types/react-dom": "18.2.7",
"@typescript-eslint/eslint-plugin": "7.0.0",
@@ -92,6 +93,7 @@
"next": "14.2.28",
"next-auth": "4.24.11",
"next-themes": "0.3.0",
"nodemailer": "^7.0.10",
"plotly.js": "2.35.3",
"react": "18.2.0",
"react-chartjs-2": "5.3.0",