Email и Telegram уведомления о заявках
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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
16
nextjs_space/.env.example
Normal 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
|
||||
@@ -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
114
nextjs_space/lib/email.ts
Normal 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
|
||||
}
|
||||
83
nextjs_space/lib/telegram.ts
Normal file
83
nextjs_space/lib/telegram.ts
Normal 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, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user