43
48
70
80
Рефакторинг редактора слотов и новый редактор сложности: очередной шаг вперёд
В этом обновлении я сосредоточился на улучшении архитектуры проекта и пользовательского интерфейса. Изменения затронули как логику работы с экипировкой, так и управление эффективности класса. Вот основные нововведения:
Новый useGearSlotEditor хук
Теперь управление состоянием предметов, сокетами и чарами в слотах происходит через кастомный хук useGearSlotEditor. Это позволило:
- Упростить компонент GearSlotEditorDialog;
- Централизовать всю бизнес-логику редактирования;
- Ввести ограничения на количество сокетов и чаров в зависимости от типа слота.
Дополнительно добавлены toast-уведомления — пользователи теперь получают обратную связь при сохранении изменений или ошибках.
🎚 Редактор эффективности класса
Добавлен совершенно новый компонент — Difficulty Bar Editor. Он включает:
- Хук useDifficultyEditor для управления рейтингом эффективности класса и его сохранением;
- Обновлённый GuideDifficultyBar, который теперь динамически реагирует на изменения;
- Интеграцию с новым API для обновления данных о эффективности.
🧼 Улучшение структуры GuideEditor
Компонент GuideEditor был переработан:
- Улучшено визуальное расположение элементов;
- Удалён устаревший и неиспользуемый код;
- Улучшена читаемость и поддерживаемость.
🌍 Локализация названий слотов
Теперь все названия слотов экипировки отображаются на русском языке:
- Добавлена локализация в slot-backgrounds.ts;
- Интерфейс стал более понятным и дружелюбным для русскоязычных пользователей.
🧩 Новый API для данных эффективности
Для управления рейтингом эффективности реализован серверный API:
- Обработка PATCH-запросов для обновления данных;
- Логика сохранения и обновления записей реализована на сервере.
Это обновление значительно упростило архитектуру и улучшило пользовательский опыт.
И теперь пару скринов
страница редактора, кнопки сохранения появляються если изменяются данные


Диалоговые окна для редактора гира


теперь у каждого слота свои условия, разделены предметы у которых могут быть сокет и у которых их нет, так же и с инчантом.
Для любопытных потыкать свежий билд Редактор или вот вам Страница билдов
Оставайтесь на связи — впереди ещё много интересного!

202
135
Финальный пост по фильтрам
Оптимизация и улучшения в работе с гайдами
В рамках последнего обновления я сосредоточился на повышении производительности и удобства взаимодействия с системой гайдов. Ниже кратко расскажу, что было сделано:
⚡ Кэширование данных
- Реализовано кэширование получения данных гайдов с использованием unstable_cache, с временем повторной валидации в 1 час. Это значительно снижает нагрузку на сервер при повторных запросах.
- Добавлено кэширование фильтров по режимам и специализациям, что улучшает скорость отклика интерфейса при выборе параметров.
💡 Обновление компонентов
- Компонент ClassGuidesPage был обновлён для использования закэшированных данных, что положительно сказалось на его производительности.
- Компонент отображения кнопки гайда (GuideButton) получил расширение: теперь он показывает больше информации о гайде, делая интерфейс более информативным и полезным.
🧼 Рефакторинг и улучшение читаемости
- Модальное окно создания гайда было переработано для улучшения читаемости и единообразия кода.
- Также улучшена общая структура кода и логика фильтрации — теперь за это отвечают кастомные хуки, что упростило поддержку и расширение функциональности.
📦 Новый API-роут
- Реализован новый маршрут API для фильтрации гайдов по различным критериям, включая класс, специализацию, роль и режим.
✅ Результат
В результате этих изменений проект стал:
- Быстрее
- Более читаемым и поддерживаемым
- Готовым к масштабированию и добавлению новых функций
Эти улучшения — шаг вперёд к стабильной и гибкой системе гайдов для WoW-сообщества.


Теперь перешел к реализации slug дальше планирую делать структуру страници гайдов
Для желающих покликать что я там накодил ссылка
173
Stormic: установка на VPS и локально
Добрый день.
Сегодня у нас небольшой пост, который должен был появится еще недельку назад, но тем не менее он все же дошел, хотя бы сейчас. Так вот, установка!
Хочу вас сразу предупредить, что это еще совсем не то, что можно назвать "продакт реди". Это больше из разряда "посмотреть и пощупать". База данных, как минимум еще раз, сильно изменится в ближайшем будущем и вам стоит дождаться версии с новой архитектурой БД, прежде чем пытаться поднять на Stormic что-то большое и полноценное. С обновлением базы данных придут еще некоторые ключевые, необходимые функции, типа уведомлений, рейтинга и личных сообщений.
Сегодня я расскажу о том, как запустить Stormic по пути наименьшего сопротивления. В целом, разницы никакой не будет между тем, что вы захотите это запустить у себя на локалке, либо на полноценном VPS сервере. Так же, покажу еще одно open-source ПО, которое вам точно понравится.
Запускать будем на убунту, в целом любой, но проверенная временем - 22.04.
Если ваш cloud провайдер сразу дает возможность купить сервер с утановленным докером, то берите с докером, если нет, то давайте поставим:
1. Docker
- Сначала удалим все, что касается докера на сервере, чтобы установка 100% была чистой
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
- Добавим репозиторий докера
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
- Ну и ставим сам докер
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
2. Dokploy
Дальше поставим то самое open source ПО, которое вам точно понравится - dokploy.
Он позволяет собирать и управлять контейнерами докера в удобном веб-интерфейсе.
Ставим:
curl -sSL https://dokploy.com/install.sh | sh -s update
На этом его установка закончена и после небольшого ожидания он вам скажет на каком адресе найти его веб-интерфейс.
Перейдя по указанному адресу вы увидите окно регистрации. Регистрируетесь.
Здесь, на вкладке Web Server, вы можете установить свой домен для dokploy (если DNS записи вашего домена указывают на ваш VPS сервер) и тогда интерфейс dokploy будет доступен на вашем домене.
3. PostgreSQL
- На вкладке Projects нажимаете кнопку Create Project, это просто название папки, чтобы группировать ваши сервисы.
- В новой созданной папке нажимаете Create Service и выбираете Database:

- Выбираете PostgreSQL, задаете имя сервиса (тут stormic-db в примере)
- Задаете Database Name, Database User и Database Password:

- Нажимаете Create
- Заходите в новую созданную базу данных, внизу страницы в External Credentials задаете порт, ну например 5442

- Нажимаете кнопку Deploy вверху страницы.
На этом база данных запущена и готова к подключениям.
4. RabbitMQ
Он нам необходим для работы всего, что не требует моментального изменения в бд: лайки постов, лайки коментов, закладки и прочие вещи.
- Возвращаетесь обратно в папку проекта, снова нажимаете Create Service, выбираете Compose, вводите name и нажимаете Create.
- Заходите в созданный сервис, в Provider выбираете Raw и сохраняте там следующее
version: "3.8"
services:
rabbitmq:
image: rabbitmq:3-management
container_name: rabbitmq_dev
env_file: .env
restart: always
networks:
- dokploy-network
ports:
- '5672:5672' # порт для AMQP
- '15672:15672' # порт для панели управления RabbitMQ
networks:
dokploy-network:
external: true

- Нажимаете Save и переходите на вкладку Environment и сохраняете там следующее
RABBITMQ_DEFAULT_USER=stormic
RABBITMQ_DEFAULT_PASS=very_secret_password
RABBITMQ_DEFAULT_VHOST=/
RABBITMQ_MANAGEMENT_PORT=15672
RABBITMQ_AMQP_PORT=5672

- Нажимаете Save
- Возвращаетесь на вкладку General и нажимаете Deploy
На этом RabbitMQ запущен.
5. SMTP и S3
Так же вам понадобится SMTP сервис, который будет отправлять почту о успешной регистрации, подверждении email и прочее. Ну и где хранить все эти видео, картинки и гифки - S3 корзина.
S3 можно получить тут же бесплатно, если вы установить Minio (это я объяснять не буду тут, потому что настройка ведра для работы там - это отдельный пост). Поэтому я посоветую вам получить и SMTP и S3 у того же провайдера, у кого вы купили сервер, например TimeWeb Cloud. Берете SMTP и корзину S3, получаете данные для подключения к ним и возвращаетесь, а мы продолжим.
6. Stormic
Ну вот мы и пришли к тому, ради чего мы тут. Это уже самое простое.
- Возвращаетесь в папку Projects, где у вас уже лежит база данных и RabbitMQ. Снова нажимаете Create Project и выбираете Application. Вводите name (например Worker) и нажимаете Create.
- Переходите на вкладку Git и указываете ссылку на репозиторий проекта:
https://github.com/stormhead-org/stormic
- В Branch сохраняете
beta-003-update

- Нажимаете Save
- Переходите на вкладку Environment и сохраняете там следующее
NEXT_PUBLIC_SERVER_URL=https://example.app
NEXT_PUBLIC_BASE_URL=example.app
DATABASE_URI=postgresql://stormic:very_secret_password@ip_your_server:5442/stormic
PAYLOAD_SECRET=DR1LeNrpluu7O7hNHvOKpM6 #задайте тут что-то случайное
PREVIEW_SECRET=QuU1ym9cUa8wFMHf8HcgJHwt #задайте тут что-то случайное
CRON_SECRET=k32UuyPrEoNf5xKp4BwHnvHp #задайте тут что-то случайное
SMTP_HOST=smtp.timeweb.ru #если вы использовали timeweb
SMTP_USER=staff@stormhead.org #почта, которую вы используете для SMTP
SMTP_PASS=your_password #ваш пароль от SMTP
S3_BUCKET=stormic #название корзины
S3_REGION=ru-1 #если есть
S3_ENDPOINT=https://s3.example.ru #эндпоинт вашего провайдера
S3_ACCESS_KEY_ID=nMp6Q3lZq #вам его выдали при регистрации корзины
S3_SECRET_ACCESS_KEY=7ngqqC9zKy #вам его выдали при регистрации корзины
RABBITMQ_URL=amqp://stormic:very_secret_password@ip_your_server:5672
#NEXT_PUBLIC_YANDEX_METRIKA=234324232 #указываете ваш ID, чтобы получать стастистику. Это указывать необязательно
- Сохраняете это и переходите на вкладку Advanced и вводите там в Run Command
pnpm start:workers
- Сохраняете и на этом нажимаете кнопку Deploy.
На этом мы запустили воркеров, которые работают параллельно с основным сайтом, обрабатывают очереди из RabbitMQ и выполняют запросы в API.
- Теперь просто создайте еще один сервис, назовите его (например app) и проделайте все то же самое, что вы только что сделали со Stormic, только на вкладке с Advanced не указывайте pnpm start:workers, вообще ничего там не указывайте. А так все то же самое. Во вкладке Domains задайте ваш домен, где будет работать Stormic

- В хост задаете ваш домен, который будет использоваться для платформы.
- Path не трогаете, оставляете /
- Port задаете 3000
- Включаете HTTPS
- Выбираете Let's Encrypt
- Нажимате Create
После этого нажимаете Deploy, ждете пока это все соберется и запустится, этот процесс виден в соответствующих вкладках logs и deployments
Готово
Stormic запущен и работает на вашем домене.
localhost
Можете запустить Sotrmic локально.
- Где-то получаете PostgreSQL, RabbitMQ, SMTP, S3
Можете сделать все то же самое и у себя на локалке, что мы написали выше. Можете все это получить у какого нибудь Cloud провайдера.
- Устанавливаете себе nodejs
Можете скачать тут (ссылка)
- Устанавливаете pnpm
Можете скачать тут (ссылка)
- Скачиваете себе beta ветку репозитория куда-то на пк
git clone https://github.com/stormhead-org/stormic.git
- Заходите в директорию репозитория куда вы его скачали, создаете в корне проекта файл .env (именно с точкой в начале)
- Сохраняете в него то, что мы сохраняли выше в Environment, например
NEXT_PUBLIC_SERVER_URL=http://localhost:3000
NEXT_PUBLIC_BASE_URL=localhost
DATABASE_URI=postgresql://stormic:very_secret_password@ip_your_server:5442/stormic
PAYLOAD_SECRET=DR1LeNrpluu7O7hNHvOKpM6 #задайте тут что-то случайное
PREVIEW_SECRET=QuU1ym9cUa8wFMHf8HcgJHwt #задайте тут что-то случайное
CRON_SECRET=k32UuyPrEoNf5xKp4BwHnvHp #задайте тут что-то случайное
SMTP_HOST=smtp.timeweb.ru #если вы использовали timeweb
SMTP_USER=staff@stormhead.org #почта, которую вы используете для SMTP
SMTP_PASS=your_password #ваш пароль от SMTP
S3_BUCKET=stormic #название корзины
S3_REGION=ru-1 #если есть
S3_ENDPOINT=https://s3.example.ru #эндпоинт вашего провайдера
S3_ACCESS_KEY_ID=nMp6Q3lZq #вам его выдали при регистрации корзины
S3_SECRET_ACCESS_KEY=7ngqqC9zKy #вам его выдали при регистрации корзины
RABBITMQ_URL=amqp://stormic:very_secret_password@ip_your_server:5672
#NEXT_PUBLIC_YANDEX_METRIKA=234324232 #указываете ваш ID, чтобы получать стастистику. Это указывать необязательно
- Открываете в директории этой папки два консольных окна и в одном сначала вводите
pnpm i
- Нажимаете Enter
- После завершения, в этом же окне вводите
pnpm start:workers
- Нажимаете Enter
- В другом окне вводите
pnpm dev
- Нажимаете Enter
На этом у вас на адресе http://localhost:3000 запущен Stormic и вы можете его пощупать сами.
Финал
И так, теперь вы можете запустить Stormic как у себя локально, так и полноценно на VPS сервере с SSL сертфикатами и вашим доменом.
Надо сказать, что это временный вариант. В будущем вы сможете его установить просто запустив в консоле
curl -sSL https://stormhead.org/stormic/install.sh | sh
И установочный скрипт сделает все за вас. Вам нужно будет только отвечать на пару вопросиков да/нет и все.
Так же напоминаю, если у Вас есть возможность поддерживать проект финансово, чтобы хотя бы оплачивать сервер, то Вы можете подписаться на Boosty.
Отдельное спасибо первому патрону на бусти, которого привлек этот проект:
- Андрей Федорко
Жду ваших вопросов в коментах
Спасибо, что проявляете интерес к проекту. Вместе мы сможем многое )

225
Начал делать страницу гайдов class-guides
После пинка за то что у меня структура проекта ужасная, занялся уборкой в папках, так же помогли наладить структуру, теперь все выглядит получше.
Закончил делать функционал добавления гайда

Кнопка которая запускает генерацию гайда готова, спасибо Вампиратке за экшн который с апи близов тягает версии патчей, теперь патч автоматически определяется и записавыется в бд, я переделал рендер, теперь все данные генериреут сервер, а не подгружает клиен, это убрало проблему с подргузкой данных и на клиенте
Так же по мере надобности пополняю модели в схеме призмы, и приступаю к работе над рендером guides-page

немного подкрутил модели призмы и добавил туда новые поля для картинок фона класБГ режима и роли.
Теперь в планах сделать сид, чтоб можно было удобно все тестить и продолжить работу нам guides-page
Огромное спасибо, Overk1lls и LionBlackVMP за то, что вызвались помочь.
планов пока много, начали прощупывать апи близов, если все получиться то откажемся от тултипов вовхеда и в целом по минимуму будет использовать ембеты вовхеда

201
191
Небольшой отчет о проделанной работе
Я закончил серверную часть для табов, не без проблем но я их решил.
А вот где не ожидал подлянки так это на делое, деплой прошел очень тяжело, поел не мало мох нервов, но я справился, все протестировал, работает как задумано, без ошибок.
Но проделанной работой за сегодня я вполне доволен. И так что имеем на текущий момент.
Сирое создание гайда, кнопка которая по сути создает в бд пустую заглушку под гайд. Табы, добавление табов, редактирование названий, добавление картинки в таб, markdown редактор для контента, все сохраняется, удаляется и изменяется.


Сохранения работает через поиск уник ид которые присваиваются табам через heroTalentId, модель табов будет пере использоваться в других местах.
Сохранение я делаю через множественное обновление или создание записей в таблице Tab через Prisma, используя транзакцию (tx).
const upsertResults = await Promise.all(
tabs.map((tab) =>
tx.tab.upsert({
where: {
value_heroTalentsId: {
value: tab.value,
heroTalentsId: tab.heroTalentsId,
},
},
create: tab,
update: {
label: tab.label,
iconUrl: tab.iconUrl,
content: tab.content,
},
})
)
);
Линк на страницу с табами https://black-temple.vercel.app/havoc/1

223
Небольшой пост по проделанной работе
Закончил сегодня с версткой и логикой для компоненты табов, пришлось немного переделать дефолтные табы от shadcn, так же встроил в них react-md-editor с предпросмотром и возможностью развернуть на весь экран редактор.

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

Все настроено для светой и темной темы, и мобильной версии

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

226
Обновление v0.0.3-beta
Что новенького?
- Ну из очевидного - появилась мобильная версия. Не без грехов конечно, однако для первого времени сойдет. Потихоньку допилим все шероховатости, отступы, состояния и прочие мелочи;
- Новая тема. Старая темная тема никуда не делась, верну ее в каком-то ближайшем обновлении и можно будет выбрать из 3х тем: светлая, эта черно-желтая и старенькая темно-синя;
- Оптимизирована работа с изображениями. Больше мы не грузим исходное изображение, а везде уже использует ужатые и оптимизированные к вебу. Тем не менее, вы все еще можете нажать на скрепку на прикрепленных изображениях в коментах и получить ссылку на исходное изображение, не ужатое;
- Миллиард оптимизаций компонентов и импортов, чтобы оно все двигалось немного быстрее.
Что дальше?
Думаю, что в ближайший месяц будут только исправления и минорные обновлениям, какие-то допиливания мобилки и вообще ui. А через месяц я постараюсь придти к Вам с огромным обновлением, которое значительно ускорит скорость работы абсолютно каждого момента платформы, уведомлениями и личными сообщениями. Еще на главной странице вверху можно будет удобно закреплять посты буквально в одну строку, то есть не целая карточка поста закрепленного будет, а одна строка с кастомным названием типа "Результаты розыгрыша за апрель" (в названии этой строки будете писать сами что хотите и указываете пост на который ссылаетесь) и клик по строке ведет на соответствующий пост.
Что скажите по мобильной версии и новой теме? Стоит ли возвращать старую синюю?
Еще хотелось бы напомнить, чтобы вы можете ставить звездочки на гитхабе (клик) - это супер классно и невероятно мотивирует. Не меньше мотивирует и то, что вы можете рассказывать о проекте где это будет интересно и где это уместно. Отклик и Ваша заинтересованность супер мотивирует. Спасибо
Так же напоминаю, если у Вас есть возможность поддерживать проект финансово, чтобы хотя бы оплачивать сервер, то Вы можете подписаться на Boosty.
Отдельное спасибо первому патрону на бусти, которого привлек этот проект:
- Андрей Федорко
Всем хороших праздников и выходных. Всего доброго )

310
562
Mastodon + Docker + Portainer + Nginx. Открываем двери федивёрса на VPS Ubuntu 22.04
Mastodon — социальная сеть микроблогов, использующая в своей основе модель децентрализации, называемую также федерацией, федиверсом или федеративной вселенной реализованную с помощью протокола ActivityPub. Он имеет открытый исходный код и может быть развернут любым пользователем на любом сервере, причем каждый сервер является полностью самостоятельным экземпляром Mastodon. Каждый экземпляр может иметь свои собственные политики, правила модерации и управляется только Вами. В федерации нет корпоративных модераторов от компаний-разработчиков, управление всем контентом осуществляется только Вами и никто извне не сможет вас ограничить в рамках вашего экземпляра. В федерации, состоящей из всех пользовательских серверов Mastodon (и других сервисов, например Pixelfed, PeerTube и т. д.), каждый экземпляр может быть осведомлен о других экземплярах по протоколу ActivityPub. Это позволяет взаимодействовать с другими пользователями как внутри одного сервера, так и между разными экземплярами Mastodon.-
Это руководство - перенос моей существующей статьи из self-host инстанса Ghost. На сегодняшний день последняя актуальная версия Mastodon 4.2.5. Однако, там фундаментально ничего не изменилось с 4.2.1 и все будет работать так, как описано в этом руководстве. В ближайшее время я постараюсь обновить это руководство, т.к. если с актуальностью технической части все хорошо, то некоторые логические последовательности стоит переписать для простоты понимания.-
В этом руководстве вы установите последнюю версию Mastodon, которая на момент написания статьи является 4.2.1. Настроите свой сервер Ubuntu 22.04, установите Docker для работы с контейнерами, а так же настроите Portainer для управление Вашими Docker-контейнерами через графический интерфейс, а не командную строку. Также установите proxy-сервер nginx и получите для всех своих сервисов SSL/TLS Let's Encrypt сертификаты для защищенного HTTPS соединения.
Данное руководство можно считать расширенной версией большинства руководств. Я не буду рассказывать «как запустить инстанс за 2 минуты», чтобы потом удалить его через 20 минут, т. к. он совершенно не подходит для дальнейшего развития. Я расскажу как сделать правильно и визуально приятно глазу, чтобы вы сами видели результат своих трудов и могли смотреть логи и ошибки в графическом интерфейсе, а не в черном окошке. Поэтому тут будут моменты, когда было бы проще сделать «все в одной корзине» и Вы бы просто ноубрейн вставляли код, но я буду объяснять почему так и зачем. Чтобы в дальнейшем вы понимали что Вы делали и зачем это делали. Мы будем идти немного более сложным путем, чем могли бы, но это Вам пригодится потом, когда Вы, например, захотите вынести свою базу данных на отдельный VPS сервер и держать ее там для повышения производительности и безопасности. Опыт из данного руководства подготовит Вас к этому. Так что старт будет немного тернист, но перспективы прекрасны.-
Что у Вас уже есть перед началом:
- Арендованный у cloud-провайдера VPS сервер Ubuntu 22.04 (рекомендую Timeweb);
- Ваш домен куплен и его DNS А-записи указывают на IPv4 адрес вашего VPS сервера (например example.com).
- Создан поддомен для Portainer и А-записи поддомена также указывают на IPv4 адрес вашего VPS сервера (например panel.example.com).
Подготовка
Для начала установим весь необходимый софт для установки и дальнейшего обслуживания нашей ноды Mastodon:
sudo apt install wget curl nano software-properties-common dirmngr apt-transport-https gnupg gnupg2 ca-certificates lsb-release ubuntu-keyring unzip ufw htop -y
Зададим политики файрвола, чтобы контролировать порты на Вашем сервере:
sudo ufw allow OpenSSH sudo ufw allow httpsudo ufw allow https
И включим файрвол:
sudo ufw enable
Это все, что необходимо сделать с сервером для стабильной работы. Дальше устанавливаем Docker, который в дальнейшем будет отвечать за контейнеры Mastodon.
Сначала убедимся, что у Вас нет никаких старых установок Docker и от него нет никакого мусора в системе, а если есть, то удалим это:
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
Обновляем систему:
sudo apt-get update
Добавляем APT репозиторий Docker на сервер:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Теперь устанавливаем сам Docker:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Удостоверимся, что Docker работает правильно:
sudo docker run hello-world
Чтобы два раза не возвращаться к Docker, сразу установим к нему Portainer. Это делается в пару команд.
Для начала создадим docker-том, где будет работать Portainer:
docker volume create portainer_data
И установим сам Portainer:
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
Все. Теперь, если Вы перейдете по вашему IP адресу сервера (например х.х.х.х:9443) или по адресу домена, который вы привязали к вашему серверу (например panel.example.com:9443), то вы попадаете в графический интерфейс панели управления Portainer, который в свою очередь управляет Docker установленный на вашем сервере. В дальнейшем Вы избавитесь от необходимости указывать порт 9443 для доступа к Portainer.
Установка Mastodon
Для начала Вам необходимо создать директории на нашем сервере, где Вы будете хранить ваши данные. Я предпочитаю все разделять по директориям, чтобы потом понимать где что искать.
Начнем с создания директорий для базы данных:
sudo mkdir -p /opt/database/mastodon/{postgresql,pgbackups,redis}
и сразу задаем права для директории:
sudo chown 70:70 /opt/database/mastodon/pgbackups
Точно так же создаем директорию для данных самого Mastodon:
sudo mkdir -p /opt/mastodon/web/{public,system}
sudo mkdir -p /opt/mastodon/branding
И задаем права для них:
sudo chown 991:991 /opt/mastodon/web/{public,system}
После этого идем в установленный ранее Portainer. При первом запуске он предложит вам зарегистрировать аккаунт администратора.
примечание:при первом заходе в Portainer, возможно он Вас попросит перезагрузить свой контейнер и не даст создать аккаунт. В этом случае просто перезагрузите контейнер командой docker restart portainer_agent и зайдите в Portainer снова.-
После входа в Portainer перейдите на вкладку Home и нажмите Live Connect к вашему единственному серверу. Дальше в левой части экрана у вас появится новое меню, где Вас интересует пункт Stack. Переходите в него и нажимайте +add stack.
Перед Вами откроется окно для создания файла docker-compose.yml, выглядит это так:

Здесь вводите название на Ваш выбор. Главное, чтобы Вы сами понимали что оно означает. Например mastodon_db, а в основное окно ниже вставляем следующий код:
version: '3'
services:
postgres:
image: postgres:15-alpine
container_name: postgres
shm_size: 512mb
restart: always
network_mode: bridge
ports:
- '5432:5432'
healthcheck:
test: ['CMD', 'pg_isready', '-U', 'postgres']
volumes:
- postgresql:/var/lib/postgresql/data
- pgbackups:/backups
environment:
- 'POSTGRES_HOST_AUTH_METHOD=trust'
redis:
image: redis:7-alpine
container_name: redis
restart: always
network_mode: bridge
ports:
- '6379:6379'
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
volumes:
- redis:/data
redis-volatile:
image: redis:7-alpine
container_name: redis-volatile
restart: always
network_mode: bridge
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
volumes:
postgresql:
driver_opts:
type: none
device: /opt/database/mastodon/postgresql
o: bind
pgbackups:
driver_opts:
type: none
device: /opt/database/mastodon/pgbackups
o: bind
redis:
driver_opts:
type: none
device: /opt/database/mastodon/redis
o: bind
После этого внизу экрана нажимаем Deploy the stack и ждем, когда появится уведомление о удачном развертываний Ваших баз данных.
Теперь точно так же делаем новый Stack, называем его как-то удобно для Вас, например mastodon_web и копируем следующий код:
version: '3'
services:
website:
image: ghcr.io/mastodon/mastodon:v4.2.1
container_name: website
env_file: stack.env
command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
restart: always
ports:
- '127.0.0.1:3000:3000'
networks:
- internal_network
- external_network
healthcheck:
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:3000/health || exit 1']
volumes:
- uploads:/mastodon/public/system
streaming:
image: ghcr.io/mastodon/mastodon:v4.2.1
container_name: streaming
env_file: stack.env
command: node ./streaming
restart: always
ports:
- '127.0.0.1:4000:4000'
networks:
- internal_network
- external_network
healthcheck:
test: ['CMD-SHELL', 'wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1']
sidekiq:
image: ghcr.io/mastodon/mastodon:v4.2.1
container_name: sidekiq
env_file: stack.env
command: bundle exec sidekiq
restart: always
depends_on:
- website
networks:
- internal_network
- external_network
healthcheck:
test: ['CMD-SHELL', "ps aux | grep '[s]idekiq\ 6' || false"]
volumes:
- uploads:/mastodon/public/system
shell:
image: ghcr.io/mastodon/mastodon:v4.2.1
container_name: shell
env_file: stack.env
command: /bin/bash
restart: "no"
networks:
- internal_network
- external_network
volumes:
- uploads:/mastodon/public/system
- static:/static
networks:
external_network:
internal_network:
internal: true
volumes:
uploads:
driver_opts:
type: none
device: /opt/mastodon/web/system
o: bind
static:
driver_opts:
type: none
device: /opt/mastodon/web/public
o: bind
Ниже этого окна будет еще один пункт, где вам нужно нажать advanced mode:

В открывшееся окно вставляем значение переменных окружения (.env), которыми будет пользоваться Ваш Mastodon:
LOCAL_DOMAIN=example.com
SINGLE_USER_MODE=false
SECRET_KEY_BASE=257935a5e78673fc79b8888888888bb8a9e58452aa8aab6896794b0e31d8b77cf8888888888a96818913f3213472bb857e3f888888888805da1daeb1e88b9b53
OTP_SECRET=03d99fe6cd8dd316bb962ec366888888888822fcc0b4a31795554b5cee1f5d685fd88888888885c96979ca4f8097851cf5ca898888888888dfcdaa6ddce2fae1
VAPID_PRIVATE_KEY=8hk8888888888RpDA8888888888ZWHm9KYKIpIrhMj0=
VAPID_PUBLIC_KEY=BPZJW5ieC8888888888Zo9uyp05qUn1tbi8888888888Dxa81dvabtLCxgLUg94fliD4UJyY8888888888885lFdE=
DB_HOST=postgres
DB_PORT=5432
DB_NAME=postgres
DB_USER=mastodon
DB_PASS=verysecretpass
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
SMTP_SERVER=smtp.timeweb.ru
SMTP_PORT=25
SMTP_LOGIN=admin@example.com
SMTP_PASSWORD=0088664422
SMTP_AUTH_METHOD=plain
SMTP_OPENSSL_VERIFY_MODE=none
SMTP_ENABLE_STARTTLS=auto
SMTP_FROM_ADDRESS=Mastodon
примечание:Тут присутствует настройка отправки почты через SMTP. Настоятельно рекомендую это сделать. Получите почтовый ящик к своему домену там же, где вы купили домен. В примере выше указаны настройки для Timeweb, в данном случаем Вам нужно будет изменить только SMTP_LOGIN и SMTP_PASSWORD на свои и все. Так же нужно заменить SMTP_FROM_ADDRESS на что-то свое. SMTP_FROM_ADDRESS задает какой адрес электронной почты отправителя будут видеть Ваши пользователи, когда им будут приходить письма от Вас с уведомлениями или регистрацией.-
После этого нажимаем Deploy the stack и ждем, когда все контейнеры загрузятся. Это может занять несколько минут.
примечание:при попытке первого деплоя этого стака, возможно, спустя пару минут у Вас появится ошибка в верхнем правом углу о неудаче. Ничего страшного, можно сказать что так и должно быть. Нажимаем еще раз Deploy the stack и он запустится без ошибок, если вы все сделали до этого правильно.-
На данном этапе у Вас есть 2 стака, в первом находится база данных, во втором сам Mastodon. Сейчас нужно будет объединить их в одну сеть, чтобы они друг друга видели и могла общаться между собой. Для этого во втором стаке с Mastodon поочередно заходите (просто нажимайте на название) в каждый из контейнеров website, streaming и sidekiq и в самом низу страницы добавляйте их в сеть базы данных выбрав нужную сеть нажатием на Select a network. Если вы стак с базой данных назвали как я предлагал выше, то нужная сеть будет называться mastodon_db_default. Каждый из контейнеров website, streaming и sidekiq должен быть сразу в двух сетях - в своей родной и сети от базы данных, в данном случае в mastodon_db_default.
Конфигурация контейнеров
В целом мы сделали уже самое основное и по большому счету нам осталось это между собой подружить и запустить в рабочем состоянии, т.к. сейчас это запущено, но ничего не работает. И начинаем с конфигурации базы данных:
sudo docker run --name postgres14 -v /opt/database/mastodon/postgresql:/var/lib/postgresql/data -e POSTGRES_PASSWORD=verysecretpass --rm -d postgres:14-alpine
Заходим в контейнер с базой данных:
sudo docker exec -it postgres14 psql -U postgres
CREATE USER mastodon WITH PASSWORD 'verysecretpass' CREATEDB;
exit
sudo docker stop postgres14
На этом наша база данных настроена и готова в работе. Осталось закончить с конфигурацией контейнера Mastodon, но тут совсем просто, т.к. он будет просить Вас ввести те значения переменных (.env), которые вы указывали выше во время запуска второго стака. Например первое, что он попросит Вас указать - это адрес вашего домена example.com. В самом конце Вам будет предложено проверить правильность настройки SMTP и попросит ввести e-mail адрес куда отправить тестовое письмо. Сделайте это, чтобы быть уверенным, что все работает исправно. Когда будут введены все переменные из таблицы выше (то, что вы указывали в advanced mode в Portainer), соглашайтесь на предпоследний вопрос о подготовке базы данных и он Вам выдаст новый список переменных (для поля advanced mode в Portainer). Дальше будет предложено создать аккаунт администратора, от этого откажитесь.
Начинаем конфигурацию Mastodon:
sudo docker exec -it streaming bundle exec rake mastodon:setup
Скопируйте список переменных из терминала, вернитесь в Portainer во второй стак mastodon_web и вставьте теперь уже правильные переменные в advanced mode. После этого нажмите внизу страницы update the stack.
После того, как контейнеры будут перезагружены, убедитесь, что все 3 контейнера website, streaming и sidekiq находятся в сети mastodon_db_default, как мы это делали ранее заходя в каждый из них по очереди, если нет, то добавьте их снова в сеть.
Теперь нам осталось только создать администратора и на этом конфигурация самого Mastodon будет окончена. Для этого используйте пару следующих команд.
sudo docker exec -it streaming /bin/bash
Создаем аккаунт администратора:
RAILS_ENV=production bin/tootctl accounts create admin --email admin@example.com --role=Owner
Можете заменить admin на свой ник и почту тоже заменить на свою.
примечание:Обратите внимание, что после выполнения команды выше, в окне терминала будет указан пароль от этого аккаунта. Запишите его сразу куда-то, чтобы не потерять.-
Сразу подтверждаем свой аккаунт, без необходимости делать это через почту:
RAILS_ENV=production bin/tootctl accounts modify admin --confirm
Замените admin на Ваш ник, если вы его заменили в прошлой команде.
Выходим из контейнера:
exit
На этом экземпляр Mastodon готов и полностью готов к работе. Осталось дело за малым - выпустить SSL сертификаты и настроить nginx.
Установка NGINX
Импортируем ключи nginx:
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
| sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
Добавляем репозиторий стабильной версии nginx:
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg arch=amd64] \
http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
| sudo tee /etc/apt/sources.list.d/nginx.list
Обновляем систему:
sudo apt update
И устанавливаем nginx:
sudo apt install nginx
Запускаем сервис nginx:
sudo systemctl start nginx
Получаем SSL-сертификаты
Для получения сертификатов нам нужен Certbot. Есть разные версии, но мы будем использовать версию последнюю версию в Snapd:
sudo snap install core
sudo snap refresh core
Устанавливаем Certbot:
sudo snap install --classic certbot
Для исправной работы Certbot создаем символическую ссылку:
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Если вы правильно настроили свои DNS A-записи для своего домена, то следующая команда сгенерирует Вам SSL сертификаты:
sudo certbot certonly --nginx --agree-tos --no-eff-email --staple-ocsp --preferred-challenges http -m name@example.com -d example.com
Замените почту и адрес домена example.com на свои.-
Так же сразу получите сертификаты для своего поддомена для Portainer, заменив example.com на panel.example.com.
Получаем сертификат Diffie-Hellman:
sudo openssl dhparam -dsaparam -out /etc/ssl/certs/dhparam.pem 4096
Убедитесь, что обновление сертификатов работает исправно, выполнив следующую команду:
sudo certbot renew --dry-run
Настраиваем NGINX
Открываем файлик конфига с помощью редактора nano:
sudo nano /etc/nginx/nginx.conf
В открывшемся файле найдите строку include /etc/nginx/conf.d/*.conf; и перед этой строкой вставьте следующую строку:
server_names_hash_bucket_size 64;
Нажмите Ctrl + S, чтобы сохранить изменения и закройте редактор нажатием Ctrl + X.
Создайте и откройте в редакторе файл конфигурации Nginx:
sudo nano /etc/nginx/conf.d/mastodon.conf
и вставьте туда следующий код:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream backend {
server 127.0.0.1:3000 fail_timeout=0;
}
upstream streaming {
# Instruct nginx to send connections to the server with the least number of connections
# to ensure load is distributed evenly.
least_conn;
server 127.0.0.1:4000 fail_timeout=0;
# Uncomment these lines for load-balancing multiple instances of streaming for scaling,
# this assumes your running the streaming server on ports 4000, 4001, and 4002:
# server 127.0.0.1:4001 fail_timeout=0;
# server 127.0.0.1:4002 fail_timeout=0;
}
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;
server {
listen 80;
listen [::]:80;
server_name example.com;
root /opt/mastodon/web/public;
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
ssl_protocols TLSv1.2 TLSv1.3;
# You can use https://ssl-config.mozilla.org/ to generate your cipher set.
# We recommend their "Intermediate" level.
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Uncomment these lines once you acquire a certificate:
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
keepalive_timeout 70;
sendfile on;
client_max_body_size 99m;
root /opt/mastodon/web/public;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon;
location / {
try_files $uri @proxy;
}
# If Docker is used for deployment and Rails serves static files,
# then needed must replace line `try_files $uri =404;` with `try_files $uri @proxy;`.
location = /sw.js {
add_header Cache-Control "public, max-age=604800, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}
location ~ ^/assets/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}
location ~ ^/avatars/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}
location ~ ^/emoji/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}
location ~ ^/headers/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}
location ~ ^/packs/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}
location ~ ^/shortcuts/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}
location ~ ^/sounds/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;
}
location ~ ^/system/ {
add_header Cache-Control "public, max-age=2419200, immutable";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'none'; form-action 'none'";
try_files $uri @proxy;
}
location ^~ /api/v1/streaming {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Proxy "";
proxy_pass http://streaming;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
tcp_nodelay on;
}
location @proxy {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Proxy "";
proxy_pass_header Server;
proxy_pass http://backend;
proxy_buffering on;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_valid 410 24h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cached $upstream_cache_status;
tcp_nodelay on;
}
error_page 404 500 501 502 503 504 /500.html;
}
В этом коде есть 4 места, где нужно заменить example.com на ваш адрес домена Mastodon.
И сразу создайте файл конфига Nginx для Portainer:
sudo nano /etc/nginx/conf.d/portainer.conf
И вставьте туда следующий код:
upstream portainer {
server 0.0.0.0:9443;
}
server {
listen 80;
server_name panel.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
# The block below works only for the selected domains
server_name panel.example.com;
ssl_certificate /etc/letsencrypt/live/panel.example.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/panel.example.com/privkey.pem; # managed by Certbot
ssl_trusted_certificate /etc/letsencrypt/live/panel.example.com/chain.pem;
access_log /var/log/nginx/portainer.access.log;
error_log /var/log/nginx/portainer.error.log;
location / {
proxy_pass https://portainer/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
location /api/websocket/ {
proxy_pass https://portainer/api/websocket/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
}
В этом коде так же есть 4 места, где нужно заменить panel.example.com на ваш адрес поддомена Portainer.
Теперь перезапустим Nginx командой:
sudo systemctl restart nginx
Если все работает исправно, что никакого вывода в терминале не будет. Вы перезапустите Nginx и все.
Можете проверить синтаксис файлов конфигов Nginx следующей командой:
sudo nginx -t
Теперь, если вы перейдете по адресу своего домена example.com, то у вас будет открываться Ваш экземпляр Mastodon.
А переход по адресу panel.example.com будет открывать Ваш Portainer.
На этом все. У вас есть возможность отслеживать логи своего инстанса Mastodon, его поведение, обновление, поддержка и много другой работы через графический интерфейс на вашем домене Portainer, без необходимости постоянно открывать терминал и подключаться к VPS по SSH. Теперь дело за Вами, как Вы будете это развивать и администрировать. Удачи Вам на просторах федиверса!

Послесловие
Решил перенести сюда свое старое руководство, чтобы посмотреть в деле как пишутся большие посты на сайте. Ну и за одно открыть selfhosted сообщество. К тому же, теперь будет мотивация когда нибудь обновить это руководство, раз оно тут уже )

691
979
634
Сейчас обсуждают
Загрузка комментариев...