Mountain| Первый финал, первая победа и новый старт

Страницы:  1

Ответить
 

Professor Seleznov


За 25 дней нам удалось добиться довольно больших изменений в нашем проекте.
Мы провели:
  • Рефакторинг back-end сервисов
  • Убрали часть легаси кода на фронте
  • Переработали некоторый UI элементы и добавили плавности
  • Добавили новый функционал
Чем бы дитя не тешилось, лишь бы не плакало
Именно так мы подумали и решили завершать первый важный этап нашего MVP проекта. Мы подготовили всю инфраструктуру к работе, подбили UI что бы если и есть баги - то которые мы явно упустили за 30 часов тестирования.
Новый функционал
Мы добавили в приложение следующее:
  • Рассылка кодов авторизации
  • Поиск серверных чатов
  • Подписка у пользователя в сообщениях какая это роль
  • Переработали дизайн тредов, теперь у него больше настроек
  • Сохранение сообщений
Поиск общих чатов
pic
Не есть что конечно, слизано с приложения Discord, но мое виденье его внутри компании немного другого формата.
В чем вообще задумка его для корпоративного мессенджера? Для начала легко найти чаты внутри команд или структур. Группировка - это настраиваемые элементы, мы вывели их в отдельный конфиг, так что поправить под компанию - 5 минут. Настраивается из настроек чата.
pic
В будущем добавим и плейсхолдел для настроек, пока что так оставим.
Подписка у пользователя в сообщениях какая это роль
pic
Представим что к вам пришел новый сотрудник, а в команде 50 человек. И вот ему надо привыкнуть еще к тому кто разработчик, кто тестировщик, кто стрим лид и тп, а еще же ведь и правильных людей тегать. Эту проблему и позволяет решить данная приписка.
Переработали дизайн тредов, теперь у него больше настроек
Вот тут наверное самое важное, то что плохо реализовано в корпоративных тредах. Долго думали что же нам не хватает и как попытаться уместить все в обычную форму.
  • Мы расширили настройки на создание тредов
pic
Теперь кроме названия треда и иконки можно:
  • Проставить теги
  • Написать нормальное описание
pic
После создания тредов теперь мы можем не только посмотреть их список, но и так же:
  • Отфильтровать по названию, даже по одному слову в середине текста названия или описания
  • Отфильтровать по тегам, идет сортировка по часто используемым
  • Закрепить тред (все закрепленные всегда будут вверху, только потом будут идти не закрепленные, даже при поступлении в них новых сообщений)
pic
pic
Возможно, предвижу, кому-то удобнее прям внутри сообщений писать, тем самым создавая обсуждения. Потом мы добавим такое, но не в рамки обсуждения, а в рамках внутренних комментариев.
Рассылка кодов авторизации
Инфраструктура под это дело заложена в сервис авторизации. При регистрации или авторизации сервис будет отправлять на почту пользователя сообщение в формате HTML.
pic
Рендер письма который заложен в код процесса рассылки
Пока еще не заводили отдельную почту для нашего сервиса.
Рефакторинг back-end сервисов
  • Мы изменили логику проверки прав, она стала более замудренная, но в тоже время и более правильная.
pic
Теперь Gateway (основной сервис который либо пропускает запрос, либо нет) - подключен к redis pub/sub, инвалидирует версию прав и бит маску. Если не было события изменения - берет из мемори, если была - обновляет свой кеш. Кеш одного пользователя занимает 600.B. что не является слишком много. Для тяжелых прав на 2000 пользователей в одном сервер чате это около 4.5мб. В целом - пойдет.
Кеш хранимый в gateway:
{
'chat_id': str, # ObjectId, 24 символа
'version': int, # монотонный счётчик
'base': int, # ← OR всех ролей юзера, ОДНО число
'topics': {tid: int}, # только топики с overrides
'is_owner': bool, # админ или нет
}
  • Сервис топиков тоже потерпел изменения. теперь он не ходит в Permission сервис что бы уточнить права пользователя на просмотр, а Gateway: ProxyRouter приклеивает заголовки:
def build_perm_headers(self, snapshot: dict) -> dict:
return {
'X-Perm-Base': str(snapshot.get('base', 0)),
'X-Perm-Topics': json.dumps(snapshot.get('topics', {})),
'X-Is-Owner': '1' if snapshot.get('is_owner') else '0',
}
А сервис топиков их начинает парсить:
def parse_perm_headers(request) -> Optional[dict]:
base = int(request.headers.get('x-perm-base'))
topics = json.loads(request.headers.get('x-perm-topics', '{}'))
is_owner = request.headers.get('x-is-owner') == '1'
return {'base': base, 'topics': topics, 'is_owner': is_owner}
На выход топик сервис уже отдает отфильтрованные темы которые соответствуют правам пользователя внутри его роли:
{
"success": true,
"topics": [
{
"topic_id": "69ee3a46dab658745b095c27",
"chat_id": "69ee32fba41e74b3744d6502",
"name": "59545",
"created_by": "69edfdf3a6343fa670fe55db",
"topic_type": "thread",
"position": 0,
"created_at": "2026-04-26T16:16:06.983000",
"updated_at": "2026-04-28T07:09:30.006000",
"is_active": true,
"group_id": "69f05d25335af601c7accfe7",
"unread_count": 0
},
{
"topic_id": "69ee39a4dab658745b095c26",
"chat_id": "69ee32fba41e74b3744d6502",
"name": "55532",
"created_by": "69edfdf3a6343fa670fe55db",
"topic_type": "text",
"position": 1,
"created_at": "2026-04-26T16:13:24.480000",
"updated_at": "2026-04-28T07:09:30.001000",
"is_active": true,
"group_id": "69f05d25335af601c7accfe7",
"unread_count": 0
},
{
"topic_id": "69f05cf4335af601c7accfe6",
"chat_id": "69ee32fba41e74b3744d6502",
"name": "fgfhd",
"created_by": "69edfdf3a6343fa670fe55db",
"topic_type": "voice",
"position": 2,
"created_at": "2026-04-28T07:08:36.007000",
"updated_at": "2026-04-28T07:09:30.001000",
"is_active": true,
"unread_count": 0,
"group_id": null
}
],
"groups": [
{
"chat_id": "69ee32fba41e74b3744d6502",
"name": "tdrfhg",
"created_by": "69edfdf3a6343fa670fe55db",
"position": 0,
"created_at": "2026-04-28T07:09:25.028000",
"updated_at": "2026-04-28T07:09:25.028000",
"is_active": true,
"group_id": "69f05d25335af601c7accfe7"
}
]
}
  • Наконец то вынесли все коллекции сервис в отдельные базы. Раньше была одна общая БД, делалось для быстрого написания кода, но пораждало много зависимостей и хождений в чужие коллекции. Теперь все сервисы имеют свою БД со своими коллециями. Отдельный инстанс имеет только сервис сообщений.
  • 50% сервисов перевели в режим публикации событий. Т.е. раньше было много HTTP вызовов между ними, что пораждало лишние задержки в 1-4мс. Мелочь, но в рамках 1000 или 10000 пользователей это уже существенная нагрузка. Теперь HTTP вызовы служат только для получения информации от другого сервиса, все события которые раньше были переложены на Redis. Пример: Пользователь зашел в новый чат. Сервис чата сформирует событие что у него появился новый memories, сервис прав подхватит это событие и присвоит ему права и положит их в Redis, сервис Gateway увидит новые данные и заберет их к себе.
    • Мы работаем над переводом остальных сервисов, но не все так быстро))
Удаление легаси кода
При создании проекта, я вообще ничего не знал о методах разработки фронтенда. Сейчас когда нас несколько уже и мы отходим от AI разработки - начинаем сталкиваться с проблемами. Например тогдлы переключения - так как фронт писал ИИ, он в каждую форму где есть тогл - делал новый стилевый файл, и таких файлов скопилось 15+ ед. Мы перевели их на shared и пере-используем. И таких компонентов было очень много. В итоге нам получилось подчистить примерно 2к стилевых строк кода, сократить в JSX около 150 строк, потому что теперь это общие подключения.
Выделили отдельные сервисы кеша. Тонкие обёртки над универсальным key-value store для кэширования. Не хранят данные сами — только формирует ключи и инкапсулирует логику работы с одним конкретным namespace. Пример ниже:
export class TopicCache {
constructor(store) {
this.store = store;
this.KEY = 'topic_messages';
}
getMessages(chatId, topicId) {
return this.store.get(this.store.key(this.KEY, chatId, topicId));
}
setMessages(chatId, topicId, messages) {
this.store.set(this.store.key(this.KEY, chatId, topicId), messages);
}
invalidateMessages(chatId, topicId) {
if (topicId) {
this.store.invalidate(this.store.key(this.KEY, chatId, topicId));
} else {
this.store.invalidateByPrefix(this.store.key(this.KEY, chatId));
}
}
appendMessage(chatId, topicId, message) {
const cacheKey = this.store.key(this.KEY, chatId, topicId);
const cached = this.store.get(cacheKey);
if (cached) {
const exists = cached.some(m => m.message_id === message.message_id);
if (!exists) {
const updated = [...cached, message];
this.store.set(cacheKey, updated);
console.log(`[CACHE] Appended message to topic ${chatId}:${topicId}, total: ${updated.length}`);
}
}
}
}
Аналогично сделали и с WS сервисом, вынесли все WS действия в отдельные сервисы, для сообщений, топиков, прав, тредов и тп. Раньше это были одни монолитные файлы.
Большую часть действующих фронт сервисов перевели на isMobile проверку. Например анимации фона на мобилке - не нужны, мы их не грузим и в сборку они так же не попадут.
Изменения в UI
Часть изменений и доработок написал выше.
Изменение в тулбаре голосовой комнаты
pic
Кнопки анимированные, при наведении на ПК показывают анимацию
Добавили выбор устройства на ПК:
pic
Тоглам кнопкам добавили плавный переход, на фото это не будет видно.
Для мобильной версии сделали 2 вида тапа.
Короткий: Открывает меню взаимодействия с сообщениями
pic
Длинный: позволяет выбрать несколько сообщений сразу. Удалить, переслать или ответить.
pic
Итог
За 25 дней мы проделали огромную работу над проектом. Работали порой и до часу ночи. Все прошлые выходные провели с утра до ночи в нашем проекте.
Что сейчас:
Наши инфраструктурные сервисы готовы. Мы начинаем фазу тестирования. Активно ищем сервер в аренду с простой поддержкой и не очень дорогой. Хотели 20.04 уже запуститься, но не успели, мысли приходили на лету.-Источник
 
Loading...
Error