Русский
Русский
English
Статистика
Реклама

Реализация подписки на обновления с помощью Google Sheets, Netlify Functions и React. Часть 2


Это вторая часть туториала, посвященного реализации Real World App подписки на обновления с помощью гугл таблиц, бессерверных функций и реакта.


Вот ссылка на первую часть.


Напомню, что основной функционал нашего приложения, который мы реализовали в первой части туториала, является следующим:


  • на главной странице отображается приветствие и предложение подписаться на обновления
  • при нажатии на кнопку Подписаться, пользователь попадает на страницу с формой, содержащей два поля: имя и адрес электронной почты
  • для защиты от ботов используется гугл рекапча 2 версии
  • при заполнении полей и прохождения проверки снимается блокировка с кнопки Подписаться
  • при нажатии этой кнопки данные пользователя отправляются в таблицу с помощью бессерверной функции

Дополнительный функционал, реализацией которого мы займемся в этой части:


  • с помощью скрипта осуществляется автоматическая рассылка уведомлений
  • в рассылаемых письмах содержится ссылка на отписку от обновлений
  • при переходе по этой ссылке адрес электронной почты передается бессерверной функции, с помощью которой из таблиц удаляется соответствующая строка

Демо приложения, разработкой которого мы занимаемся, можно посмотреть здесь (оно вполне работоспособное, если хотите, можете подписаться на обновления).


Код приложения находится здесь.


Для реализации приложения используются следующие технологии:


  • netlify-cli интерфейс командной строки для запуска сервера для разработки (инициализации бессерверных функций) и "деплоя" приложения на Netlify; требуется глобальная установка: yarn global add netlify-cli или npm i -g netlify-cli; обязательно
  • google-spreadsheet JavaScript-библиотека для работы с гугл таблицами; обязательно
  • react на мой взгляд, это лучший JavaScript-фреймворк для фронтенда, но вы можете использовать любую другую библиотеку; наши бессерверные функции не зависят от конкретного фреймворка
  • react-router-dom React-библиотека для маршрутизации
  • semantic-ui-react React-CSS-фреймворк
  • react-google-recaptcha React-компонент, позволяющий напрямую взаимодействовать с соответствующим сервисом
  • nodemailer наиболее популярная Node.js-библиотека для работы с электронной почтой (рассылки писем)
  • dotenv утилита для доступа к переменным среды окружения

Начнем с деплоя приложения на Netlify.


Деплой приложения


Заходим на Netlify, создаем аккаунт, затем вводим в терминале следующую команду:


netlify login

Вводим логин и пароль, получаем сообщение об успешной авторизации.


Выполняем сборку проекта:


yarn build# илиnpm run build

И разворачиваем приложение в тестовом режиме:


netlify deploy

Отвечаем на вопросы (новое приложение, название приложения (например, mail-list), директория для деплоя (build) и т.д.), получаем ссылку на развернутое приложение.


Переходим по ссылке, видим, что приложение не работает. Почему? Потому что мы не добавили переменные среды окружения.


Переходим в раздел sites, открываем наше приложение, выбираем вкладку Site settings, затем вкладку Build & deploy, находим раздел Environment, добавляем переменные (Environment variables).




Не будем ходить вокруг да около, а сразу развернем приложение в продакшн-режиме:


netlify deploy --prod

Готово. Легко, правда? Вот за что я люблю Netlify.


Теперь, когда у нас имеется URL, мы можем зарегистрировать наше приложение в Google ReCAPTCHA.


Заходим в консоль администратора и создаем новое приложение (+). Вводим название сайта (ярлык), выбираем reCAPTCHA v2, указываем домен (URL нашего приложения без протокола), принимаем условия использования (флажок "Отправлять владельцам оповещения" можно снять), нажимаем "Отправить". Получаем ключ сайта и секретный ключ, нам нужен только первый.





Добавляем в .env такую переменную:


REACT_APP_GOOGLE_RECAPTCHA_SITE_KEY=YOUR_SITE_KEY

Вносим изменение в Subscribe.js:


<ReCAPTCHAsitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITE_KEY}onChange={() => setRecaptcha(true)}/>

Повторно собираем и разворачиваем приложение:


yarn build# илиnpm run build# иnetlify deploy --prod

Если все сделано правильно, то на странице с формой появится настоящая капча.



Отлично.


Переходим к автоматизации рассылки уведомлений и реализации возможности отписаться от обновлений.


Автоматическая рассылка уведомлений


Давайте подумаем о том, что нам нужно для того, чтобы пользователь имел возможность отписаться от обновлений.


Мы, например, могли бы добавлять в уведомление специальную ссылку, при переходе по которой выполняется отписка. Для идентификации пользователя, выразившего такое желание, нам нужен его адрес электронной почты. Следовательно, ссылка должна формироваться из пути к соответствующей странице нашего приложения + email пользователя (для того, чтобы мы могли извлекать его из параметров строки запроса).


Для реализации автоматической рассылки уведомлений мы будем использовать nodemailer, а для тестирования Mailtrap.


Создаем аккаунт на Mailtrap, открываем автоматически созданный проект MyInbox, на вкладке SMTP Settings в разделе Integrations выбираем Node.js -> Nodemailer, получаем данные для авторизации.




Сохраняем эти данные в .env:


SMTP_USER='USER'SMTP_PASS='PASS'

В корне проекта создаем директорию send-mail, а в ней index.js следующего содержания:


require('dotenv').config()const nodemailer = require('nodemailer')const { GoogleSpreadsheet } = require('google-spreadsheet')const doc = new GoogleSpreadsheet(process.env.GOOGLE_SPREADSHEET_ID)// Тестовый транспортер для отправки сообщенийconst testTransporter = nodemailer.createTransport({host: 'smtp.mailtrap.io',port: 2525,auth: {user: process.env.SMTP_USER,pass: process.env.SMTP_PASS}})// Функция для создания сообщения в формате HTML// Она принимает имя пользователя и его email// Обратите внимание на значение атрибута `href` тега `a` -// URL соответствующей страницы нашего приложения (скоро мы ее создадим) + email пользователяconst createMessage = (username, email) => `<p><strong>Уважаемый ${username} </strong>, <em>спасибо за подписку</em>!</p><p>Для того, чтобы отписаться от обновлений, перейдите по <a href="http://personeltest.ru/aways/mail-list.netlify.app/unsubscribe/${email}" target="_blank">этой ссылке</a></p>`const sendMail = async () => {try {await doc.useServiceAccountAuth({client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,private_key: process.env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n')})await doc.loadInfo()const sheet = doc.sheetsByIndex[0]const rows = await sheet.getRows()// Перебираем строки таблицы  данные пользователей,// создаем сообщение и отправляем его// text  резервный контент на случай, если почтовый клиент пользователя не поддерживаем сообщения в формате HTMLrows.forEach(async (row) => {await testTransporter.sendMail({from: 'Mail list <mail-list.netlify.app>',to: row.email,subject: 'Благодарность за подписку',text: 'Спасибо за подписку',html: createMessage(row.username, row.email)})})console.log('Сообщения отправлены')} catch (err) {console.error(err)}}sendMail()

Добавим в package.json (раздел scripts) команду для рассылки уведомлений:


send: node send-mail/index.js

Запускаем скрипт (разумеется, в таблице должны быть какие-то данные):


yarn send# илиnpm run send

Получаем Сообщения отправлены в терминале и письмо в Mailtrap.



Для взаимодействия с реальными почтовыми службами (yahoo в моем случае) нужен реальный SMTP-провайдер.


Среди наиболее популярных решений можно назвать SendGrid и SendingBlue, но в случае выбора одного из этих сервисов, нам придется долго и упорно убеждать их владельцев в том, что мы не собираемся заниматься рассылкой спама. Еще есть Mailgun, но он платный с трехмесячным free trial.


Поэтому мы будем использовать Gmail.


Безусловно, если очень хочется, можно поднять собственный SMPT-сервер. Также существуют инструменты для рассылки писем, которые, как заявляют их разработчики, работают без SMTP, например, sendmail.


Добавляем в .env переменные с данными вашего Gmail-аккаунта:


GMAIL_USER='USER'GMAIL_PASS='PASS'

И вносим изменения в send-mail/index.js:


/*const testTransporter = nodemailer.createTransport({host: 'smtp.mailtrap.io',port: 2525,auth: {user: process.env.SMTP_USER,pass: process.env.SMTP_PASS}})*/const gmailTransporter = nodemailer.createTransport({service: 'gmail',auth: {user: process.env.GMAIL_USER,pass: process.env.GMAIL_PASS}})rows.forEach(async (row) => {await gmailTransporter.sendMail({// ...})})

Запускаем скрипт (в таблице должен быть указан ваш email):


yarn send


Существует один нюанс, связанный с использованием Gmail в качестве сервиса для рассылки писем гугл может блокировать к нему доступ, считая приложение ненадежным (существует платная версия Gmail Google Workspace, которая с точки зрения гугла, конечно же, является надежной).


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



С реализацией автоматической рассылки мы закончили, но при переходе по ссылке, указанной в письме, мы попадаем на резервную страницу, поскольку в нашем приложении нет страницы для отписки от обновлений, как и соответствующей бессерверной функции. Давайте это исправим.


Отписка от обновлений


Добавляем в приложение (src/pages) новую страницу Unsubscribe.js. На этой странице после скрытия индикатора загрузки, мы пытаемся получить email пользователя из параметров строки запроса с помощью хука useParams. Если email отсутствует, выполняется перенаправление на главную страницу. Иначе мы отправляем email в функцию, которая удаляет из таблицы соответствующую строку. Если пользователь с указанным email не оформлял подписку на обновления, выбрасывается исключение. При успешном завершении операции отображается сообщение о том, что пользователь больше не будет получать уведомлений.


import { useState, useEffect } from 'react'import { Link, useParams, useHistory } from 'react-router-dom'import { Container, Button } from 'semantic-ui-react'import { Spinner, useDeferredRoute } from '../hooks'function Unsubscribe() {const { loading } = useDeferredRoute(1000)const [error, setError] = useState(null)// Извлекаем email из параметров строки запросаconst { email } = useParams()const history = useHistory()useEffect(() => {// Если email отсутствует, выполняем перенаправление на главную страницуif (!email) {return history.push('/')}async function unsubscribe() {try {// Отправляем email в функциюconst response = await fetch('/.netlify/functions/unsubscribe', {method: 'POST',body: JSON.stringify(email),headers: {'Content-Type': 'application/json'}})// Если возникла ошибка, значит, пользователь не оформлял подпискуif (!response.ok) {const json = await response.json()setError(json.error)}} catch (err) {console.error(err)}}unsubscribe()// eslint-disable-next-line}, [])if (loading) return <Spinner />return (<Container>{error ? (<h3>{error}</h3>) : (<h3>Вы больше не будете получать уведомлений</h3>)}<Button color='teal' as={Link} to='/'>На главную</Button></Container>)}export default Unsubscribe

Все, что нам осталось сделать, это реализовать функцию unsubscribe. Она очень похожа на функцию subscribe загружается таблица, выполняется поиск и удаление соответствующей строки:


require('dotenv').config()const { GoogleSpreadsheet } = require('google-spreadsheet')exports.handler = async (event) => {const doc = new GoogleSpreadsheet(process.env.GOOGLE_SPREADSHEET_ID)try {await doc.useServiceAccountAuth({client_email: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL,private_key: process.env.GOOGLE_PRIVATE_KEY.replace(/\\n/g, '\n')})await doc.loadInfo()const sheet = doc.sheetsByIndex[0]// Получаем email пользователяconst data = JSON.parse(event.body)const rows = await sheet.getRows()// Выполняем поиск соответствующей строкиconst index = rows.findIndex((row) => row.email === data)// Если строка не найдена, значит, пользователь не оформлял подпискуif (index === -1) {const response = {statusCode: 400,body: JSON.stringify({error: 'Пользователь с указанным email не найден'}),headers: {'Access-Control-Allow-Origin': '*','Access-Control-Allow-Credentials': 'true'}}return response}// Удаляем строкуawait rows[index].delete()const response = {statusCode: 200,body: JSON.stringify({ message: 'Пользователь удален' }),headers: {'Access-Control-Allow-Origin': '*','Access-Control-Allow-Credentials': 'true'}}return response} catch (err) {console.error(err)const response = {statusCode: 500,body: JSON.stringify({ error: 'Что-то пошло не так. Попробуйте позже' }),headers: {'Access-Control-Allow-Origin': '*','Access-Control-Allow-Credentials': 'true'}}return response}}

Еще раз (обещаю, что в последний) собираем и разворачиваем проект:


yarn build# илиnpm run build# иnetlify deploy --prod

Не забудьте обновить переменные среды окружения на Netlify.


Теперь при переходе по ссылке, указанной в письме, пользователь попадает на страницу с сообщением о том, что он успешно отписался от обновлений, а в таблице удаляется соответствующая строка. При переходе на данную страницу без указания email выполняется перенаправление на главную страницу, а при указании несуществующего email отображается сообщение о том, что пользователь с таким email не найден.




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


Как бы то ни было, если вы в точности следовали инструкциям, а еще лучше реализовали какие-то дополнительные возможности, то в вашем портфолио появилось настоящее Real World App, разработанное с использованием самых современных технологий (да, мы не использовали TypeScript, но для нашего небольшого проекта это было бы слишком круто).




Облачные серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Источник: habr.com
К списку статей
Опубликовано: 08.06.2021 12:19:01
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании маклауд

Reactjs

Serverless

Vds

Vps

Быстрый vps

Дешевый vds

Google spreadsheets

Подписка

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru