Опять вебменшены (и не только)

Блог без комментов — что борщ без сметаны.

До недавнего времени вебменшены у меня на сайте показывались посредством webmention.js (который я настоятельно рекомендую в качестве самого простого варианта тем, кто ещё не), но решение это всегда предполагалось временным — хотя бы потому, что не хочется быть привязанным к webmention.io. В качестве первого шага к независимости от последнего я этим летом как раз и переделал отображение вебменшенов. Внешне всё почти по-прежнему (так, собственно, и задумывалось), но «под капотом»…

Один из вопросов, немедленно возникающих при первых мыслях о написании своего webmention-endpoint: «Как хранить?» Сразу появляются мысли о базах данных, их преимуществах и недостатках, но, если мыслить в эту сторону, проще уж поднять у себя тот же webmention.io (он же опенсорцный) и не заморачиваться. Но у меня сайт на Hugo, и тот факт, что всё хранится в текстовых файлах в одном git-репозитории, меня очень радует. Логичный вывод: вебменшены надо хранить так же.

Катализатором стал пост о внедрении вебменшенов в Hugo-сайт: в самом деле, пусть сервер ходит время от времени на webmention.io и сохраняет новые вебменшены в JSON, а мы уж тут как-нибудь. Идея хранить их в отдельном файле в /data/ была немедленно забракована: на то и Page Bundles, чтобы хранить для каждого поста его содержание (в index.md) и полученные к нему вебменшены (в webmentions.json) в одной директории. Те вебменшены, что прилетели в адрес сайта в целом (например, person mentions), можно хранить в webmentions.json прямо в директории /content/, и пока не отображать…

Автор упомянутого поста написал сохранение на JS и запускает с Netlify, но у нас тут никаких нетлифаев не водится, так что «сохранялка» нужна отдельная — благо, она уже была написана, надо только добавить опцию для сохранения в соответствующие директории. Сказано — сделано, заодно и ещё полдюжины опций разных дописал, пока ковырялся (не знаю, нужны ли они кому-то, но в процессе отладки «само возникло» — пусть будет)1. Плюс — код, который отвечает за сохранение в поддиректории, наверняка пригодится потом, когда буду писать webmention-endpoint.

Первый затык: на части страниц (а в секции «Реакции» — почти на всех) только текст, и для такой страницы создавать Bundle было, конечно, лень. В Hugo содержимое страницы с адресом https://evgenykuznetsov.org/posts/2021/old-comments/ может быть либо в /content/posts/2021/old-comments.md, либо в /content/posts/2021/old-comments/index.md (во втором случае в эту же директорию можно сложить картинки и прочее, что на этой странице потребуется — это и называется «Page Bundle»). Если я собираюсь хранить вебменшены в Bundle, первый вариант мне совсем не годится, а таких страниц у меня накопилось изрядное количество. Надо сохранить их все по второму варианту, каждую в своей поддиректории. Не вручную, конечно, а то всё лето и уйдёт — особенно с учётом языковых вариантов. Впрочем, у нас тут Linux: бери bash и действуй, как говорится:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
set -e

wd=$(pwd)

if [[ "$1" != "" ]]; then
        wd="$1"
fi

for x in "$wd"/*.md; do
        case "$x" in 
                *.ru.md)    suffix="ru.md";;
                *.en.md)    suffix="en.md";;
                *.md)       suffix="md";;
                *)          continue;;
        esac
        name="${x%%.*}"
        if [ ! -d "$name" ]; then
                mkdir "$name"
        fi
        mv "$x" "$name/index.$suffix"
done

…и да здравствует Stack Overflow!

Дальше — проще. Убрал из шаблонов webmention.js, написал обработку JSON’а из бандла (во многом — тот же webmention.js, переведённый с JavaScript на язык шаблонов Hugo и доработанный «под себя»), «причесал» CSS (теперь знаю о CSS/SaSS сильно больше, чем в июне), написал новый шаблон для RSS-лент комментариев. Заодно разметил динамическую подгрузку аватаров и имён; при этом вылезли баги в indieweb-glue (некоторые вещи не кешировались, хотя стоило, особенно 404-е ответы, в которых к тому же не прописывался CORS-заголовок) — починил тоже. Всё работает. Правда, теперь вебменшены отображаются не сразу после получения, а после следующей пересборки сайта, зато теперь я, при желании, могу легко организовать пре-модерацию, например.

Ой, так я ж теперь могу и старые вебменшены рисовать, те, которые ещё на Known получал, я ведь при переезде с Known на Hugo их сохранил, причём именно в JSON, в том же формате, в котором они отдаются webmention.io! Интересно, а что с аватарами за напасть? Ах, вон оно что: Known, принимая вебменшены, сохранял аватары у себя, и ссылки стоят на evgenykuznetsov.org/service/web/imageproxy/, которого больше и не существует вовсе. Надо эти ссылки поубирать. Не вручную, понятное дело: у нас ведь есть jq и особая магия find — хвала Дискордии за Stack Overflow!

find ./ -type f -name "webmentions.json" \
    -printf \
        "jq -c 'del \
            (..|.photo?| \
            select(contains \
            (\"evgenykuznetsov.org/service/web/imageproxy\")?) \
            )' %p | \
        sponge %p\n" \
    | \
sh

А в каком формате у меня сохранены комменты из G+, ЖЖ и @дневников?.. Надо же, тот же самый JSON, вдохновлённый тем же webmention.io! Отобразим-ка и их тоже… Ага! У «лайков» в Google+ не было даты, и шаблон RSS-ленты об них «спотыкается»: Hugo охотно пытается распарсить дату из nil, но получает значение типа error.Error, которое дальше не обработать. Вписываем ещё одну проверку, и вот они, у меня на сайте — комменты, которым больше 15 лет! До чего же увлекательно перечитывать, особенно из тех времён, когда блог был куда более личным… Кого-то уже и по никам не помню, кого-то и в живых нет, с кем-то переписываемся-общаемся до сих пор, а кому-то тут же захотелось написать: «А помнишь?!.» Что ни говори, блогу, который ведёшь уже почти полжизни, да с комментами, никакой личный дневник и в подмётки не годится.

Следующим шагом буду webmention-endpoint писать.


  1. Кстати: если кто-то пойдёт этим путём и будет сохранять вебменшены в JSON, настоятельно рекомендую сохранять их в варианте pretty-printed (как, например, делает по умолчанию jq). Да, несколько сотен лишних байт на страницу, зато потом гораздо нагляднее в git diff↩︎