Что Мистер Пырсус изволит кушать?

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

  • Обычно пишут, что домашних ласок кормят (замороженными) мышами или цыплятами. Это рекомендованное питание, так как оно приближено к рациону в дикой среде.
  • Но помимо этого заводчица Перрика отправляла достаточно большой список разрешенных продуктов (можно прочесть, что можно хорькам). Там были и субпродукты, и овощи, и различные виды мяса, и даже рыба.
  • Пока Перри был маленьким, у него еще не было острых клыков, он кушал куриный фаршик, печень. Интереса к другим продуктам из списка он не проявлял. Хотя я старалась предлагать и перепелиные яйца, и креветку, и другие продукты, чтобы питание было разнообразным. Я проводила определенные параллели с тем, как мы выращивали щенков.
  • Когда Перри вырос у него сформировались четкие предпочтения. Если обЭд не удовлетворял хвостатую персону, то он показательно голодал. Мое материнское сердце, естественно, не выдерживало. И Пырсус получал то, что хотел.... В этот список входят: филе курицы/индейки, сердечки, желудочки, мышки (голыши).
  • Хотя, когда Перри был в гостях у бабушки, то он и говядину ел. А у меня не ел. дети...
  • Но иногда в чате ласкового начиналась полемика по поводу питания. И вот тут открывается портал в ад. Кто-то кричал, что ласки - облигатные хищниик. Нужно кормить только цыплятами и все. Кто-то скандировал, что основной корм - мыши, от цыплят в рационе развиваются болезни. И тут получилось, что Перри я кормлю в корне неверно. Он получает мышей только пару раз в неделю, потому что не любит такой корм и часто недоедает. В основном он ест магазинное филе.
  • И тогда я плавно перевела его только на мышей. Поведение изменилось кардинально за 1-2 недели. Пырюшка Плюшка превратился в Убийцу Пырсуса. Кусачий, нервный, шерсть дыбом.
  • Я проконсультировалась с заводчицей и ветеринаром. Вот как быть? Пихать ему то, что он "должен" есть или то, что любит есть? И мне сказали кормить так, как я кормила до этого. Малыш выглядит отлично, он игривый, шерсть блестящая. Что еще нужно? Филе, желудки, сердца и мыши только пару штук в неделю как "витамины" для профилактики различных заболеваний.
  • Ласки все разные. Как и собаки, и кошки, и мы с вами. И пищевое поведение у них тоже может быть разное. Заводчица уже не первый раз слышала такой рассказ про "озверение".
  • Поэтому резюме: 1) ласка не помрет и не будет "жить в ужасных муках", если кормить ее не только грызунами/цыплятами; 2) питание может влиять на характер; 3) на магазинном мясе ласка тоже может жить вполне долгую и счастливую жизнь.

Спасибо за внимание ♡ 🐾

Что Мистер Пырсус изволит кушать?

0

43

👤 Улучшения профиля пользователя: загрузка аватара, социальные ссылки и настройки

В рамках последних обновлений я значительно улучшил функциональность профиля пользователя в своём приложении на базе Next.js, Tailwind CSS, Radix UI и Prisma. Ниже рассказываю, что было реализовано, какие задачи решены и как это повлияло на пользовательский опыт.

📸 Загрузка аватара и обновление профиля

✅ Что сделано:

  • Добавлена возможность загрузки аватара пользователя через новую функцию updateUserAvatar.
  • Использован пакет @radix-ui/react-avatar для управления отображением аватаров.
  • Обновлена модель User в Prisma schema:
    • Поле fullName теперь уникальное.
    • Добавлено новое поле telegram для связи.
    • Поле fullName теперь уникальное.
    • Добавлено новое поле telegram для связи.
  • В updateUserInfo теперь также можно сохранять информацию пользователя (О себе).

🔒 Валидация данных:

  • Использованы Zod-схемы для валидации всех форм профиля (настройки, соцсети и др.).
ImageImage

🌐 Социальные ссылки и улучшения интерфейса

✨ Новый функционал:

  • Создан хук useProfileSocialForm — упрощает управление полями с социальными ссылками.
  • Расширена страница профиля: теперь отображаются дополнительные поля пользователя (bio, Telegram, и др.).

🎨 Обновления в UI:

  • Обновлены компоненты ProfileSettingsPage и ProfileSettingsSocial — теперь код более структурирован и читаем.
  • Удалён устаревший app-sidebar и заменён на адаптированный admin-sidebar для панели администратора.
  • Иконки в профиле теперь отображаются без прозрачности (opacity), улучшена читаемость и контраст.
ImageImage

🧑‍💻 Пользовательский опыт

Улучшения:

  • Все изменения в профиле теперь интуитивно понятны и мгновенно отображаются.
  • Валидация защищает от некорректных данных, а обновлённый интерфейс делает настройки профиля проще.
  • Загрузка аватара работает плавно, даже на мобильных устройствах.

0

48

🚀 Обновление административной панели и функциональности WoW-патчей в Next.js + Tailwind CSS

В этом обновлении я значительно улучшил административный интерфейс и логику работы с патчами World of Warcraft в моём проекте на базе Next.js 15, Tailwind CSS, ShadCN, и Turbopack. Ниже рассказываю, какие задачи были решены и какие компоненты были реализованы.

👥 Управление пользователями: новая админка

Image

✅ Основные улучшения:

  • Создана страница AdminUsersPage, где администраторы могут просматривать и управлять пользователями.
  • Хук useAdminUsers реализует логику получения данных, поиска, пагинации и обновления состояния пользователей.
  • Компонент AdminUsersPanel объединяет таблицу, строку поиска и пагинацию в единую панель.
  • Добавлен компонент строки пользователя — позволяет менять роль пользователя прямо из таблицы.
  • Реализована система пагинации и фильтрации по имени/почте.
  • Добавлен метод updateUserRole для смены ролей через API.

🛠 Работа с патчами World of Warcraft

Image

Новые функции:

  • addPatchAction теперь возвращает добавленный патч и запускает повторную валидацию страницы.
  • updatePatchAction добавлен для обновления патчей с валидацией и повторной подгрузкой данных.
  • В компонент add-patch-wow добавлены функции редактирования и удаления патча, включая модальное окно для редактирования.
  • Функция удаления теперь также инициирует повторную валидацию страницы.

🧱 Новые UI-компоненты

  • Sidebar: адаптивная боковая панель с контекстным управлением состоянием (открытие/закрытие на мобильных).
  • Table: компонент таблицы для структурированного отображения данных.
  • Pagination: UI-контролы для переключения страниц с пользовательскими стрелками и логикой.
  • useIsMobile: хук для определения мобильного устройства и адаптации интерфейса.

🔐 Аутентификация и структура API

  • Создан клиент API с конфигурацией базового URL и авторизацией через токен.
  • auth service реализует регистрацию, вход и управление сессией.
  • Оптимизирован импорт в auth-options.ts — устранён лишний код.

0

70

23.05.2025

Что новенького? Серверные сводки

Приветствую! )

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

Что происходит?

Изначально Stormic был монолитом на Next.js, где клиент и сервер жили в одном приложении. Это удобно на старте, но по мере роста проекта такой подход тормозит развитие: увеличивается сложность кода, растут задержки и все остальные прелести. Чтобы избавиться от накопленного долга, я разбиваю систему на две части:

  1. Тонкий клиент на Next.js – отвечает исключительно за рендеринг и базовую логику взаимодействия с пользователем.
  2. Толстый сервер на Go – вся бизнес-логика, обработка данных и интеграции выносятся на бэкенд.

Новый сервер: как работает?

  • Язык и производительность Сервер полностью переписан на Go – это даёт нам высокую скорость исполнения и низкое потребление ресурсов.
  • gRPC внутри Я выбрал gRPC для внутреннего API и связи между микросервисами. Благодаря бинарному протоколу сократились задержки и объёмы передаваемых данных.
  • REST-прокси для внешних интеграций Чтобы сторонние разработчики не ломали головы над gRPC, прямо поверх него работает REST-слой. Вы можете отправлять привычные HTTP-запросы, а сервер автоматически проксирует их во внутренний gRPC и обратно. Это вам понадобится, если вы в будущем захотите, например, написать бота который будет модерировать ваше сообщество.
  • OpenAPI и Swagger REST-эндпойнты документируются по стандарту OpenAPI. Генерируется удобная Swagger-документация – быстрое погружение в API и тестирование прямо из браузера.
  • GraphQL для клиента Внутренние запросы от клиента теперь идут по GraphQL. Это позволяет запрашивать ровно столько данных, сколько нужно, и сводит к минимуму избыточные обращения к серверу.
  • ORM Ent и PostgreSQL Для работы с базой данных используется мощный и типобезопасный Ent ORM.

Реальный прирост скорости

Одно из главных достижений – сокращение числа запросов.

  • Раньше: чтобы получить информацию о правах пользователя, шло до 6 REST-запросов.
  • Сейчас: один GraphQL-запрос возвращает всё сразу.

Аналогичная ситуация с отрисовкой хэдера: раньше задействовалось до 4х запросов (сам заголовок плюс два модальных окна), а теперь – всё в одном запросе. Благодаря этому клиент стал «тоньше» и реагирует на действия мгновенно.

Кэширование запросов

Добавлен многоуровневый кэш:

  1. Прямой запрос на сервер – для самых свежих данных.
  2. «Сначала кэш, потом сервер» – показываем данные из кэша, затем обновляем, если что-то изменилось.
  3. Только из кэша – не идём в базу, если нам важна скорость отображения, а данные уже есть.

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

Порты и маршрутизация

Сервер работает сразу на трёх портах:

  • 8080 → GraphQL Для внутренних запросов от клиента.
  • 4080 → REST HTTP Входящие REST-запросы превращаются в gRPC и обратно.
  • 4000 → чистый gRPC Для микросервисов и тех, кто готов работать напрямую по протоколу gRPC.

Что дальше?

На этом этапе архитектура сервера практически готова. Остаётся дописать на клиенте запросы под GraphQL и настроить обработку ответов – однотипная, но важная работа. Судя по планам, я даже +/- успеваю уложиться в отведённый месяц.

После полного перехода на новую бэкенд-часть запланированы:

  1. Личные сообщения и уведомления
  2. Улучшенная работа с изображениями
    • Кадрирование и выбор области показа.
    • Управление порядком отображения при загрузке нескольких картинок.

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

Если вам нравится то, что тут происходит, то

Boosty

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

Что новенького? Серверные сводки

0

128

Рефакторинг редактора слотов и новый редактор сложности: очередной шаг вперёд

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

Новый useGearSlotEditor хук

Теперь управление состоянием предметов, сокетами и чарами в слотах происходит через кастомный хук useGearSlotEditor. Это позволило:

  • Упростить компонент GearSlotEditorDialog;
  • Централизовать всю бизнес-логику редактирования;
  • Ввести ограничения на количество сокетов и чаров в зависимости от типа слота.

Дополнительно добавлены toast-уведомления — пользователи теперь получают обратную связь при сохранении изменений или ошибках.

🎚 Редактор эффективности класса

Добавлен совершенно новый компонент — Difficulty Bar Editor. Он включает:

  • Хук useDifficultyEditor для управления рейтингом эффективности класса и его сохранением;
  • Обновлённый GuideDifficultyBar, который теперь динамически реагирует на изменения;
  • Интеграцию с новым API для обновления данных о эффективности.

🧼 Улучшение структуры GuideEditor

Компонент GuideEditor был переработан:

  • Улучшено визуальное расположение элементов;
  • Удалён устаревший и неиспользуемый код;
  • Улучшена читаемость и поддерживаемость.

🌍 Локализация названий слотов

Теперь все названия слотов экипировки отображаются на русском языке:

  • Добавлена локализация в slot-backgrounds.ts;
  • Интерфейс стал более понятным и дружелюбным для русскоязычных пользователей.

🧩 Новый API для данных эффективности

Для управления рейтингом эффективности реализован серверный API:

  • Обработка PATCH-запросов для обновления данных;
  • Логика сохранения и обновления записей реализована на сервере.

Это обновление значительно упростило архитектуру и улучшило пользовательский опыт. 

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

ImageImage

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

ImageImage

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

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


Рефакторинг редактора слотов и новый редактор сложности: очередной шаг вперёд

0

202

Время нового мини отчета

Улучшение управления экипировкой: рефакторинг BisGearEditor и связанных компонентов

На этой неделе я провёл серьёзную переработку системы редактирования экипировки в моём проекте. Основная цель — сделать интерфейс удобнее, а структуру данных — чище и гибче. Вот основные изменения:

🛠 Рефакторинг BisGearEditor Теперь компонент BisGearEditor использует GuideSpecGearProps, что позволяет эффективнее работать с данными экипировки и упростить логику внутри компонента.

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

🧩 GearSlotEditorDialog Добавлен новый диалог для редактирования деталей конкретного слота экипировки. Это делает редактирование наглядным и более удобным.

🌐 Серверный API для обновления данных Добавлен серверный маршрут, который позволяет обновлять информацию об экипировке напрямую в базе данных. Это ещё один шаг к полноценному CRUD-интерфейсу.

🎨 UI-улучшения Интерфейс получил улучшенные подсказки (tooltips) и более логичную компоновку слотов. Также введён утилитный модуль slotBackgrounds, который помогает управлять фоновыми изображениями для разных слотов.

📚 Обновление GuideEditor и GuidePageContent Я адаптировал эти компоненты под новую структуру данных и API. Теперь они интегрируются с обновлённой системой редактирования экипировки без лишней логики.

Это обновление серьёзно повысило удобство работы с редактором Best-in-Slot снаряжения и заложило прочную основу для будущих улучшений. 

А теперь время скринов и быстрых объяснений

Image

Я полностью переработал компонету для бис листов, теперь она избавилась от тоны кода и стала удобной в работе и поддержке.
Я уже 3й раз перерабатываю модель для ранения данных о бис листах и сегодня меня осенило и я смог реализовать наконец то что мне было нужно, теперь для слотов я использую enum с прописаными слотами и у гемов тоже теперь есть прописаный слот, что избавило меня от головной боли с рендером в нужные слоты и от бд на 100+ строк

ImageImage

Так же реализовал редактор бис листа с тултипами чтоб был более понятный интерфейс

Image

Редактирование происходит в диалоговом окне

Image

Я достаю id предметов с ссылок wowhed и через blizzard api получаю их картинку и название, редактор уже рабочий и работает исправно, как минимум по моей быстрой проверке. Для пустых слотов тоже сделал заглушку 

Image

Нужно только добавить локализацию, чтоб данные слотов конвертило на русский 

Если интересно — следите за обновлениями!

Время нового мини отчета

0

135

Финальный пост по фильтрам

Оптимизация и улучшения в работе с гайдами

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

⚡ Кэширование данных

  • Реализовано кэширование получения данных гайдов с использованием unstable_cache, с временем повторной валидации в 1 час. Это значительно снижает нагрузку на сервер при повторных запросах.
  • Добавлено кэширование фильтров по режимам и специализациям, что улучшает скорость отклика интерфейса при выборе параметров.

💡 Обновление компонентов

  • Компонент ClassGuidesPage был обновлён для использования закэшированных данных, что положительно сказалось на его производительности.
  • Компонент отображения кнопки гайда (GuideButton) получил расширение: теперь он показывает больше информации о гайде, делая интерфейс более информативным и полезным.

🧼 Рефакторинг и улучшение читаемости

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

📦 Новый API-роут

  • Реализован новый маршрут API для фильтрации гайдов по различным критериям, включая класс, специализацию, роль и режим.

✅ Результат

В результате этих изменений проект стал:

  • Быстрее
  • Более читаемым и поддерживаемым
  • Готовым к масштабированию и добавлению новых функций

Эти улучшения — шаг вперёд к стабильной и гибкой системе гайдов для WoW-сообщества.

ImageImage

Теперь перешел к реализации slug дальше планирую делать структуру страници гайдов

Для желающих покликать что я там накодил ссылка

0

173

15.05.2025

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:
Image
  • Выбираете PostgreSQL, задаете имя сервиса (тут stormic-db в примере)
  • Задаете Database Name, Database User и Database Password:
Image
  • Нажимаете Create
  • Заходите в новую созданную базу данных, внизу страницы в External Credentials задаете порт, ну например 5442
Image
  • Нажимаете кнопку 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
Image
  • Нажимаете Save и переходите на вкладку Environment и сохраняете там следующее
RABBITMQ_DEFAULT_USER=stormic
RABBITMQ_DEFAULT_PASS=very_secret_password
RABBITMQ_DEFAULT_VHOST=/
RABBITMQ_MANAGEMENT_PORT=15672
RABBITMQ_AMQP_PORT=5672
Image
  • Нажимаете 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
Image
  • Нажимаете 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
Image
  • В хост задаете ваш домен, который будет использоваться для платформы.
  • 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.

Отдельное спасибо первому патрону на бусти, которого привлек этот проект:

  • Андрей Федорко

Жду ваших вопросов в коментах

Спасибо, что проявляете интерес к проекту. Вместе мы сможем многое )

Stormic: установка на VPS и локально

0

225

Начал делать страницу гайдов class-guides

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

Закончил делать функционал добавления гайда 

Image

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

Так же по мере надобности пополняю модели в схеме призмы, и приступаю к работе над рендером guides-page

Image

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

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

Начал делать страницу гайдов class-guides

0

201

12.05.2025

Фидбэк и то, что должно работать, но работает неочевидно

Добрый день

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

  1. Коменты в постах грузятся долго;
  2. Нужно скрыть меню с точками в шапке постов, если у вас нет каких-либо резрешений на модерацию или измененя поста;
  3. Добавить возможность просматривать изображения в теле поста;
  4. В админке сообщества, модальные окна для блокировки пользователей не оптимизированы под мобильные устройства;
  5. В админке сообщества светлая тема имеет некорректные цвета из-за чего некоторые элементы сложно читаемы;
  6. Переработать отображение даты публикации поста, чтобы оно было не под кнопкой подписки;
  7. Убрать возможность вступать и покидать свои же сообщества, где вы являетесь владельцем, пока не передадите сообщество новому владельцу;
  8. Исправить все, что касается поисковой выдачи в поисковиках.

Это касаемо ошибок.

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

Фидбэк и то, что должно работать, но работает неочевидно

0

191

Небольшой отчет о проделанной работе

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


ImageImage

Сохранения работает через поиск уник ид которые присваиваются табам через 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

Небольшой отчет о проделанной работе

0

223

Небольшой пост по проделанной работе

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


Image

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

Image

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

Image

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

Небольшой пост по проделанной работе

0

226

07.05.2025

Обновление v0.0.3-beta

Что новенького?

  1. Ну из очевидного - появилась мобильная версия. Не без грехов конечно, однако для первого времени сойдет. Потихоньку допилим все шероховатости, отступы, состояния и прочие мелочи;
  2. Новая тема. Старая темная тема никуда не делась, верну ее в каком-то ближайшем обновлении и можно будет выбрать из 3х тем: светлая, эта черно-желтая и старенькая темно-синя;
  3. Оптимизирована работа с изображениями. Больше мы не грузим исходное изображение, а везде уже использует ужатые и оптимизированные к вебу. Тем не менее, вы все еще можете нажать на скрепку на прикрепленных изображениях в коментах и получить ссылку на исходное изображение, не ужатое;
  4. Миллиард оптимизаций компонентов и импортов, чтобы оно все двигалось немного быстрее.

Что дальше?

Думаю, что в ближайший месяц будут только исправления и минорные обновлениям, какие-то допиливания мобилки и вообще ui. А через месяц я постараюсь придти к Вам с огромным обновлением, которое значительно ускорит скорость работы абсолютно каждого момента платформы, уведомлениями и личными сообщениями. Еще на главной странице вверху можно будет удобно закреплять посты буквально в одну строку, то есть не целая карточка поста закрепленного будет,  а одна строка с кастомным названием типа "Результаты розыгрыша за апрель" (в названии этой строки будете писать сами что хотите и указываете пост на который ссылаетесь) и клик по строке ведет на соответствующий пост.

Что скажите по мобильной версии и новой теме? Стоит ли возвращать старую синюю? 

Еще хотелось бы напомнить, чтобы вы можете ставить звездочки на
гитхабе (клик) - это супер классно и невероятно мотивирует. Не меньше мотивирует и то, что вы можете рассказывать о проекте где это будет интересно и где это уместно. Отклик и Ваша заинтересованность супер мотивирует. Спасибо 

Так же напоминаю, если у Вас есть возможность поддерживать проект финансово, чтобы хотя бы оплачивать сервер, то Вы можете подписаться на Boosty.

Отдельное спасибо первому патрону на бусти, которого привлек этот проект:

  • Андрей Федорко

Всем хороших праздников и выходных. Всего доброго )

Обновление v0.0.3-beta

0

310

24.04.2025

Обновление v0.0.2-beta

Доброго времени суток.
Немного информации о том, что у нас тут обновилось за неделю

Что нового?

  1. Добавлены иконки ролей юзеров в посты и комментарии;
  2. Закрепление постов в левом меню навигации;
  3. Ссылки на социальные сети в левом меню платформы;
  4. Счетчик постов сообщества в карточке сообщества;
  5. Отдельная страница со всеми пользователями платформы.

Баги и исправления:

  1. Добавлены обработчики ошибок для юзеров, сообществ и постов. Теперь корректно отображается информация, если что-то не найдено, забанено или удалено;
  2. Исправлены сокеты для работы с корзиной s3. Теперь сокеты корректно разрывают соединение по таймауту и не уходят в оверкап, когда никакие изображения на платформе не загружаются, потому что нет свободных сокетов;
  3. Не загружались аватарки авторов постов на странице закладок и в ленте пользователя. Теперь работают корректно;
  4. Удалены элементы, которые не имели функционала. Потом вернутся, когда к ним будет добавлен функционал. Теперь выглядит немного чище;
  5. Исправлено отсутствие визуального выделения текста в темной теме;
  6. Множество мелких исправлений, отступов, цветов;
  7. Оптимизированы импорты и компоненты, чтобы структура проекта стала проще.

Какие ошибки известны?

  1. Немного некорректно формируется древо комментариев под постами, их вложенность.

Что дальше?

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

Всем спасибо )

Обновление v0.0.2-beta

0

562

17.04.2025

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, выглядит это так:

Image

Здесь вводите название на Ваш выбор. Главное, чтобы Вы сами понимали что оно означает. Например 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:

Image

В открывшееся окно вставляем значение переменных окружения (.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 будет окончена. Для этого используйте пару следующих команд.

Заходим в контейнер 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. Теперь дело за Вами, как Вы будете это развивать и администрировать. Удачи Вам на просторах федиверса!

Image

Послесловие

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

Mastodon + Docker + Portainer + Nginx. Открываем двери федивёрса на VPS Ubuntu 22.04

0

691