Как получить доступ к ресурсам IPFS географически и загрузить их в приложениях React

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

Если вы не изучали IPFS, но хотите развернуть его вместе со своими приложениями React, прочтите мое введение в IPFS, прежде чем продолжить эту статью:



Вышеупомянутая статья посвящена настройке узлов IPFS на вашем собственном VPS, а затем настройке шлюза для обслуживания активов, размещенных в IPFS. Он действует как необходимое фундаментальное введение в IPFS; это позволит вам быстро и эффективно настроить ваши узлы.

Мы будем опираться на эту настройку и сделаем наши узлы IPFS более безопасными для обслуживания активов. В частности, мы рассмотрим:

  • Настройка и пропуск прокси-сервера NginX для прямых посещений с http: // ‹your_ip_address› / ipfs на ваш шлюз IPFS. При этом нам больше не требуется включать порт 8080 (или любой другой порт, на который настроен ваш шлюз).
  • Настройка NginX с использованием SSL и доменного имени. Здесь мы дополнительно улучшим конфигурацию NginX, добавив доменное имя, SSL и включив GZIP. На этом этапе у нас будет возможность получить доступ к содержимому IPFS в формате http: // ‹your_domain› / ipfs / ‹hash_to_content›.
  • Управление CORS с помощью IPFS, чтобы разрешить доступ только вашим доменам к шлюзу вашего узла IPFS.
  • Загрузка ресурсов IPFS в React. Отсюда загрузка ресурсов IPFS, таких как изображения и SVG, очень проста; все, что нам нужно, это URL-адрес с хешем содержимого IPFS. Чтобы упростить содержание, мы будем использовать React LazyLoad и React GSAP wrapper для анимации ресурсов после их загрузки.
  • Выбор шлюза IPFS для получения контента. Это важно для минимизации задержки. Если бы у вас было 5 узлов IPFS, работающих в разных частях мира, вы хотели бы получать контент из ближайшего к нему в зависимости от местоположения ваших посетителей. Мы рассмотрим, как это сделать двумя способами; Переменные среды React и запрос на стороне сервера для определения ближайшего узла.

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

Во-первых, необходимо установить и настроить NginX, чтобы настроить прокси-прокси, чтобы в конечном итоге упростить общедоступный URL-адрес шлюза и включить доставку по HTTPS. Давай сделаем это в первую очередь.

Установите NginX и настройте прокси-сервер

Наш первый порт захода - настроить NginX на нашем сервере узла IPFS. В зависимости от того, какую ОС вы используете, установка может отличаться. Для дистрибутива Ubuntu установите NginX со следующим:

#install nginx
sudo apt update
sudo apt install nginx
sudo systemctl status nginx
#open firewall ports
sudo ufw allow 'Nginx Full'
sudo ufw status

Теперь давайте настроим прокси-сервер, который будет проксировать каждое посещение HTTP (порт 80) на порт 8080 - порт нашего шлюза IPFS. Создайте default.conf файл в /etc/nginx/sites-available:

server {
    listen 80;
    listen [::]:80;
    server_name <your_ip_address>;
    #IPFS proxypass
    location /ipfs {
       proxy_set_header X-Forwarded-Host $host;
       proxy_set_header X-Forwarded-Server $host;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_pass http://127.0.0.1:8080$uri;
    }
}

Обязательно замените <your_ip_address> общедоступным IP-адресом вашего сервера.

Теперь, если мы перезапустим NginX, вы сможете загружать ресурсы IPFS непосредственно из HTTP-запросов.

sudo service nginx restart

Чтобы проверить свою конфигурацию, проверьте, загружается ли следующее изображение котенка, снова указав свой IP-адрес в URL-адресе:

http://<your_ip_address>/ipfs/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg

Теперь давайте добавим доменное имя, SSL и сжатие GZIP для дальнейшей защиты и оптимизации доставки ресурсов IPFS.

Добавление SSL и домена (и GZip)

Если вы знакомы с NginX, эти параметры конфигурации будут очень простыми; другой серверный блок для порта 443 необходимо настроить с необходимыми параметрами, чтобы разрешить SSL.

Примечание. Обязательно зарегистрируйте сертификат SSL сейчас, если вы еще не зарегистрировали его. Вам понадобится ключ сертификата и пакет ca-bundle (с включенным crt), чтобы NginX мог ссылаться на него.

Мы хотим обслуживать контент исключительно через зашифрованное соединение, поэтому нам нужно либо полностью заменить вышеуказанный сервер port 80 сервером port 443, либо изменить нашу конфигурацию port 80 на постоянное перенаправление на HTTPS-адрес того же URI.

С этим постоянным перенаправлением весь HTTP-трафик будет перенаправлен на URL-адрес HTTPS того же URI после имени домена.

Измените блок порта 80 следующим образом:

server {
    listen 80;
    listen [::]:80;
    server_name <your_domain> www.<your_domain>;
    return 301 https://<your_domain>$request_uri;
}

Теперь мы можем быть уверены, что будут обрабатываться только HTTPS-запросы.

Теперь добавьте следующую конфигурацию HTTPS под серверный блок порта 80, заменив выделенный жирным шрифтом текст своими собственными значениями:

server {
    listen 443;
    listen [::]:443;
    #configure SSL
    ssl on;
    ssl_certificate /etc/nginx/ssl/domain.ca-bundle;
    ssl_certificate_key /etc/nginx/ssl/domain.key;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    resolver 127.0.0.1;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/nginx/ssl/domain.ca-bundle;
    server_name <your_domain> www.<your_domain>;
   #configure GZIP
    gzip on;
    gzip_vary on;
    gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml image/gif image/jpeg image/png image/tiff image/vnd.wap.wbmp image/x-icon image/x-jng image/x-ms-bmp image/svg+xml image/webp
    gzip_disable "msie6";
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.0;
    #configure IPFS proxypass
    location /ipfs {
       proxy_set_header X-Forwarded-Host $host;
       proxy_set_header X-Forwarded-Server $host;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_pass http://127.0.0.1:8080$uri;
    }
}

Этот серверный блок состоит из нашей конфигурации SSL и Gzip, в которой мы сжимаем большинство поддерживаемых форматов файлов, с последующим прохождением прокси-сервера на наш шлюз IPFS.

Чтобы узнать больше о параметрах конфигурации SSL, которые предоставляет NginX, обратитесь к его документации по SSL:



Gzip также можно настроить. Следующий документ NginX представляет Gzip вместе с некоторыми рекомендуемыми конфигурациями:



Прокси-пропуск

Последний раздел нашего серверного блока - это прокси-сервер IPFS.

Установлены некоторые дополнительные заголовки, чтобы мы могли получить доступ к информации о сервере запросов. Возможно, наиболее важной конфигурацией является значение proxy_pass, указывающее на localhost (127.0.0.1, порт 8080), за которым следует URI. URI в этом случае всегда будет начинаться с / ipfs /, следуя тому же соглашению, которое IPFS ожидает при запросе ресурсов через шлюз.

Давайте снова перезапустим NginX, чтобы отразить последние изменения:

sudo service nginx restart

Как и раньше, попробуйте загрузить изображение котенка, которое теперь обслуживается зашифрованным через HTTPS, по следующему URL-адресу:

https://<your_domain>/ipfs/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg

Теперь, когда SSL настроен, мы можем двигаться дальше и принимать запросы только от нашего конкретного доменного имени с контролем CORS.

Добавление CORS Control в IPFS

Теперь мы только хотим, чтобы наш домен имел доступ к нашему шлюзу IPFS - необходимо настроить CORS. Это можно сделать в командной строке. Выполните следующие команды IPFS, чтобы ограничить доступ к домену:

ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["<your_domain>"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]'

На самом деле эти команды изменяют раздел API вашего файла конфигурации IPFS. Содержимое, добавленное к нему указанными выше командами, выглядит следующим образом:

{
  "API": {
    "HTTPHeaders": {
      "Access-Control-Allow-Credentials": [
        "true"
      ],
      "Access-Control-Allow-Methods": [
        "PUT",
        "GET",
        "POST"
      ],
      "Access-Control-Allow-Origin": [
        "<your_domain>"
      ]
    }
  },
...

Использование обеих команд IPFS или ручное редактирование файла конфигурации IPFS дает нам тот же результат.

Перезапустите IPFS, чтобы изменения вступили в силу.

Если вы следовали предыдущей статье, мы настроили IPFS для работы в фоновом режиме через supervisord. В этом случае перезапустите процесс ipfs следующим образом:

sudo supervisorctl restart ipfs

Если вы используете другой диспетчер процессов, у вас будет аналогичная возможность перезапустить процесс.

Теперь наш домен готов обслуживать ресурсы IPFS непосредственно из запросов HTTPS. Давайте теперь рассмотрим, как мы можем получить некоторые ресурсы в React и отобразить их в эстетически приятном виде.

Загрузка ресурсов IPFS в React

На этом этапе вы можете встраивать ресурсы IPFS прямо в свои приложения React. Например, вы можете разместить изображение котенка в теге <img /> JSX:

<img src="https://<your_domain>/ipfs/QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg" />

Это действительно загрузит изображение, но есть две проблемы, которые вы можете решить:

  1. Загрузка изображения не будет немедленной. Вместо этого изображение появится в пользовательском интерфейсе после загрузки. Это, конечно, относится к любому активу, который вы загружаете вне импортированных ресурсов React.
  2. Изображение начнет загружаться сразу при загрузке страницы. Разве не было бы хорошо, если бы мы могли загружать изображение только в том случае, если оно присутствует на экране, или если мы прокручиваем страницу вниз и собираемся посетить это изображение? В конце концов, это довольно большой файл, и он инициирует другой запрос, когда страница загружается, когда в этом нет необходимости.

Мы можем решить эти проблемы с помощью отложенной загрузки и добавления анимации для представления загруженных элементов IPFS. Здесь мы будем использовать 2 пакета:

  • react-lazy-load: простой способ остановить загрузку ресурсов до тех пор, пока они не появятся на экране или когда они скоро появятся. Мы обернем <LazyLoad /> компонент вокруг наших ресурсов IPFS, чтобы они следовали этому поведению.
  • react-gsap: относительно новый пакет, который объединяет библиотеку GSAP в простые в использовании компоненты React. Используя компоненты <Tween /> и <Timeline />, мы можем ввести анимацию (или поэтапную анимацию в списке объектов) для представления ресурсов.

Ниже react-lazy-load и react-gsap будут использоваться одновременно для достижения хорошего поведения при загрузке ресурсов IPFS.

Рассмотрим следующий пример, где мы просто заранее определяем список URI IPFS и загружаем их в список. Для этого мы отображаем этот список и оборачиваем актив IPFS внутри <LazyLoad /> компонента. Также в компоненте <LazyLoad /> находятся компоненты <Tween /> и <Timeline /> для управления тем, как ресурс анимируется на странице:

Прежде чем продолжить, проверьте это поведение. Этот шаблон также можно расширить, включив, например, запрос к серверной службе для создания списка ресурсов IPFS.

Где взять активы IPFS?

Последняя проблема, которую мы рассмотрим в этой статье, - это настройка шлюза IPFS для извлечения ресурсов. Если вы используете один экземпляр IPFS и один экземпляр приложения React, следующее обсуждение может не относиться к вам.

Приложение, предназначенное для глобальной аудитории, должно быть распределено по нескольким VPS в стратегических местах по всему миру. Обслуживание ресурсов IPFS через HTTP-шлюз ничем не отличается.

Что необходимо, так это распределить шлюзы IPFS в стратегически важных местах, чтобы минимизировать задержку.

Приложение - ›Шлюз -› Доставка IPFS - ›Приложение

Доставка контента в базовой сети IPFS очень быстрая. Если бы внешнее приложение напрямую взаимодействовало с узлом IPFS для получения содержимого IPFS, нам не нужно было бы беспокоиться о распределении наших шлюзов - базовые протоколы обеспечили бы быструю доставку запрашиваемого мной содержимого и его кэширование во многих одноранговых узлах, поэтому до тех пор, пока контент остается востребованным. Это возможно с другими протоколами IPFS Javascript, но выходит за рамки данной статьи.

Но здесь мы имеем дело со сценарием, когда наше приложение React отправляет запрос через HTTP на шлюз, запущенный (скорее всего) на другом сервере. Перед тем, как сеть сможет работать над доставкой запрошенного актива, делается дополнительный HTTP-запрос к серверу шлюза IPFS. Эта связь между вашим приложением и сервером шлюза вызовет дополнительную задержку, которую можно минимизировать, если серверы хостинга IPFS расположены стратегически.

Каждая сборка React должна знать, где находится ближайший узел IPFS, в виде его IP-адреса. Мы можем решить эту проблему как минимум двумя способами:

Метод 1. Переменные среды

После создания приложения React для конкретного VPS мы можем включить доменное имя ближайшего сервера шлюза IPFS в виде переменной среды. Этот метод является быстрым и надежным, и для сбора этой информации не требуется никаких запросов на серверную службу.

Если вы используете приложение Create React, переменные среды можно определить в корневом каталоге вашего проекта. Все переменные среды должны иметь префикс REACT_, чтобы они учитывались во время сборки.

Сохраните адрес шлюза IPFS как переменную среды, например:

  • Создайте .env файл в каталоге приложения React, если он еще не существует.
  • Вставьте вашу переменную, содержащую URL-адрес шлюза IPFS:
REACT_IPFS_GATEWAY_URL = https://<your_domain>/ipfs/
  • Сохраните файл

Теперь вы можете получить доступ к переменной в проекте через process.env.REACT_IPFS_GATEWAY_URL. Ссылаясь на предыдущую суть, загрузка ресурса IPFS в объекте <img /> JSX теперь будет выглядеть следующим образом:

<img src={`${process.env.REACT_IPFS_GATEWAY_URL}${record}`} />

Мы просто объединяем URL-адрес шлюза, за которым следует наш URL-адрес хэша, в данном случае отображаемый из нашего массива.

Вы можете жестко закодировать ресурс, и в этом случае мы можем использовать тот же формат:

<img src={`${process.env.REACT_IPFS_GATEWAY_URL}QmW2WQi7j6c7UgJTarActp7tDNikE4B2qXtFCfLPdsgaTQ/cat.jpg`} />

Соберите свой проект React, и ваши переменные .env будут доступны в сборке и, следовательно, везде, где вы публикуете свою сборку.

Метод 1: запрос на серверную службу

Возможно, более удобный подход, если ваши шлюзы IPFS часто меняются, вы также можете запросить сервер внутреннего узла или другую службу о ближайшем ресурсе IPFS.

Это дает нам дополнительную гибкость, но большую задержку и дополнительное состояние загрузки во внешнем интерфейсе; перед загрузкой ресурсов IPFS необходимо сделать дополнительный запрос к серверу. Конечно, если вы уже полагаетесь на исходный запрос серверной части для загрузки контента, URL-адрес вашего шлюза IPFS может быть возвращен с этим существующим запросом.

Что дает нам дополнительную гибкость, так это то, что при использовании этого метода нам не нужно знать, где именно размещается наше приложение React или где находится шлюз IPFS.

Вместо этого наш внутренний запрос может просто ссылаться только на IP-адреса и определять местоположение серверов с помощью такой службы, как GeoIP Lite, которую я обсуждал в этой статье.

Формула Хаверсина

Отсюда вы можете рассчитать расстояние между каждым сервером шлюза IPFS и приложением React и вернуть URL-адрес ближайшего шлюза IPFS.

Посмотрите этот эксперимент Эндрю Хеджеса, который вычисляет расстояние между двумя точками на Земле на основе долготы и широты.

Учитывая lon1 и lat1 IP-адреса1 и lon2 и lat2 IP-адреса 2, формула сводится к следующей формуле Хаверсина:

on = lon2 - lon1 
dlat = lat2 - lat1 
a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2 
c = 2 * atan2( sqrt(a), sqrt(1-a) ) 
d = R * c (where R is the radius of the Earth)

Примечание: эта формула не учитывает несфероидальную (эллипсоидальную) форму Земли. Это будет иметь тенденцию к переоценке заполярных расстояний и недооценке трансэкваториальных расстояний. Значения, используемые для радиуса Земли (3961 миля и 6373 км), оптимизированы для местоположений около 39 градусов от экватора (примерно широта Вашингтона, округ Колумбия, США).

В любом случае это решение действительно определит ближайший узел IPFS в поместье с открытым исходным кодом.

Заключение

В этой статье описан процесс настройки шлюза IPFS для доставки зашифрованного контента непосредственно из сети IPFS.

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

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

Дальнейшие соображения

В этой статье мы не обсуждали IPNS или DNS Link, функции IPFS, которые используются для ссылки на конкретный актив, который часто обновляется. Эти функции проистекают из алгоритмов хеширования IPFS, при которых одно изменение файла приводит к другому хешу IPFS, нарушая исходную ссылку. IPNS был введен для решения этой проблемы, давая нам возможность указывать на один и тот же ресурс, даже если его хэш изменяется посредством модификаций файла.

Эти и другие функции будут обсуждаться в следующей статье, чтобы еще больше улучшить методы доставки через шлюз IPFS.