Зачем хранить чужие данные, если есть IndieWeb?

Быть хозяином своих данных здорово. Вот этот сайт уже больше пяти лет — центральная IndieWeb-точка моей онлайн-жизни, и мне это очень нравится. Правда, радость владения собственным сайтом неотделима от серьёзной ответственности: как владелец сайта, я отвечаю и за его содержимое, и за все данные для его работы, что хранятся «под капотом».

Пока на сайте только то, что создал я, большой проблемы нет, но IndieWeb-то хорош (в отличие от обычной Паутины) именно своей социальностью, возможностью взаимодействовать с другими людьми через их сайты. И для этого обычно приходится поместить к себе на сайт какие-то данные, принадлежащие другим людям, что лично мне всегда доставляет известный дискомфорт.

Захочу я, к примеру, упомянуть Аарона Парецки (это один из выдающихся подвижников IndieWeb) у себя на сайте. Можно, конечно, просто сделать ссылку на его сайт, разметить как person mention и на том успокоиться, но выглядеть это будет ужасно скучно. В какой-нибудь соцсети вроде Twitter или Facebook это выглядело бы как всплывающая карточка с фотографией Аарона и какой-то информацией из его профиля. Мне тоже хочется, чтобы мои упоминания Аарона выглядели нарядно. Ладно, сделать модную всплывающую карточку мне (пока) не по силам, но можно было б хотя бы фотографию рядом с именем, как это сделано на IndieWeb-вики для упоминаний пользователей.

Можно было бы распарсить репрезентирующую h-card с сайта Аарона, получить из неё нужную информацию и ссылку на фотографию профиля, и сослаться на эту фотографию напрямую (как, собственно, на IndieWeb-вики и сделано), но стоит Аарону потом сменить URL «профильной» фотографии — и всё сломается. Для выхода из ситуации придётся саму фотографию скопировать к себе на сайт, но тогда, если Аарон заменит у себя эту фотографию, здесь останется старая. Ну и, очевидно, любую другую информацию из его профиля, которую я решу здесь на сайте демонстрировать, придётся сюда на сайт сохранить, и обновляться автоматически, когда Аарон обновляет свой профиль, она не будет.

Есть ещё юридические вопросы, куда ж без них. Кажется, уже едва ли не в каждой точке планеты есть какие-то законы про то, что такое личные данные и как их правильно хранить. Мне б не хотелось нарушать законы, если можно обойтись без этого, но я даже не возьмусь серьёзно разобраться во всём этом законодательстве, чтобы понять, как не надо поступать. Да и в конце концов, не хочу я хранить у себя на сайте данные Аарона, пусть он их на своём сайте хранит!

Я всё это достаточно долго крутил в голове, пока не пришёл к очевидному умозаключению: на самом деле данные Аарона нужны не у меня на сайте, а на устройстве у посетителя этого сайта! Мне достаточно возможности указать устройству посетителя: сходи, мол, к Аарону на сайт, там возьми нужную информацию, и нарисуй вместо вот этой скучной ссылки что-то поэлегантнее. Не для того ли нам ниспослан JavaScript?

Низкий поклон хорошим людям из русскоязычного чата про IndieWeb1; мне немедленно указали на потенциальные проблемы задуманного мною решения (CORS, например) и подсказали идею использовать кэширующий прокси-сервер, чтобы эти проблемы обойти. Собственно, это и сделало весь проект посильным: я ведь в JavaScript толком не умею, а вот в Go немножечко могу, и возможность не делать вообще всё на JavaScript весьма добавила уверенности в своих силах.

Немножко JavaScript всё-таки потребовалось, конечно, так что если вы это читаете с выключенным JavaScript (или вообще в RSS-читалке), ниже этого абзаца вы видите только иконку и имя со ссылкой на сайт. Но если вы читаете это с моего сайта, и JavaScript в вашем браузере включён, вместо иконки там фотография Аарона, а при наведении курсора на ссылку в качестве всплывающей подсказки появляется полное имя:

Аарон

И соль вся в том, что у меня-то на сайте только ссылка на сайт Аарона! А дальше уже ваш браузер при посредстве магии JavaScript запрашивает у прокси имя и фотографию Аарона и показывает их вам. Если Аарон свой профиль поменяет, на этой странице тоже будет новая инфомация.

А потребовались для этого всего лишь небольшой скрипт

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let persons = document.getElementsByClassName("person-mention");
for (let person of persons) {
    let u = person.getElementsByClassName("u-url")
    if (u.length == 1) {
        let url = u[0].attributes.href.value;
        let req = 'https://indieweb-glue.evgenykuznetsov.org/api/hcard?url=';
        req += url;
        const r = encodeURI(req);
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
                let hcdata = JSON.parse(this.responseText);
                if (hcdata.uphoto == null) {} else {
                    let i = person.getElementsByTagName("i");
                    if (i.length > 0) {
                        let uri = 'https://indieweb-glue.evgenykuznetsov.org/api/photo?url=';
                        uri += hcdata.source;
                        const urienc = encodeURI(uri);
                        const svg = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"%3E%3C/svg%3E';
                        let pic = '<img class="u-photo photo lazyload" src="' + svg + '" data-src="' + urienc + '">';
                        i[0].outerHTML = pic;
                    }
                }
                if (hcdata.pname == null) {} else {
                    let a = person.getElementsByTagName("a");
                    if (a[0].attributes.title == null) {
                        a[0].setAttribute("title", hcdata.pname);
                    }
                }
            }
        };
        xmlhttp.open("GET", r, true);
        xmlhttp.send();
    }
}

и прокси-сервер IndieWeb-glue, который я поднял2. Он открытый, если хотите — пользуйтесь. Может он пока совсем немного, но код открытый, лежит на GitHub, и участие в разработке приветствуется от всей души. Кстати, не могу не сказать спасибо Ракстону, который мне указал на ошибки в первом прототипе.

Ура! В упоминании Ракстона тоже фотография появляется. Определённо хорошее начало.

Придёт день, когда я таким же образом буду вебменшены показывать…


  1. Исходно здесь была ссылка на чат в Telegram (где всё и происходило), но теперь оттуда есть бридж в Matrix. Matrix лучше (уже хотя бы тем, что не надо регистрироваться, чтобы заглянуть), поэтому теперь в посте ссылка туда. ↩︎

  2. Два года он работал на Heroku, но в 2022-м в Heroku решили закрыть бесплатные сервисы. Пришлось перенести на тот же VPS, где хостится весь мой сайт. ↩︎