Как автоматизировать рассылку электронных писем с помощью Strapi CRON Jobs

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

Автор: Алекс Годвин

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

Использование задач Strapi cron для автоматизации электронной почты — это мощная концепция. В этом руководстве мы создадим целевую страницу, которая позволит компании собирать лиды в виде электронных писем пользователей и отправлять автоматические электронные письма через заранее определенные промежутки времени.

Необходимые условия

  • Базовые знания Vue.js,
  • Знание JavaScript, и
  • Node.js (для strapi рекомендуется v14).

Готовая версия вашего приложения должна выглядеть так, как показано на рисунке ниже:

Введение в Strapi

В документации по Strapi говорится, что «Strapi — это гибкая безголовая CMS с открытым исходным кодом, которая дает разработчикам свободу выбора своих любимых инструментов и фреймворков, а редакторам — возможность легко управлять и распространять свой контент».

Strapi позволяет крупнейшим мировым компаниям ускорить доставку контента и создать красивый цифровой опыт, делая панель администратора и API расширяемыми с помощью системы плагинов.

Создание проекта Strapi

Чтобы установить Strapi, перейдите к документации по Strapi на сайте Strapi. Для этого проекта мы будем использовать базу данных SQLite. Чтобы установить Strapi, выполните следующие команды:

    yarn create strapi-app my-project # using yarn
    npx create-strapi-app@latest my-project # using npx
Войти в полноэкранный режим Выйти из полноэкранного режима

Замените my-project на имя, которым вы хотите назвать каталог вашего приложения. Ваш менеджер пакетов создаст каталог с указанным именем и установит Strapi.

Если вы правильно следовали инструкциям, Strapi должен быть установлен на вашей машине. Выполните следующие команды, чтобы запустить сервер разработки Strapi:

    yarn develop # using yarn
    npm run develop # using npm
Войти в полноэкранный режим Выйти из полноэкранного режима

Сервер разработки запустит приложение на http://localhost:1337/admin.

Создание типа коллекции списков рассылки

Давайте создадим тип коллекции Mailing-list, который будет содержать адреса электронной почты подписчиков:

  1. Нажмите на Content-Type Builder в разделе Plugins в боковом меню.
  2. В разделе collection types нажмите create new collection type.
  3. Создайте новый collection-type с именем Mailing-list .
  4. Создайте следующие поля в разделе *product content-type*:

Создание типа коллекции шаблонов электронной почты

Далее мы создадим тип коллекции Email-template, который будет моделью для рассылаемых нами писем:

  1. Нажмите на Content-Type Builder в Plugins в боковом меню.
  2. В разделе collection types нажмите create new collection type.
  3. Создайте новый collection-type с именем Email-template .
  4. Создайте следующие поля в разделе product content-type :

Это позволит нам создавать электронные письма, которые мы рассылаем нашим пользователям.

Введение в задания CRON

Задания Cron используются для различных целей. Задания Cron используются для планирования выполнения задач на сервере. Чаще всего они используются для автоматизации управления или обслуживания системы. Тем не менее, они также актуальны при создании веб-приложений. Веб-приложение может быть обязано регулярно выполнять определенные операции в различных обстоятельствах.

Зачем использовать задания Cron?

  • Если у вас есть сайт членства с датами истечения срока действия, вы можете использовать задания cron для регулярной деактивации или удаления учетных записей, срок действия которых истек.
  • У вас есть возможность отправлять ежедневные информационные письма.
  • Планирование действий, выполняемых на регулярной основе.

Структура заданий Cron

  '* * * * * *'


*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    |
│    │    │    │    │    └ day of week (0 - 7) (0 or 7 is Sun)
│    │    │    │    └───── month (1 - 12)
│    │    │    └────────── day of month (1 - 31)
│    │    └─────────────── hour (0 - 23)
│    └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, OPTIONAL)
Вход в полноэкранный режим Выйти из полноэкранного режима

Настройка заданий Cron в Strapi

Выполните следующие шаги, чтобы настроить задание cron в вашем приложении Strapi:

  1. Откройте файл config/server.js, затем добавьте следующие строки кода в конфигурацию сервера.
    cron: {
        enabled: true,
        tasks: cronTasks,
      }
Войти в полноэкранный режим Выйти из полноэкранного режима
  1. Создайте файл cron-task.js в папке config и добавьте следующее
    module.exports = {  
        '01 10 16 * * *': async ({ strapi }) => {
          console.log('cron running')
    }
Вход в полноэкранный режим Выйти из полноэкранного режима

Создать cron-job в Strapi очень просто.

Настройка провайдера электронной почты в Strapi

Мы установим @strapi/provider-email-nodemailer.

  1. Чтобы установить пакет, выполните следующие действия:
    npm i @strapi/provider-email-nodemailer
Войдите в полноэкранный режим Выйти из полноэкранного режима
  1. В config/plugins.js добавьте следующее:
    module.exports = ({ env }) => ({
        email: {
          config: {
            provider: 'nodemailer',
            providerOptions: {
              host: env('SMTP_HOST'),
              port: env('SMTP_PORT'),
              auth: {
                user: env('SMTP_USERNAME'),
                pass: env('SMTP_PASSWORD'),
              },
              pool: true,
              logger: true,
              debug: true,
              maxConnections: 10000
            },

            settings: {
              defaultFrom: env('DEFAULT_EMAIL'),
              defaultReplyTo: env('DEFAULT_EMAIL'),
            },
          },
        },
    });
Войти в полноэкранный режим Выйти из полноэкранного режима
  1. Создайте файл .env и добавьте в него свои учетные данные:

    SMTP_HOST=ВАШ_SMTP_HOST
    SMTP_PORT=465
    SMTP_USERNAME=ВАШЕ ИМЯ_SMTP_USERNAME
    SMTP_PASSWORD=ВАШ_SMTP_PASSWORD
    DEFAULT_EMAIL=ВАШ_ПОЧТОВЫЙ_АДРЕС

Вы можете использовать любого поставщика почтовых услуг по своему выбору.

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

Откройте файл cron-task.js и отредактируйте его содержимое, добавив следующие строки кода:

    const marked = require('marked')
    module.exports = {

        '01 13 15 * * *': async ({ strapi }) => {
          console.log('cron running')
          try {
            let emails = await strapi.service('api::email-template.email-template').find()

            emails = emails.results.reverse()

            const subscribers = await strapi.service('api::mailing-list.mailing-list').find()

            const content = marked.parse(emails[0].Content)

            await Promise.all(subscribers.results.map(async (el, i) => {  
              return await strapi
              .plugin('email')
              .service('email')
              .send({
                to: el.email,
                subject: 'Test mail',
                html: content,
              });
            }))

          } catch (error) {
            console.log(error)
          }
        },
      };
Войти в полноэкранный режим Выйти из полноэкранного режима

Вы можете установить задание CRON на любое время, которое вам нужно. Более того, согласно нашей логике, мы получим последнее письмо из нашего email-template и отправим его всем пользователям в нашем mailing-list.

Создание приложения, которое собирает электронные письма пользователей и добавляет их в наш список рассылки

Очевидно, что нам нужно средство для сбора фактических электронных писем для нашего списка рассылки. Мы построим внешний интерфейс нашего приложения, используя Vue 3. Vue — это JavaScript-фреймворк для создания пользовательских интерфейсов.

Выполните следующую команду для установки vue:

    mkdir client
    npm init vue@latest
Войдите в полноэкранный режим Выйти из полноэкранного режима

Дайте соответствующие ответы на подсказки, затем выполните следующие команды:

    cd <your-project-name>
    npm install
    npm run dev
Войти в полноэкранный режим Выйти из полноэкранного режима

Ваше приложение vue должно быть запущено на указанном порту. Откройте файл app.vue и обновите его следующим кодом:

    <script setup>
    import { RouterLink, RouterView } from 'vue-router'
    import HelloWorld from '@/components/HelloWorld.vue'
    </script>
    <template>
      <header>
        <div class="wrapper">
          <HelloWorld msg="Welcome to UoRos" />
          <nav>
            <RouterLink to="/">Home</RouterLink>
            <RouterLink to="/about">About</RouterLink>
          </nav>
        </div>
      </header>
      <RouterView />
    </template>
    <style>
    @import '@/assets/base.css';
    #app {
      max-width: 1280px;
      margin: 0 auto;
      padding: 2rem;
      font-weight: normal;
    }
    header {
      line-height: 1.5;
      max-height: 100vh;
    }
    .logo {
      display: block;
      margin: 0 auto 2rem;
    }
    a,
    .green {
      text-decoration: none;
      color: hsla(160, 100%, 37%, 1);
      transition: 0.4s;
    }
    @media (hover: hover) {
      a:hover {
        background-color: hsla(160, 100%, 37%, 0.2);
      }
    }
    nav {
      width: 100%;
      font-size: 12px;
      text-align: center;
      margin-top: 2rem;
    }
    nav a.router-link-exact-active {
      color: var(--color-text);
    }
    nav a.router-link-exact-active:hover {
      background-color: transparent;
    }
    nav a {
      display: inline-block;
      padding: 0 1rem;
      border-left: 1px solid var(--color-border);
    }
    nav a:first-of-type {
      border: 0;
    }
    @media (min-width: 1024px) {
      body {
        display: flex;
        place-items: center;
      }
      #app {
        display: grid;
        grid-template-columns: 1fr 1fr;
        padding: 0 2rem;
      }
      header {
        display: flex;
        place-items: center;
        padding-right: calc(var(--section-gap) / 2);
      }
      header .wrapper {
        display: flex;
        place-items: flex-start;
        flex-wrap: wrap;
      }
      .logo {
        margin: 0 2rem 0 0;
      }
      nav {
        text-align: left;
        margin-left: -1rem;
        font-size: 1rem;
        padding: 1rem 0;
        margin-top: 1rem;
      }
    }
    </style>
Вход в полноэкранный режим Выйти из полноэкранного режима

Откройте компонент HelloWorld.vue и обновите его следующим кодом:

    <script setup>
    defineProps({
      msg: {
        type: String,
        required: true
      }
    })
    </script>
    <template>
      <div class="greetings">
        <h1 class="green">{{ msg }}</h1>
        <h3>
          UoRos is a Futuristic company, invested in making sustainabile energy.
        </h3>
        <h3>
          <strong class="bold">
            Solar + Renewable energy
          </strong>
        </h3>
      </div>
    </template>
    <style scoped>
    h1 {
      font-weight: 500;
      font-size: 2.6rem;
      top: -10px;
    }
    h3 {
      font-size: 1.2rem;
    }
    .bold {
      font-weight: 900;
    }
    .greetings h1,
    .greetings h3 {
      text-align: center;
    }
    @media (min-width: 1024px) {
      .greetings h1,
      .greetings h3 {
        text-align: left;
      }
    }
    </style>
Вход в полноэкранный режим Выйти из полноэкранного режима

В компоненте TheWelcome.vue разместите следующий код:

    <script setup>
    import WelcomeItem from './WelcomeItem.vue'
    import DocumentationIcon from './icons/IconDocumentation.vue'
    import ToolingIcon from './icons/IconTooling.vue'
    import EcosystemIcon from './icons/IconEcosystem.vue'
    import CommunityIcon from './icons/IconCommunity.vue'
    import SupportIcon from './icons/IconSupport.vue'
    </script>
    <template>
      <WelcomeItem>
        <template #icon>
          <DocumentationIcon />
        </template>
        <template #heading>Vision</template>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Voluptatibus corrupti eveniet asperiores assumenda, sit quia eligendi porro exercitationem! Vitae ab veniam dolorum voluptates! Iste rerum molestiae nobis tenetur unde odit.
      </WelcomeItem>
      <WelcomeItem>
        <template #icon>
          <ToolingIcon />
        </template>
        <template #heading>Purpose</template>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Alias optio, facere velit officiis dolore doloremque ipsa minima, explicabo, fugiat placeat debitis repellat. Assumenda accusantium enim, aspernatur nemo illo ducimus quia.
        <br />
      </WelcomeItem>
      <WelcomeItem>
        <template #icon>
          <EcosystemIcon />
        </template>
        <template #heading>Ecosystem</template>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Architecto beatae officia culpa animi quas labore! Ducimus tempora, voluptatibus illo laudantium laborum repudiandae tempore labore fugit at excepturi dolore placeat minus?
      </WelcomeItem>
    </template>
Вход в полноэкранный режим Выйти из полноэкранного режима

Обновите компонент About.vue следующим кодом:

    <template>
      <div class="about">
        <div>
          <h1 class="heading">
            About UoRos
          </h1>
          <p class="desc_text">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Esse maiores velit molestias assumenda numquam eligendi. Perferendis pariatur, eligendi at nostrum odio ducimus quod consequatur dignissimos culpa magni commodi, quo esse!
          </p>
          <h1 class="heading">
            What we do
          </h1>
          <p class="desc_text">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Esse maiores velit molestias assumenda numquam eligendi. Perferendis pariatur, eligendi at nostrum odio ducimus quod consequatur dignissimos culpa magni commodi, quo esse!
            Lorem ipsum dolor, sit amet consectetur adipisicing elit. Placeat voluptatibus officia atque fuga nesciunt rem iste ab saepe ducimus, praesentium soluta quis temporibus, tempora voluptas facere quos reprehenderit beatae in!
          </p>
          <h1 class="heading">
            Join our mailing list
          </h1>
          <p class="desc_text">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Delectus eum assumenda dolor laudantium sed temporibus nulla sunt dicta voluptatibus asperiores harum officiis, at, quibusdam beatae corrupti. Suscipit placeat modi corrupti!
          </p>
          <div>
            <form action="" @submit="signUpToMail">
              <p>{{ error }}</p>
              <input type="email" class="email_input" name="email" placeholder="Email address" id="" v-model="email">
              <input type="submit" class="email_submit" value="Sign up">
            </form>
          </div>
        </div>
      </div>
    </template>
    <style>
    @media (min-width: 1024px) {
      .about {
        min-height: 100vh;
        display: flex;
        align-items: center;
      }
      .heading {
        /* margin: 10px 0; */
        font-weight: 700;
      }
      .desc_text {
        margin: 0 0 15px 0;
      }
      .email_input {
        padding: 12px 10px;
        font-size: 16px;
        border: none;
        background: rgb(232, 240, 254)
      }
      .email_submit {
        background-color: hsla(160, 100%, 37%, 1); /* Green */
        border: none;
        color: white;
        padding: 12px 10px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 16px;
        margin: 0 5px;
      }
    }
    </style>
    <script>
    import axios from 'axios'
    export default {
      data() {
        return {
          email: '',
          error: ''
        }
      },
      methods: {
        async signUpToMail(e) {
          e.preventDefault()
          if(!this.email) return this.error = `Enter an email address`
          const data = {
            data: {email: this.email}
          }
          console.log('Email', this.email)
          try {
            await axios(`http://localhost:1337/api/mailing-lists`, {
              method: 'POST',
              data
            })

          } catch (error) {
            console.log(error) 
          }

          this.email = ''
          this.error = ''
        }
      }

    }
    </script>
Войти в полноэкранный режим Выйти из полноэкранного режима

Этот компонент отвечает за вызов API к нашему серверу Strapi.

В старой документации по Strapi говорится об этом:

Проблема

Для повышения производительности (горизонтального масштабирования) вы можете развернуть приложение Strapi в нескольких контейнерах/кластерах/экземплярах. Это может быть, например, несколько кластеров pm2 или экземпляров Google App engine, в зависимости от хостинга.

Если вы развернули приложение Strapi в нескольких экземплярах, то каждый из них будет запускать задание cron, и тогда, например, у вас может возникнуть дублирование отправки писем, поскольку оно было запущено & выполнялось в нескольких экземплярах.

Решением этой проблемы может быть использование стороннего сервиса, который обрабатывает планирование с помощью, например, cron, а затем использует пользовательскую конечную точку для запуска (в одном экземпляре) пользовательского кода один раз.

Возможное решение

Вы можете создать пользовательский маршрут/контроллер, который будет выполнять ту же логику, что и crontask (возможно, реализовать API-токены для его защиты). Затем вы будете вызывать эту конечную точку из любой другой системы.

Это может быть встроенная система заданий CRON в Linux с простым запросом curl или использование более сложной системы cron на собственном хостинге. Вы даже можете использовать такие вещи, как Zapier или IFTTT, неважно, что это, лишь бы оно могло делать GET, POST или PUT запрос, который вы хотите (обычно GET).

Таким образом, конечная точка «crontask» будет выполняться только на одном узле кластера.

Заключение

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

Оцените статью
devanswers.ru
Добавить комментарий