О чем эта статья?

Эта секция написана уже после статьи, чтобы читатель посмотрел, а надо ли ему что-то отсюда или нет, но это забавное приключение (напоминаю, что статьи в форме (б|в)лога, как всегда.

Что будет ниже:

  • Поиск open source решения для общения голосом, шаринга экрана, включения видео и чатов в режиме peer-to-peer, без лишних бекендов

  • Запуск этого решения в открытую в github pages

  • Заворачивание этого решения на приватный сервер

  • Простенькое закрытие доступа туды через http basic authentication

  • Заключение с описанием некоторых замечаний и потенцевальных возможностей

Зачем?

Типичное проприетарное решение, которое не дает вам пользоваться всеми преимуществами коммуникации
Типичное проприетарное решение, которое не дает вам пользоваться всеми преимуществами коммуникации

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

Звонки в raidcall, skype, teams, mattermost, google chat, slack, jabber и телеге звучат безумно, особенно, когда нужно быстро подключиться, пошарить экран, а самое главное — не думать о том, что шаришь кому-то телеметрию за проприетарное 3rd-party.

Когда ведешь лекции, особенно бесит, что дискорд блочит screen sharing, когда там юзеров >25, а в гугл мит есть лимит по времени.

Разумеется, читатель имеет право предъявить за проприетарные ОС, браузеры и прочие ПО, либо, что мне просто лень платить, но скорее просто хочется поискать велосипедов, да и поделиться с вами, что накопал и начал юзать.

Как и говорилось ранее, я попытался придумать оправданий для велосипедов, едем-те.

Гуглим какой-нибудь open source

И находим там всякий хлам, ищем дальше, конкретизируя запросы
И находим там всякий хлам, ищем дальше, конкретизируя запросы

Спустя полчаса поисков, находим какое-то более менее звучащее решение

Читаем ридмик, и видим, что в целом удовлетворяет
Читаем ридмик, и видим, что в целом удовлетворяет

Надо пойти посмотреть демку - https://chitchatter.im/

Выглядит более чем достаточно, переходя по кнопкам находим, что есть: mardkown-based чат, видео, аудио, шаринг экрана и файлов

Тестим и понимаем, что работает вроде как норм, осталось разобраться что под капотом.

Находим упоминания rtc, а самое главное trystero

import { joinRoom as trysteroJoinRoom, Room, BaseRoomConfig } from 'trystero'

export const joinRoom: typeof trysteroJoinRoom = (
  _config: BaseRoomConfig,
  _roomId: string
) => {
  const room: Room = {
    makeAction: () => [() => Promise.resolve([]), () => {}, () => {}],
    ping: () => Promise.resolve(0),
    leave: () => {},
    getPeers: () => ({}),
    addStream: () => [Promise.resolve()],
    removeStream: () => {},
    addTrack: () => [Promise.resolve()],
    removeTrack: () => {},
    replaceTrack: () => [Promise.resolve()],
    onPeerJoin: () => {},
    onPeerLeave: () => {},
    onPeerStream: () => {},
    onPeerTrack: () => {},
  }

  return room
}

export const selfId = ''

Копаем про этого зверя и находим репу trystero, а с ним и вебсайт, обещающий сделать любое решение мультиплеером — https://oxism.com/trystero/

Читаем и понимаем, что это какая-то шляпо про

Trystero can connect peers via ? BitTorrent, ? Nostr, ? MQTT, ? IPFS, and ? Firebase.

Посидел, покурил, не понял, думаю комментаторы лучше объяснят, почему для https://en.wikipedia.org/wiki/Session_Description_Protocol нужен торрент, жду объяснений в комментариях. Учитывая, что все обещают зашифрованный трафик, вроде как звучит норм.

Решение найдено, теперь внедряем

Форк

Просто делаем форк основного репозитория отседа

И идем его настраивать:

  • Вырубаем ненужные странички (Wikis, Issues, Projects), и идем сразу же конфигурировать все необходимое из секции self-hosted

  • Включаем удаление веток, оставляем только Rebase flow (почему? пишите нормальные коммиты, не храните PRs дольше 1-2 дней, и будет вам счастье)

Судя по README, нужно сделать две вещи:

  • Поменять homepage в package.json, чтобы приложение резолвило к себе статику

  • Указать DEPLOY_KEY (как же бесит постоянна вакханалия вокруг обслуживания ключей в очередном github action

Начну со второго, так как сначала надо проверить, что оно запускается само

Локально генерим ключики

ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N ""

Топаем сюды — settings → deploy keys → add new

Пишем в Title: DEPLOY_KEY
Пишем в Key: значение сгнерированного публичного ключа: cat gh-pages.pub
Ставим галочку возле Allow write access

Потом идем в — secrets → actions → new repository secret, и делаем тоже самое, только вставляя приватный ключ: cat gh-pages

Судя по action-у, оно будет тригерриться только при пуше в main и в github pages, а значит надо создать main ветку и сделать её главной

Топаем в ветки и создаем main

Затем топаем в настройки репозитория и ставим main главной веткой

Дальше пытаемся понять почему не запустились actions и находим, что надо было разрешить actions у форкнутой репы

Соглашаемся и триггерим action для деплоя в gh-pages

Примерно полторы минуты, оно прошло

Справа Code-странице репозитория появляется инфо про github pages

Кликаем туда и находим ссылку на инстанс по клику на иконку возле 1 minute ago

Работать оно работает, вот только статику не отдаёт, но это не важно, так как статика просто не там лежит

Оно ищет под https://the-homeless-god.github.io/assets/index-OD1TD8_t.js

А вот часть github pages оно пропустило в рамках homepage

В любом случае, давайте настроим, чтобы лишний раз убедиться, что всё работает, а вдруг и кому-то достаточно только этого.

Так что 1 раз пушим в мастер редактирование в package.json поля homepage на ссылку на наш github page

Коммитим и ждем как пройдет пайплайн

Помогло? Нет.
Идем в vite.config.js и указываем ему base

Запустилось, потыкался, ок

Пойду открою им PR в README хоть, пусть про base напишут

А теперь мы хотим это тащить на приватный сервер

А почему? А зачем? Оно же итак работает из-под github pages, но вот что важно — а я не хочу чтобы туда кто-то имел доступ

Берите тачку где хотите, мне нравятся ruvds, digitalocean и из самых бюджетных — это justhost.

Берите бубунту (осуждаю, но для статьи на хабре пойдет).

На этой самой бубунте:

  • Создайте отдельного юзера и папку под нашу статику, ну и права отдайте

    adduser ga
    mkdir -m 755 /var/www/chat
    chown ga:ga /var/www/chat
    su -- ga
    mkdir /home/ga/.ssh/
      ssh-keygen -t rsa -b 4096 -C "deployer" -f deployer -N ""

    И пишем публичный ключик в authorized_keys

  • Поставьте nginx и настройте конфиг смотреть на статику

    # устанавливаем nginx
    apt install nginx
    
    # говорим firewall чтоб разрешал ваш nginx, ssh и http/https
    ufw allow ssh
    ufw enable
    ufw allow http
    ufw allow https
    ufw allow 'Nginx HTTP'
  • И редачим конфиг: vim /etc/nginx/nginx.conf

    server {
    	access_log /var/log/nginx/access.log;
    
        add_header X-Xss-Protection "1; mode=block" always;
        add_header X-Frame-Options SAMEORIGIN;
        add_header X-Content-Type-Options nosniff;
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    
        root /var/www/chat;
        index index.html;
    
        location / {
          try_files $uri $uri/ /index.html;
        }
    }

В настройках репозитория добавляем три новых ключа:

  • SERVER_HOST - адрес нашего сервака

  • SERVER_USERNAME - имя юзера

  • SERVER_SSH_KEY - содержимое приватного ключа

И дописываем deploy-логику

 # тут мы собс-на просто смотрим, что сервер жив и радуемся
  - name: agent - get server status
    uses: appleboy/ssh-action@master
    with:
      host: ${{ secrets.SERVER_HOST }}
      username: ${{ secrets.SERVER_USERNAME }}
      key: ${{ secrets.SERVER_SSH_KEY }}
      script: neofetch && df -H / && free -m

	# тут мы ставим на github action ssh ключ, чтобы можно было пользоваться всякими командами а-ля rsync, scp и прочее
  - name: agent - install ssh key
    uses: shimataro/ssh-key-action@v2
    with:
      key: ${{ secrets.SERVER_SSH_KEY }}
      known_hosts: unnecessary

  # тут мы его устанавливаем
  - name: agent - install ssh
    run: ssh-keyscan -p 22 -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts

  # подчищаем за собой директорию на сервере
  - name: agent - cleanup static folder
    uses: appleboy/ssh-action@master
    with:
      host: ${{ secrets.SERVER_HOST }}
      username: ${{ secrets.SERVER_USERNAME }}
      key: ${{ secrets.SERVER_SSH_KEY }}
      script: rm -rf /var/www/chat/**

  # деплоим статику на сервер
  - name: agent - deploy static
    run: ls -la ./dist && rsync -avz -r -e "ssh -p 22" ./dist/ ${{ secrets.SERVER_USERNAME }}@${{ secrets.SERVER_HOST }}:/var/www/chat/

  # смотрим, что статика действительно там
  - name: agent - show deployed dir
    uses: appleboy/ssh-action@master
    with:
      host: ${{ secrets.SERVER_HOST }}
      username: ${{ secrets.SERVER_USERNAME }}
      key: ${{ secrets.SERVER_SSH_KEY }}
      script: ls -la /var/www/chat/

Коммитим это всё и ждём

Прячем за basic http authentication

Подробнее тут

# Ставим либу
apt install apache2-utils

# Генерим пользователя + пароль
htpasswd -c /etc/apache2/.htpasswd ga

# Смотрим, что всё хорошо
cat /etc/apache2/.htpasswd

Дальше в nginx.conf пишем чтобы доступов не было

auth_basic           "Administrator’s Area";
auth_basic_user_file /etc/apache2/.htpasswd;

Получится что-то вроде

Перезапускаем Nginx + дописываем себе генерацию серта откуда-нибудь, будь-то Let’s Encrypt

systemctl nginx restart

И теперь когда вы перейдете по ip своего сервера будет окошко с запросом логина и пароля

Остаётся только убирать налету информацию про ip адрес в homepage в package.json и удалить содержимое vite.config.ts про base

Получится примерно так:

Мы просто удаляем строку с помощью sed, а для подмены содержимого homepage поля используем встроенные методы и пересобираем

Заключение

Теперь у вас как и у меня есть свой спрятанный на сервере клиент для коммуникации без проприетарного бугага.

Может быть вы что-то накоммуниздите здесь полезного для себя, а может что-то напишите тут на улучшение.

Допустим вы решили коммерциализировать это решение, но оно по лицензии к вам не подходит?

Вы можете написать адаптер который вклинивается в этот код с вашими дописками хоть из-под другого npm пакета, хоть из-под git submodule.

Из ограничений у этого клиента — 256 участников в вашей комнате

Я бы написал как это всё дело скрыть под VPN, но мне лень писать статью дольше чем сейчас, так как просто сидел в дискорде и хотел показать коллегам другие способы коммуницировать и шарить экран, а поэтому это тянет для другой статьи организации демо-энва только для режима разработчиков.

Среди прочего, упустил немногое про бубунту, просто потому что ну не люблю я её, используйте BSD.

А в остальном — ничего нигде не хранится, всё шифруется, да и всё бесплатно, если не нужно приватить доступ к статике. И можно вести лекции не боясь, что качество просядет и общаться с коллегами используя максимальный стриминг.

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

Have a fun!

Комментарии (11)


  1. the_homeless_god Автор
    06.04.2024 23:54
    +5

    PR по апдейту доки приняли сразу, и, вот, я в списке контрибьюторов, приятно


  1. jarkevithwlad
    06.04.2024 23:54

    интересно какие там ограничения по разрешению / частоте кадров / битрейту ? Ну и было бы ещё удалённое управление цены бы не было, сейчас пользуемся sunshine + moonlight, там вполне себе можно 4к / 120fps / 150 мб/с и даже hdr, единственное звонок в другом ПО + порты должны быть открыты, но и задержки самые минимальные, можно и играть


  1. SabMakc
    06.04.2024 23:54

    Можно затестить: https://chitchatter.im/public/habrahabr
    Как понимаю, одна вкладка - одна комната. И пока вкладка открыта хоть у кого-либо - беседа существует.

    А одинаковые приватные комнаты с уникальным паролем образуют уникальные комнаты )
    Правда идет ли трафик между всеми "одинаковыми" комнатами (и потом расшифровывается по паролю) или трафик идет с учетом пароля - вопрос отдельный.


  1. AcckiyGerman
    06.04.2024 23:54
    +2

    А почему Jitsi не подошла?
    Решение обкатанное, ставится за 15 минут, вашим запросам удовлетворяет, не требует возни с гитхабом.


    1. SabMakc
      06.04.2024 23:54

      А разве здесь возня с GitHub обязательна?
      Да и фишка с "работает через GitHub Pages" лично мне нравится )


  1. Avabor
    06.04.2024 23:54

    Интересная статья мне очень понравилась! Так держать