Внешний интерфейс: использование Gatsby.js и Node.js для статических обновлений сайта
Опубликовано: 2022-03-11Для некоторых требований проекта генерация статической страницы не только достаточна , но и наиболее эффективна с точки зрения скорости и масштабируемости.
В первой части этой серии мы объединили Node.js, Express, MongoDB, cron и Heroku, чтобы создать серверную часть, готовую к CI, которая использует API GitHub в соответствии с ежедневным расписанием. Теперь мы готовы добавить Гэтсби в смесь, чтобы завершить наш проект создания статической страницы.
Разработка внешнего интерфейса: сделайте его веб-сайтом Gatsby
Поскольку веб-сайты Gatsby основаны на React, будет полезно, если вы уже знакомы с тем, как создать веб-сайт с помощью React.
Для стилизации я предпочел Bootstrap, reactstrap и react-markdown. Возможно, вы знаете, что примечания к выпуску в GitHub хранятся в формате Markdown, поэтому нам нужен конвертер.
Структура проекта статического веб-сайта
Наша структура файлов/папок будет следующей:
Для чего эти файлы? Давайте посмотрим:
-
env.development
иenv.production
— это файлы конфигурации переменных среды. - Шаблон
all-repositories.js
будет использоваться для нашей домашней страницы, содержащей список репозиториев. - Шаблон
repository.js
будет использоваться для отображения сведений о данном репозитории. -
gatsby-node.js
— это место, где мы используем нашу внутреннюю конечную точку и запускаем наши методыcreatePage
. -
package.json
, как всегда, содержит зависимости и свойства проекта. -
global.css
— наш основной файл таблицы стилей.
Реализация веб-сайта Гэтсби
Как и в случае с нашей серверной частью, запустите npm install
(или yarn
, если у вас установлен Yarn) после добавления необходимых зависимостей в package.json
интерфейса:
{ // ... "dependencies": { "axios": "^0.18.0", "bootstrap": "^4.3.1", "gatsby": "^2.0.0", "react": "^16.5.1", "react-dom": "^16.5.1", "react-markdown": "^4.0.6", "reactstrap": "^7.1.0" } // ... }
env.development
и env.production
имеют только внутренние URL-адреса соответствующих сред. У первого есть:
API_URL=http://localhost:3000
…в то время как производство имеет:
API_URL=https://[YOUR_UNIQUE_APP_NAME].herokuapp.com
gatsby-node.js
будет запускаться только один раз, пока вы создаете свой код. Таким образом, мы должны собрать всю необходимую информацию из нашей серверной части здесь. Затем ответ будет использоваться с нашими шаблонами для создания соответствующих статических страниц.
В нашем случае all-repositories.js
нужны все репозитории, а repository.js
нужен соответствующий репозиторий для каждой итерации. Наконец, мы можем динамически генерировать пути к страницам, передавая owner
репозитория и параметры name
как часть поля path
:
const axios = require('axios'); require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` }); const getRepositoryData = async () => { console.log(process.env.API_URL); return axios.get(`${process.env.API_URL}/repositories`); }; exports.createPages = async ({ actions: { createPage } }) => { let repositories = await getRepositoryData(); repositories = repositories.data; // Create a page that lists all repositories. createPage({ path: `/`, component: require.resolve('./src/templates/all-repositories.js'), context: { repositories } }); // Create a page for each repository. repositories.forEach(repository => { createPage({ path: `/repository/${repository.owner}/${repository.name}`, component: require.resolve('./src/templates/repository.js'), context: { repository } }); }); };
Для all-repositories.js
и repository.js
, если мы опустим стили, мы просто собираем данные из pageContext
и используем их так же, как мы используем параметры в React.
В all-repositories.js
мы будем использовать поле repositories
в pageContext
следующим образом:
export default ({pageContext: {repositories}}) => ( // ... <ListGroup> {/* Because the repositories parameter is a list, we are iterating over all items and using their fields */} {repositories.map(repository => (repository.tagName && <ListGroupItem className="repository-list-item"> // ... <Row> {`${repository.repositoryDescription}`} </Row> // ... </ListGroupItem> ))} </ListGroup> // ... );
Что касается repository.js
, вместо этого мы будем использовать поле repository
pageContext
:
export default ({pageContext: {repository}}) => ( <div className="layout"> {repository.tagName && <ListGroupItem className="repository-list-item"> // ... <h1 className="release-notes">{`Release notes`}</h1> <hr/> {/* This the place where we will use Markdown-formatted release notes */} <ReactMarkdown source={`${repository.releaseDescription}`}/> </ListGroupItem> } // ... </div> );
Теперь убедитесь, что ваш сервер запущен и работает. Вы помните, что для этого проекта мы установили http://localhost:3000.
Затем запустите gatsby develop
из корня вашего внешнего проекта и откройте http://localhost:8000.
Если вы добавили несколько репозиториев (владелец/имя) в свою серверную часть и запустили функцию обновления через GitHub-API хотя бы один раз, вы должны увидеть что-то вроде этого:
И после нажатия на один из репозиториев вы должны увидеть что-то вроде этого:
После внесенных выше изменений наша интерфейсная реализация завершена.
Здорово! Теперь нам осталось только развернуться.
Развертывание внешнего интерфейса
В этой части нам не нужно вносить какие-либо изменения в наше внешнее приложение. Мы просто развернем его на Netlify — вам понадобится учетная запись там.
Но сначала нам нужно развернуть наш код на GitHub, GitLab или Bitbucket. Как и в случае с серверной частью, я развернул свой код на GitHub.
Затем войдите в Netlify и нажмите кнопку «Новый сайт из Git» на панели инструментов. Следуйте инструкциям на экране, чтобы выбрать поставщика Git и найти свой репозиторий.
На последнем этапе, если вы правильно структурировали свой код, Netlify автоматически установит команду сборки и каталог публикации следующим образом:

Затем нажмите «Развернуть сайт». Он развернет ваш сайт на случайно сгенерированном субдомене Netlify, но вы можете изменить его в любое время — я изменил свое развертывание на https://sample-create-page-api-gatsby.netlify.com, где вы можете найти живую демонстрацию готового приложения.
Все идет нормально. Но поскольку это приложение со статической страницей, нам приходится ежедневно перестраивать его, чтобы поддерживать его в актуальном состоянии.
Ежедневное обновление с помощью Build Hook
Перехватчики сборки в Netlify работают как триггеры сборки, поэтому вы можете запускать их из своего бэкэнда после завершения задания cron. Для этого сначала создайте хук сборки в Netlify.
В разделе «Сборка и развертывание → Непрерывное развертывание» вы можете найти «Зацепки сборки». Нажмите «Добавить хук сборки».
Дайте ему имя и сохраните его. (Я назвал свой build-from-backend
.) Затем скопируйте созданную им ссылку.
Теперь давайте откроем наш серверный проект и добавим эти несколько строк в файл cron.controller.js
. Мы просто отправляем запрос POST
на URL-адрес сборки Netlify.
const Axios = require('axios'); const Config = require('../config/env.config'); const NETLIFY_BUILD_HOOK_URI = Config.netlifyEndpoint; function updateGatsby() { if (NETLIFY_BUILD_HOOK_URI) { console.log('Gatsby build request will be send'); Axios.post(NETLIFY_BUILD_HOOK_URI).then(() => { console.log('Gatsby build request was successful'); }); } }
Затем обновите нашу функцию updateDaily
:
function updateDaily() { RepositoryController.updateRepositories().then(() => { updateGatsby(); }); }
Наконец, обновите наш файл env.config.js
, чтобы установить свойство netlifyEndpoint
, которое будет собираться из переменных среды:
"netlifyEndpoint": process.env.NETLIFY_BUILD_HOOK || ""
Теперь вам нужно установить переменную среды NETLIFY_BUILD_HOOK
, которую вы только что скопировали из Netlify. В Heroku вы можете установить переменные среды в разделе «Настройки» вашего приложения.
После отправки вашего коммита серверное приложение отправит запрос на сборку в Netlify, и ваши страницы будут обновляться ежедневно в конце вашего задания cron. Вот как должно выглядеть репозиторий после добавления функциональности сборочного хука, а также окончательные версии внутреннего репозитория и внешнего репозитория.
В качестве завершающего штриха проекта мы покажем, как использовать функцию AWS Lambda, активируемую AWS CloudWatch, которая своевременно активирует серверную часть для каждого ежедневного обновления.
Простой запрос AWS Lambda и AWS CloudWatch
AWS Lambda — это бессерверная вычислительная платформа. Для наших целей нам нужны только самые основы этой платформы. Вам потребуется учетная запись AWS.
Сначала войдите в AWS под своей учетной записью и найдите Консоль управления Lambda. Например, для us-east-2
его можно найти по адресу https://us-east-2.console.aws.amazon.com/lambda/home.
Если он еще не выбран, перейдите в раздел «Функции»:
Здесь мы создадим нашу функцию с нуля, нажав «Создать функцию». Дадим ему объяснительное название. Мы будем использовать Node.js в качестве языка выполнения. Затем нажмите следующую «Создать функцию», чтобы завершить ее.
Он перенаправит нас на страницу новой функции, где мы сможем написать наш код в index.js
.
Давайте реализуем нашу первую лямбда-функцию. Поскольку у нас нет сторонних зависимостей, мы должны использовать основные модули Node.js. (Если вместо этого вы хотите включить сторонние зависимости, следуйте этому руководству от AWS.)
Убедитесь, что имя экспортируемого метода (в данном случае handler
) соответствует параметру «Обработчик» на странице:
Остальное — это простой GET
-запрос к вашему серверу:
const https = require('https'); exports.handler = async (event) => { return new Promise((resolve, reject) => { https.get(process.env.HEROKU_APP_URL, (resp) => { let data = ''; resp.on('data', (chunk) => { data += chunk; }); resp.on('end', () => { resolve(JSON.parse(data)); }); }).on("error", (err) => { reject("Error: " + err.message); }); }); };
Убедитесь, что вы установили переменную среды HEROKU_APP_URL
на странице и сохраните конфигурацию:
Последний шаг — добавление триггерного правила CloudWatch для запуска этой функции за десять минут до каждого ежедневного обновления — в этой серии статей это работает до 23:50:
Нашим типом триггерного правила CloudWatch будет «Выражение расписания», и, в соответствии с принятым форматом, наше выражение cron будет cron(50 23 * * ? *)
.
Теперь мы настроили нашу функцию AWS Lambda так, чтобы она запускалась нашим правилом CloudWatch.
Теперь на наших статических веб-страницах: Gatsby/React и Netlify
С добавлением AWS Lambda/CloudWatch в нашу серверную часть Node.js/MongoDB/Heroku, а также созданием и размещением интерфейса Gatsby и Netlify, наше приложение готово!
Ранее я поделился ссылкой на демонстрационную версию, но не стесняйтесь также проверить расширенную версию моего прототипа — в ней есть некоторые дополнительные изменения, которые, я уверен, вам понравятся.
Вы можете использовать это в качестве плана для подобных проектов — я надеюсь, что эти статьи помогут вам создавать прототипы ваших приложений более быстрым и экономичным способом. Спасибо за прочтение!