Nodemailer
Установка
yarn add nodemailer react-hot-toast
Настройки
Использование
GMAIL_USER=АДРЕС_ПОЧТЫ@gmail.comGMAIL_PASS=ПАРОЛЬ
// Contact Formexport const form = { name: "Имя*", email: "Email*", tel: "Телефон*", commmets: "Запрос (необязательно)", button: "Отправить запрос",};
// Contact Form Alertsexport const alerts = { errorName: "Имя должно содержать от 3 до 20 символов", errorEmail: "Неверный формат электронной почты", errorTel: "Неверный формат телефона", errorCommmets: "Сообщение должно содержать от 10 до 500 символов", messageSuccess: "Сообщение успешно отправлено",};
"use client";
import { alerts, form } from "@/data/form";import { useState } from "react";import toast from "react-hot-toast"; // Документация по Toats тут: https://react-hot-toast.com
export default function ContactForm() { const [loading, setLoading] = useState(false); const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [tel, setTel] = useState(""); const [message, setMessage] = useState("");
const validateForm = () => { if (name.length < 3 || name.length > 20) { toast.error(alerts.errorName, { style: { borderRadius: "12px", background: "#0070BA", color: "#fff", padding: "12px 20px", }, iconTheme: { primary: "#fff", secondary: "#0070BA", }, }); return false; }
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { toast.error(alerts.errorEmail, { style: { borderRadius: "10px", background: "#0070BA", color: "#fff", }, iconTheme: { primary: "#fff", secondary: "#0070BA", }, }); return false; }
const telRegex = /^(?:(?:\(?(?:00|\+)([1-4]\d\d|[1-9]\d*)\)?)[\-\.\ \\\/]?)?((?:\(?\d{1,}\)?[\-\.\ \\\/]?)+)(?:[\-\.\ \\\/]?(?:#|ext\.?|extension|x)[\-\.\ \\\/]?(\d+))?$/i; if (!telRegex.test(tel)) { toast.error(alerts.errorTel, { style: { borderRadius: "12px", background: "#0070BA", color: "#fff", padding: "12px 20px", }, iconTheme: { primary: "#fff", secondary: "#0070BA", }, }); return false; }
if (message.length < 0 || message.length > 500) { toast.error(alerts.errorCommmets, { style: { borderRadius: "10px", background: "#0070BA", color: "#fff", }, iconTheme: { primary: "#fff", secondary: "#0070BA", }, }); return false; }
return true; };
const handleSubmit = async (e: any) => { e.preventDefault();
if (!validateForm()) { return; }
setLoading(true); const formData = { name, email, tel, message, };
const response = await fetch("/api/contact", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(formData), });
setLoading(false);
if (response.ok) { toast.success(alerts.messageSuccess, { icon: "Emoji-Icons", style: { borderRadius: "10px", background: "#0070BA", color: "#fff", }, }); e.target.reset(); // Сброс формы после успешной отправки setName(""); setEmail(""); setTel(""); setMessage(""); } else { console.error("Ошибка отправки сообщения"); toast.error("Ошибка отправки сообщения"); } };
return ( <div className="w-full"> <form onSubmit={handleSubmit} className="flex flex-col gap-6"> <input className="h-16 rounded-xl bg-[#DAEEFC] focus:bg-white duration-150 px-4 text-xl font-medium placeholder-slate-400 !outline-none !ring-0 border-0 focus:outline-none" name="name" type="text" placeholder={form.name} value={name} onChange={(e) => setName(e.target.value)} /> <input className="h-16 rounded-xl bg-[#DAEEFC] focus:bg-white duration-150 px-4 text-xl font-medium placeholder-slate-400 !outline-none !ring-0 border-0 focus:outline-none" name="email" type="email" placeholder={form.email} value={email} onChange={(e) => setEmail(e.target.value)} /> <input className="h-16 rounded-xl bg-[#DAEEFC] focus:bg-white duration-150 px-4 text-xl font-medium placeholder-slate-400 !outline-none !ring-0 border-0 focus:outline-none" name="tel" type="tel" placeholder={form.tel} value={tel} onChange={(e) => setTel(e.target.value)} /> <textarea className="rounded-xl bg-[#DAEEFC] focus:bg-white duration-150 px-4 text-xl font-medium placeholder-slate-400 !outline-none !ring-0 border-0 focus:outline-none" name="message" placeholder={form.commmets} rows={4} value={message} onChange={(e) => setMessage(e.target.value)} ></textarea> <button type="submit" className="h-16 rounded-xl bg-gradient-to-tr from-[#EF7D00] to-[#E6007E] px-12 text-xl font-medium text-white duration-150 hover:opacity-90 focus:outline-none inline-flex justify-center items-center" disabled={loading}> {loading ? "Загрузка..." : form.button} </button> </form> </div> );}
import ContactForm from "./components/ContactForm";import { Toaster } from "react-hot-toast";import { contact } from "@/data/content";
export default function ContactForm() { return ( <Toaster position="top-center" reverseOrder={true} /> <ContactForm /> );}
import { NextResponse } from "next/server";import nodemailer from "nodemailer";
export const POST = async (req) => { if (req.method !== "POST") { // res.status(405).json({ message: "Метод не разрешен" }); return new NextResponse( JSON.stringify({ message: "Метод не разрешен" }, { status: 405 }), ); } const { name, email, tel, message } = req.body; const user = process.env.GMAIL_USER;
const transporter = nodemailer.createTransport({ host: "smtp.gmail.com", port: 465, secure: true, service: "gmail", auth: { user: user, pass: process.env.GMAIL_PASS, }, }); try { const { name, email, tel, message } = await req.json(); const mail = await transporter.sendMail({ from: user, replyTo: email, to: "mail@mail.ru", // Изменените на вашу электронную почту subject: `Новое сообщение от ${name} на сайте Название Сайта`, html: ` <p>Имя: ${name}</p> <p>Email: ${email}</p> <p>Телефон: ${tel}</p> <p>Запрос: ${message}</p> `, }); console.log(name, email, tel, message); console.log("Сообщение отправлено:", mail.messageId); // res.status(200).json({ message: "Успех" }); return new NextResponse( JSON.stringify({ message: "Успех" }, { status: 200 }), ); } catch (error) { console.error("Ошибка", error); // res.status(500).json({ message: "Что-то пошло не так" }); return new NextResponse( JSON.stringify({ message: "Что-то пошло не так" }, { status: 500 }), ); }};
// Переименуйте свою функцию в POST и экспортируйте ее в таком виде