Маркетинг

Стартап в Соло. Часть 4: техническая реализация

В этой статье хочу рассказать, как технически устроен мой Telegram чат для сайта, из каких компонентов состоит и с какими подводными камнями я сталкивался. Статья вышла очень технической, но, думаю, будет интересна для расширения кругозора о развитии проектов.
Стартап в Соло. Часть 4: техническая реализация

Содержание

  • О чем пойдет речь?

  • Архитектура

  • Зачем оптимизировать виджет?

  • Как я оптимизировал виджет

  • С какими атаками и проблемами сталкивался проект

  • Про ограничения Telegram

  • Послесловие

О чем пойдет речь

В этой статье хочу рассказать, как технически устроен мой Telegram чат для сайта, из каких компонентов состоит и с какими подводными камнями я сталкивался.

Может сложиться впечатление, что чат для сайта - это простое приложение. На самом деле, зависит от стадии развития проекта. Когда пет-проект превращается именно в IT продукт с платящими пользователями, резко возрастают требования к качеству.

Даже при 100 активных виджетах появляются нетривиальные задачи: и контроль нагрузки, и оптимизация отправки сообщений в Telegram (с его ограничениями), и администрирование пользователей, и контроль оплат. Дополнительно появляются проблемы в виде всяких говнюков DDOS атак, XSS и разных попыток поломать приложение.

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

Архитектура

Концептуально архитектура чата выглядит как на диаграмме ниже.

Уточнения:

  • Указаны только основные библиотеки и фреймворки, маленькие не указаны.

  • И клиентская часть (SSR), и серверная (API) работает в многопроцесcном режиме.

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

Архитектура проекта

Архитектура проекта

Для сравнения, архитектура MVP:

Архитектура MVP

Архитектура MVP

В первой статье я говорил кратко про все компоненты выше, но расскажу еще раз (слева направо):

DDOS-защита и CDN

Использую DDOS-Guard (насколько я знаю, они самые крупные в СНГ после Cloudflare). Недорого, удобно. В дополнение на уровне Nginx и на уровне API есть “локальная” защита. Срабатывает чуть быстрее в некритичных случаях.

Down detector

Самодельная программа, которая находится на другом сервере. Проверяет, доступен ли сайт, виджет и API. В случае, если что-то не так, мне в Telegram прилетает уведомление.

Вот так выглядит чат с оповещениями:

Сюда приходят оповещения о поломках

Сюда приходят оповещения о поломках

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

Контейнеры и сборка

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

Reverse proxy и SSL

Nginx. Лично для меня самое удобное и производительное. Легко настраивается для сокетов, легко настраивается под провайдера DDOS защиты, быстро раздает статику.

Сайт и личный кабинет пользователя

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

Для защиты от ботов при регистрации используется каптча от Яндекса. Бесплатно, интеграция стандартная за 30 минут.

Технологии: TypeScript, NextJS (серверный рендеринг) + React, cluster mode для NextJS с одни процессом на одно ядро. CSS библиотеки не используются, чтобы быстрее грузился сайт, ограничиваюсь React CSS Module.

Админка с аналитикой и менеджментом пользователей

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

Технологии: TypeScript, React, Bootstrap 4.

Выглядит примерно так:

Просмотр пользователей

Просмотр пользователей

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

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

Отдельно уточню про логи и ошибки:

  • Логи для важных функций пишутся прямо в БД с ограниченным временем и объемом хранения.

  • Критичные ошибки (с платежной системой, с API Telegram’a) прилетают мне сразу в Telegram.

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

Виджет для сайта

Собственно то, что и является конечным продуктом для клиента. Весь код компилируется в один .js файл, который клиенты вставляют на свой сайт.

Технологии: TypeScript, PreactJS, SocketIO. Особо добавить нечего.

Серверная часть с API

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

Тестами покрыто ~5%-10% серверной части. Пет-проект все-таки 🙂. Покрыл только ключевые компоненты: взаимодействие с Telegram, взаимодействие с платежными системами и стресс-тест.

Технологии: TypeScript, NodeJS + NestJS, SocketIO, PM2 (менеджмент процессов), Jest (unit тесты) + Supertest (E2E тесты).

База данных

PostgreSQL 14. Не MySQL, потому что нынче проблемы с Oracle, мне более спокойно со 100% open source’ом. Не MariaDB, потому что PostgreSQL популярнее, в среднем быстрее, да и никогда не работал с ней.

Откровенно говоря, я еще не сталкивался с такими нагрузками на реляционную БД, где действительно был бы критичным выбор между MySQL или PostgreSQL, и при этом не нужно было смотреть в сторону альтернативных NoSQL баз.

Пересказать теоретические статьи на тему "PostgreSQL MySQL performance comparison" я могу, но вот в жизни еще не сталкивался с такими вопросами, где это имело бы значение. Поэтому выбор был по принципу "что популярнее, удобнее и с чем давно работаю".

Резервные копии

Делаются несколько раз в день стандартными средствами от FirstVDS + стандартным pg_dump + zip. Проверку восстановления из бекапов провожу примерно раз в месяц.

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

Брокер сообщений и in-memory key-value хранилище

Redis. Не RabbitMQ для сообщений, потому что конкретно под этот проект базового механизма pub/sub в Redis хватает с головой. Учитывая, что и key-value, и брокинг в одном месте - выбрал Redis.

Тут нужно уточнение про виджет и Redis. Они взаимосвязаны.

Виджет подключается к серверу по сокетам. Так сообщения из Telegram отображаются быстрее, сервер не грузится лишний раз из-за long pooling’a.

Так как API запущено в несколько процессов (с разными зонами памяти), сокеты имеют свойство подключаться к разным процессам. Telegram доставляет данные так же в разные процессы. В результате процесс сокета и процесс с ответом от Telegram’a имеют свойство не сходится.

Чтобы их сопоставить, используется механизм publisher и subscriber в Redis. И сокет, и слушатель Telegram ответов подписываются на прослушивание ответов по своим ID. Как только приходит ответ от Telegram, Redis прокидывает сообщение в нужный сокет.

Дополнительно Redis используется для debounc’a, приоритизирования сообщений в Telegram и немного помогает с превентивной DDOS защитой.

Глобально с «концептуальной стороны» архитектура выглядит так. Далее расскажу про детали специфические для этого проекта.

Зачем оптимизировать виджет?

В мире веба существуют показатели Web Vitals. Они измеряются с помощью Google Page Speed Insights (на самом деле не совсем так, но неважно). Пример как раз моего сайта:

Показатели Google Page Speed для Telegram Feedback

Показатели Google Page Speed для Telegram Feedback

Любой скрипт, компонент, лишняя картинка или виджет тормозят сайт. Показатели падают, пробиться в SEO выдачу становится сложнее. А это одна из основных задач всех сайтов. При том, что практически каждый сайт обвешан Google Analytics, Яндекс Метрикой, медленными скрипами и разными CSS библиотеками.

У меня стояла задача сделать виджет насколько быстрым и маленьким (в плане скорости загрузки на сайт), чтобы он не влиял или почти не влиял на скорость. Если на этапе MVP виджет был более 500кб, включал несколько жирных библиотек и тонну CSS’a - после MVP это стало недопустимо.

Как я оптимизировал виджет

Делал я это в 4 шага.

(1) Вставляем SVG иконки в bundle.

Если раньше иконка грузилась с помощью тега:

https://telegram-feedback.com/images/arrow.svg” />

Теперь все иконки в виджете имеют формат:

Использование изображений в виджете

Использование изображений в виджете

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

(2) Заменяем ReactJS на PreactJS

React в сборке весит ~40 Кб. Меняем его на похожую (а во многих случаях аналогичную библиотеку) PreactJS, которая весит 3 Кб. Функционал остается, вес меньше.

Уточнение: в теории, можно написать виджет и на чистом JS. Но это будет намного более дорого с точки зрения времени (напомню, для меня это все-таки пет-проект в свободное время, которого мало) и, скорее всего, я напишу больше кода для выборки элементов, чем 3 Кб.

(3) Удаляем лишние библиотеки

Точно не вспомню, какие именно библиотеки я использовал, но точно помню, что удобно было использовать styled-components для динамического CSS’a. Пришлось удалять вообще все библиотеки, кроме одной - SocketIO.

Вообще, сокеты есть в стандартном API браузера, но SocketIO слишком удобно использовать и эта библиотека сильно экономит время. Единственное, что мне было слишком больно удалять.

(4) Удаляем лишний CSS и JS

~70% кода виджета - это мой CSS и JS. В самом начале я сделал предустановленные цветовые темы и на каждую тему был свой CSS файл по ~10 Кб. К тому же, был генератор фильтров для SVG иконок (чтобы закрасить иконку, нужно применить к ней фильтр, а до этого его сгенерировать).

CSS удалил, фильтры теперь генерируются на сервере при создании или сохранении виджета.

Вот так настраивается цвет виджета со стороны пользователя:

Ручная настройка дизайна виджета

Ручная настройка дизайна виджета

Вот так генерируются CSS фильтры для заданных HEX цветов (раньше это было в виджете):

Генерация цветового фильтра для SVG изображения

Генерация цветового фильтра для SVG изображения

Класс, отвечающий за генерацию фильтра

Класс, отвечающий за генерацию фильтра

Уточню, код скопипастил со StackOverflow и не до конца понимаю, как эти цвета генерируются с точки зрения пересечения цветов. Зато честно и работает.

Теперь виджет весит ~150Кб и это самый маленький результат среди всех виджетов, которые я встречал (а я искал). В мобильной версии скорость сайта падает от 1% до 3%. Это в несколько раз лучше, чем скрипт Яндекс Метрик (скрипт у них меньше весит, что круто, но имеет намного больше бизнес-логики).

В перспективе уменьшу размер виджета еще сильнее и за рамки 1% потери скорости загрузки страницы выходить не буду.

Когда-то.

С какими атаками и проблемами сталкивался проект

Перечислю, как пытались ломать сайт и виджет.

  • XSS атаки, вставки гадости в формы

    Результат: получилось через сообщения пробросить скрипт в панель администратора (причем это был пользователь с Хабра).

    Однако сейчас абсолютно весь текст обязательно экранируется, все запросы экранируются, никаких запросов в БД с необработанным вводом от пользователя (хотя их и не было).

  • DOS с одного компьютера

    Результат: не получилось. Изначально была DDOS защита на уровне API, потом появилась на уровне Nginx’a, а потом подключил DDOS Guard.

  • Слабый DDOS

    Результат: не получилось. К тому моменту всё работало в кластерном режиме (и фронт, и сервер), были ограничения по скорости запросов от Nginx и со стороны API.

  • Загрузить память сервера картинками

    Результат: почти получилось, случайно заметил. Какой-то говнюк нехороший человек начал грузить на сервер картинки весом 50 Мб с медленной периодичностью. Возможно даже вручную. Я заметил, что за пару часов память выросла на ~10Гб. Начал разбираться, нашел проблему.

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

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

    На самом деле, память так конечно фиг забьешь. И диск большой, и защит понаставил. Да и в планах перейти на CDN от Selectel’a. Но всё равно лично мне функция с картинками не нравится 🙂.

  • Попытки регистрироваться много раз

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

  • Подобрать пароль к PostgreSQL

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

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

    Спустя день я увидел, что загрузка сервера всё время 100% из-за процесса pg. Закрыл порт - не помогло. Перезагрузка тоже. Начал копать дальше и оказалось, что я оставил стандартный пароль на базе 🤦‍♂. Естественно, бот нашел базу, пароль подошел и у него вышло разгруляться. На тот момент база была не в Docker’e, кстати.

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

    Вроде ничего страшного не случилось, но ЧСВ понизилось. Мораль: серьезные пароли, никаких портов во внешний мир (даже если так удобнее), аккуратно с настройками. Ну а для удобства поставил себе графическую оболочку XFCE (всегда ее любил) и PgAdmin, ковыряюсь в базе только по VNC.

  • Боты с подбором форм

    Ну это классика с которой сталкиваются все сайты поголовно. Боты бегают по интернету и пытаются подобрать пароли к WordPress’ам, CMS’кам и т.д. У меня API свое, такое не угрожает.

  • Школьники, тролли и арабы

    Не техническая проблема, но раздражает.

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

Про ограничения Telegram

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

Тут скажу две вещи:

  1. Никак. Я с ними сосуществую и играю по правилам, а не обхожу.

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

Как это работает:

Всем сообщениям и действиям проставляется приоритет. Например, сообщение от пользователя имеет максимальный приоритет и будет доставлено в чат в первую очередь. Показ разных действий (например, кнопка "показать, что отвечаю) имеют средний приоритет.

Если в очереди (в рамках 5 секунд, например) стоит 3 сообщения пользователя и действие для кнопки “показать, что я отвечаю”:

Приоритет действий

Приоритет действий

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

Уточню: такие ситуации случаются крайне-крайне редко (судя по логам). Бывают сайты, у которых проходимость 100 000 посетителей в день и им часто пишет параллельно 5-10 пользователей.

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

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

На этот случай у меня уже готов код, чтобы менять ботов. Если сейчас в системе есть один бот и пользователю нужно писать или добавлять его, в будущем этих ботов будет 2-5-10 или сколько более. Просто система будет выбирать наименее загруженного бота и подключать его к новым сайтам.

Послесловие

За почти год проект разросся. Даже несмотря на то, что у меня редко выходило выделять на него больше 10-20 часов в неделю. А частенько и не выходило 🙂.

У любого IT-проекта, которым действительно пользуются пользователи, всегда много нюансов. Приходится всё систематизировать, защищать, оптимизировать и делать надежным. Вот и у меня так вышло.

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

Вот.

Начать дискуссию

Как учитывать расходы на обучение сотрудников для налога на прибыль

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

Как учитывать расходы на обучение сотрудников для налога на прибыль

ЕФС-1 с Разделом 2 за первое полугодие 2024: как сдать отчет

Отчетность по «травматизму» подается в составе единой формы ЕФС-1. Рассказываем, как сдать отчет за полугодие 2024 года.

ЕФС-1 с Разделом 2 за первое полугодие 2024: как сдать отчет

Маршрутизация грузоперевозок программным роботом RPA

Мы разработали программного робота RPA, и… робот превзошел все ожидания нашего заказчика (одна из крупнейших транспортных компаний страны).

Маршрутизация грузоперевозок программным роботом RPA

Курсы повышения
квалификации

18
Официальное удостоверение с занесением в госреестр Рособрнадзора

Пользователи не могут войти в приложение «Т-Инвестиции»

Клиенты брокера пожаловались на сбой в работе мобильного приложения «Т-Инвестиции».

НДФЛ

Командированные за границу для строительства атомных станций априори будут налоговыми резидентами РФ

Физлица, которые в течение года были в России менее 183 дней, являются нерезидентами РФ. Но есть исключения для ряда случаев. Таких исключений станет больше.

Как без проблем с налоговой работать с самозанятыми

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

Опытом делятся эксперты-практики, без воды

Более 79 млн проводок, 12 млн казначейских платежей и 2 млн мероприятий по кадровому администрированию и расчету зарплаты: «Северсталь – ЦЕС» исполнилось 15 лет

В июле свое 15-летие празднует «Северсталь – ЦЕС» – профессиональная сервисная компания, специализирующаяся на предоставлении услуг консалтинга, аутсорсинга и автоматизации сквозных бизнес-процессов, построении и управлении общими центрами обслуживания, разработке и внедрении цифровых продуктов и ИТ-сервисов, включая решения на базе SAP и 1С.

Шохин: государство не должно контролировать соблюдение соглашений и коллдоговоров

Российский союз промышленников и предпринимателей (РСПП) принял участие в совещании по вопросам совершенствования законодательства о занятости и изменений в ТК.

❗️ 15 июля стартовало обучение в летней школе бухгалтера. Скорее присоединяйтесь!

Вчера, 15 июля, в летней школе бухгалтера прошли первые занятия. Сегодня последний день, когда вы можете приобрести любой курс повышения квалификации, а также курсы профпереподготовки для бухгалтеров на УСН и финансовому менеджменту со скидками до -78%. Выбирайте курс и приступайте к обучению!

КоАП РФ

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

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

МИР

Карты «Мир» начали принимать в Мьянме

Пока что платежная система «Мир» в Мьянме работает только в некоторых ресторанах и торговых центрах.

Бухгалтерский учет

Бухучет для начинающих: рассказываем о дебетах, кредитах и первичке простым языком

Как устроена профессия бухгалтера, с какими задачами он работает и что означают все эти странные сокращения (НДС, ПСН, ЕСН и др.) — разбираемся в основах бухгалтерии.

Иллюстрация: Вера Ревина /Клерк.ру

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

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

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

Роструд: из основных работников в совместители можно перейти только через увольнение

Устроиться на работу по совместительству, не имея основной, нельзя. Даже если это временная работа на 0,5 ставки и позиционируется как подработка.

Ипотека

Ипотека на индивидуальное жилищное строительство достигла рекордных 385 млрд рублей

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

Ведение бизнеса

Как продавать товары за границу из России: интернет-магазин или маркетплейс

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

Как продавать товары за границу из России: интернет-магазин или маркетплейс

Налоговики активно ищут нарушителей среди продавцов маркетплейсов

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

Чек-лист налоговых рисков 2024 и как их минимизировать

По данным ФНС число проверок бизнеса снижается, но при этом растет их эффективность. На выездную проверку одной организации в 2023 году в целом по России налоговые доначисления составили 63 млн руб. (в 2022 году — 49 млн руб.). Если проверка началась, то, скорее всего, она окончится добровольной доплатой или доначислениями. Но лучше повода для нее не давать.

Чек-лист налоговых рисков 2024 и как их минимизировать

Для расчета налога на имущество учитывают обесценение и изменения справедливой стоимости ОС

Учет основных средств в бухучет идет по ФСБУ 6/2020, МСФО 36 «Обесценение активов» и МСФО 13 «Оценка справедливой стоимости».

Интересные материалы

👎 Освобожденных от НДС упрощенцев не освободили от счетов-фактур

Если доход за предыдущий год не превышает 60 млн рублей, в текущем году при УСН будет освобождение от НДС по статье 145 НК.

9