Данный сайт использует распространенный вариант хостинга с использование Github - при каждом изменении с помощью github-actions генерируется статичный сайт со всеми страницами. Из-за этого нет возможности использовать динамический контент напрямую, например, комментарии к статьям. Есть возможность использовать Disqus, но в таком случае появляется зависимость от сторонних сервисов.
Лучшим вариантом я считаю хранить комментарии у себя в репозитории, которые будут появляться на сайте в момент генерации сайта. Сервисов для реализации такого функционала очень много - как бесплатных, так и платных. Я остановлюсь на бесплатном варианте - Staticman от Eduardo Bouças, как наиболее продвинутом и удобном.
Как это работает:
- Посетитель блога отправляет комментарий, используя форму отправки под любым постом.
- В форме есть javascript, который отправляет результат на специальную API-прокладку, запущенную в виде приложения на Heroku. Если у пользователя отключен JS, то будет сформирован простой POST запрос.
- В свою очередь приложение производит валидацию комментария и, в случае успешного результата, создает pull request в репозиторий сайта, содержащий файл .yml с комментарием и мета данными.
- После принятия мной PR github автоматически генерирует новый сайт и комментарий появляется на сайте.
Сейчас у Staticman есть три версии API и основная сложность его подключения заключается в деплое приложения API-прокладки и настройки его. Мы будем использовать вторую версию, которая не сильно отличается от третьей (в третьей появилась возможность использовать Gitlab).
Создание аккаунта бота
Для создания pull request’ов в нашу репу используется гитхабовский personal access token, поэтому необходимо создать отдельный аккаунт для этого токена, так как этот токен будет использован для приложения Heroku, а, следовательно, может быть скомпрометирован и кто-то может получить доступ к вашему основному аккаунту. Кроме этого, использование второго аккаунта позволяет удобно разделить коммиты комментариев от ваших правок в репозитории сайта.
Аккаунт бота ничем не отличается от обычного аккаунта Github, поэтому запускаем вкладку в инкогнито режиме, чтобы не выходить из своего аккаунта и создаем новый аккаунт с именем, которое вам нравится, пусть для статьи это будет github-bot
. Можно сразу создать README.md в личной репе (название такое же как у аккаунта) и написать, что это бот:

Аккаунт нашего трудяги
После создания аккаунта нам необходимо сделать две вещи:
- Создать personal access token, для использования его в приложении.
- Выдать доступ к репозиторию сайта основного аккаунта.
Personal access token
Создание токена производится в настройках аккаунта в разделе Developer settings -> Personal access tokens. Там нажимаем Generate new token, вводим пароль для безопасности, придумываем название токена и выдаем разрешение к двум разделам - repo и user:

Нужные нам права доступа
После этого нажимаем Generate token и сохраняем где-нибудь отображенный токен - далее Github не даст его подсмотреть в целях безопасности. Далее в статье этот токен я будут обозначать как github-token
. Подробнее прочитать, что такое токен и для чего он может быть использован можно в официальной документации Github.
Доступ к репозиторию
Чтобы бот мог коммитить напрямую в наш репозиторий ему необходимо выдать права к репозиторию. Сделаем мы это через добавление бота в коллабораторы репы. Для этого переходим в настройки репы, там Manage access, а далее жмем кнопку Invite collaborator. В окошке вводим имя бота и отправляем приглашение.

Отправка приглашения
Все, боту придет письмо со кнопкой принятия приглашения - не нажимаем ее, так как принимать приглашение мы будем через наше приложение - это будет дополнительным этапом проверки правильности работы приложения.
Приложение API
Теперь нужно подготовить приложение для API-прослойки, которое будет принимать комментарии и создавать PR в наш репозиторий. Делать я это буду на Heroku, так как там имеется возможность использовать бесплатный инстанс, который по расчетам хватит примерно на ~1100 комментариев в месяц для аккаунта без кредитной карты (550 бесплатных часов) или ~4000 комментариев, если добавить кредитную карту в аккаунт (2000 бесплатных часов). Расчет таков - при отправке комментария приложение на Heroku просыпается и засыпает через 30 минут, таким образом в первом случае это 550/0.5
и 2000/0.5
во втором.
Деплой приложения в Heroku
После регистрации в Heroku нам необходимо создать свое приложение, проще всего это сделать из репозитория Staticman, где в README.md есть прямая ссылка не деплой:

Deploy приложения из репозитория Staticman
Второй вариант - использовать для деплоя прямую ссылку. Если сделали все правильно, то вместо стандартной формы создания приложения мы увидим создание приложения Staticman:

Создание нового приложения
Вводим имя приложения (далее я его буду обозначать как app-name
). Дожидаемся окончания деплоя и сборки нашего приложения, открываем его по кнопке Open app и убеждаемся, что ничего не запустилось 😀. Все потому, что приложению не хватает обязательных переменных, поэтому переходим к его настройке.
Генерация RSA ключа
Данный ключ нужен для шифрования секретных значений, которые выложены в публичный доступ, например, ваш секретный токен сервиса reCAPTCHA. Данные шифруются открытым ключом и попадают в публичный доступ, а API приложение расшифровывает их закрытым ключом. Если открыть логи приложения Staticman, то мы увидим, что отсутствие ключа как раз и мешает ему запуститься:

Ошибка запуска
Для генерации нового ключа используется команда:
# ssh-keygen -m PEM -t rsa -b 4096 -C "staticmankey" -f ~/.ssh/staticman_key
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in staticman_key.
Your public key has been saved in staticman_key.pub.
The key fingerprint is:
SHA256:VSMjbe....................iwPb67BXU8 staticmankey
Я не использовал пароль, так как после загрузки закрытого ключа в приложение Heroku его можно смело удалить - он нам больше не понадобится.
Проверить, что сгенирирован правильный ключ можно, открыв его в любом редакторе, либо же выполнить команду:
# head -2 ~/.ssh/staticman_key
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAyBStHD4kR9GjI9XZDf4V+QfNyuCbJRXde66WTRD3rDiyzaXC
Строка BEGIN RSA PRIVATE KEY
означает, что все сделано правильно, любой другой вариант (например, OPENSSH PRIVATE KEY
) означает, что что-то пошло не так и этот вариант не заработает.
Настройка приложения
После генерации ключа осталось настроить наше приложение, сделать это можно двумя путями - через web интерфейс, либо через командную строку.
В первом случае открываем дэшборд Heroku, выбираем наше приложение, далее Settings, там нас интересует раздел Config Vars. Жмем кнопку Reveal Config Vars и вводим две переменные:
RSA_PRIVATE_KEY
- наш приватный ключ, вносить его можно прямо из файла, можно удалять перенос строк, можно не удалять - разницы никакой, Staticman поймет его в любом случае.GITHUB_TOKEN
- наш personal access token ботаgithub-token
, который мы создали ранее
Если все правильно, то должно получится так:

Переменные приложения
Второй вариант добавления настроек - это через командную строку Heroku. Следуя инструкциям, нужно установить командную строку и выполнить вход, после этого станет доступен очень удобный вариант управления приложением. Для внесения наших переменных следует воспользоваться командами:
# heroku config:add --app app-name "RSA_PRIVATE_KEY=$(cat ~/.ssh/staticman_key | tr -d '\n')"
Setting RSA_PRIVATE_KEY and restarting ⬢ app-name... done, v9
RSA_PRIVATE_KEY: -----BEGIN RSA PRIVATE KEY-----MIIJKA.....
# heroku config:add --app app-name "GITHUB_TOKEN=github-token"
Setting GITHUB_TOKEN and restarting ⬢ app-name... done, v10
GITHUB_TOKEN: github-token
Обратите внимания, что вместо app-name
и github-token
следует использовать ваши значения. Проверить, что все правильно можно командой:
# heroku config --app app-name
Теперь открываем наше приложение по ссылке https://app-name.herokuapp.com
и мы должны увидеть приветствие Staticman:
Hello from Staticman version 3.0.0!
Если его нет, то значит в каком-то пункте вы ошиблись и стоит посмотреть логи Heroku. Теперь мы можем попробовать принять приглашение в репозиторий сайта, для этого нужно открыть ссылку в формате:
https://app-name.herokuapp.com/v2/connect/<main-user-account>/<site-repository>
В моем случае это
https://app-name.herokuapp.com/v2/connect/xbreaker/xbreaker.github.io
В ответ мы должны получить OK!
- это означает, что бот принял приглашение, а, следовательно, приложение мы настроили правильно. Если сайт отвечает Invitation not found
, то либо мы уже приняли приглашение ранее, либо забыли бота пригласить.
Еще один способ проверить правильность настроек - в настройках основного аккаунта в разделе Manage access, аккаунт бота перейдет в статус Collaborator:

Аккаунт бота с доступом
Настройка сайта
Пора подготовить наш сайт к использованию комментариев, для этого нам нужно выполнить четыре вещи:
- Создать файл настроек для Staticman
- Внести в настройки сайта URL нашего API-приложения
- Создать шаблон комментариев, который будет использован для генерации комментариев к постам
- Настройка и включение reCAPTCHA
Настройка Staticman
Подробно настройка описана в официальной документации, нам нужно создать файл staticman.yml
, полный пример заполнения можно найти здесь. Я же приведу сокращенный вариант, который настроен для этого сайта:
Поля thread
, parentName
, parent
используются для вложенных комментариев. Так же обратите внимание на настройки reCAPTCHA, если планируете ее использовать.
Настройки в config.yml
Здесь нам необходимо внести настройки Staticman, которые будут использоваться при отправке комментария - ссылку на API, плюс вариативные настройки сервиса reCAPTCHA. Настройки вносятся в файл config.eml
, у меня они расположены в params.staticman:
|
|
Ссылка на API должна быть вида:
https://app-name.herokuapp.com/v2/entry/<ваш github user>/<ваш github репозиторий>/main/comments
Обратите внимание на бранч вашего репозитория, для старых репозиториев это master, для новых - main.
Шаблон комментариев
Самый объемный пункт, так как нам нужно подготовить шаблон для своей темы, не забыть про JS, а так же про стили оформления. На моём сайте используется тема PaperMod, поэтому шаблон я буду делать для этой темы. Если вы используете другую тему, то адаптировать мой вариант будет не сложно.
Я не стал особо фантазировать, а решил использовать что-нибудь простое, интересный вариант нашелся здесь. Он послужил основой нашего шаблона, который был доработан под нужды статичного сайта - добавлен аватар и удалена голосовалка, так как в условиях статики ее нам не реализовать. Сам шаблон состоит из нескольких файлов, первым будет comments.html
- целиком я его приводить не буду, так как он слишком большой, найти его можно 🧾 здесь, я же остановлюсь на паре моментов.
Склонение окончания числительных, перед комментариями выводится их количество и, в зависимости от числа комментариев, меняется окончание слова “комментарий”. В шаблоне сейчас это выглядит очень громоздко, если кто-то подскажет более изящный вариант - буду очень благодарен.
|
|
Второй момент - это выделение комментариев автора блога от остальных. Чтобы это работало необходимо в параметры сайта внести параметр emailhash
, который содержит md5 хэш вашего почтового адреса. Чтобы его получить, заходим, например, сюда, вводим свою электронную почту и получившийся результат вносим в параметры:
|
|
Файл comments.html
следует скопировать в каталог \layouts\partials\
.
Второй файл - это файл стилей comments.css
, чтобы он попал в общий файл CSS стилей, придется пойти на хитрость - создать каталог assets\css\common\
и скопировать файл в данный каталог. Исходный код файла можно найти 🧾 здесь.
Вместе с файлом стилей нам нужен файл javascript логики, который добавит интерактивности нашим комментариям и форме отправки - comments.js
. Я использовал чистый JS (vanilla), чтобы исключить зависимость от какого-либо фреймворка. Интересный вариант отправки формы я взял отсюда и отсюда. Сам файл можно скопировать в каталог assets\js\
, подключим мы его в следующем абзаце. Исходный код файла забираем 🧾 здесь.
Чтобы подключить наш JS файл нам следует создать специальный файл темы extend_head.html
он позволяет добавить в HEAD сайта свои инструкции, чем мы и воспользуемся - добавим загрузку и сжатие нашего comments.js
. Кроме того, я добавил в него опциональную загрузку файла reCAPTCHA, если она включена в настройках. Копируем 🧾 содержимое и копируем его в каталог \layouts\partials\
.
Все файлы сразу расположены в этом 💾 gist, я специально не стал давать ссылки на репозиторий этого сайта, а выложил их отдельно. Так как в будущем файлы сайта могут измениться (или некоторые вообще могут быть удалены) и перестанут соответствовать тому, что здесь написано. А gist останется актуальным и нетронутым.
Настройка и включение reCAPTCHA
Выходим на финишную прямую, нам осталось подключить и настроить сервис reCAPTCHA. Данный сервис добавляет на сайт простой вариант проверки при отправке комментария и позволяет сильно снизить количество спам комментариев.
Для начала следует сходить на официальный сайт и зарегистрироваться. Нам нужна reCAPTCHA v2 Checkbox
версия, выбираем ее при регистрации:

reCAPTCHA v2 Checkbox
Получаем site key и secret key, которые будут использованы далее:

Наши секреты
Если site key сразу можно внести в настройки сайта и Staticman, то secret key не предназначен, чтобы передавать его в открытом виде. Поэтому его нужно зашифровать, поможет нам в этом наше ранее созданное приложение API.
Вызываем его по урлу вида:
https://app-name.herokuapp.com/v2/encrypt/secretKey
И результат, который оно возвратит и есть наш secret key для настроек. Сохраните обязательно где-нибудь оригинальный secret key на будущее.
Вносим настройки в config.yml
и staticman.yml
. Вот и все, готово 👍! Можно приступать к тестированию комментариев.

reCAPTCHA
Итого
В этом посте можно посмотреть результат реализации таких комментариев, создаваемые PR после отправки комментария можно найти в репозитории.
Если у вас есть что добавить или поправить в этой статье - можно вносить правки напрямую.
4 комментария
автор •
Первый тестовый комментарий 👍
автор •
Первый тестовый ответ на первый тестовый комментарий 💪🏻🎉🎉🎉
Очень круто получилось, есть еще вот такой вариант - utterances 🔮 Его минус в том, что обязательна авторизация через гитхаб, но выглядит не хуже 😉
автор •
Спасибо, посмотрел - прикольно. И спасибо за тестирование markdown в комментариях, нашел пару ошибок оформления в CSS.