Главная/Кейсы/Macrulez App Platform
Vue 3Node.js · ExpressDockerWebSocketTraefikПет-проект

PaaS-платформа
для деплоя Vue
и Nuxt приложений

Один Dockerfile — нормально. Десять проектов с разными ветками, переменными окружения и версиями Node — уже не нормально. Автоматизировал полный цикл: клонирование → npm ci → сборка → Docker → Traefik. Без единой ручной команды.

0ручных команд при деплое
5шагов пайплайна: clone → install → build → image → swap
< 5 секswap контейнера без простоя
3 типаvue-static · nuxt-static · nuxt-ssr
Проблема

Один раз —
нормально.
Десять — нет.

Поставить новое приложение вручную выглядело так: написать Dockerfile, написать конфиг Traefik, положить в нужную папку, запустить docker compose up. Один проект — это просто. Когда их становится несколько, и у каждого своя ветка, своя версия Node и свои переменные окружения — управлять этим руками начинает раздражать.

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

Я сделал механизм хостинга частью платформы, а управление им — модулем «Приложения» в admin-панели.

Что построено

От списка
до настроек —
всё в одном месте.

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

01

Список приложений

Сетка карточек: аватарка с детерминированным цветом (одинаковым при любых перезагрузках), имя, тип (vue-static / nuxt-ssr), домен. Цветная точка статуса — живая, обновляется через WebSocket без перезагрузки. Открыл пересборку, вернулся на список — точка уже моргает синим. Новое приложение в другом браузере — карточка появляется автоматически.

WebSocketreal-time статусdeterministic color
02

Git-доступы

Хранилище учётных данных для приватных репозиториев. Два способа: PAT-токен (HTTPS) и SSH deploy key. Секрет сохраняется в базе и никогда не возвращается клиенту после сохранения. Один токен — для нескольких репозиториев организации: выбрал из выпадающего списка при создании приложения и всё.

PATSSH deploy keyсекрет только на запись
03

Панель и сборка

Шапка с аватаркой, 8 ячеек сводки (домен, тип, ветка, статус, автодеплой, дата сборки, размер образа, uptime). Кнопки «Пересобрать» и «Остановить». Полный лог сборки в реальном времени прямо в браузере через WebSocket: ANSI-escape обрабатывается через ansi_up — цветные строки Vite, прогресс-бары Nuxt, зелёные ✓ npm — всё как в терминале.

WebSocket build logansi_upsmart autoscroll
04

Статистика

CPU, RAM и сетевой трафик — данные из Docker Stats API раз в 30 секунд. Четыре периода: 1 ч / 6 ч / 24 ч / 7 дней. Графики ECharts с заливкой. Входящий и исходящий трафик — два ряда на одном чарте.

Docker StatsEChartsCPU · RAM · network
05

Трафик

HTTP-метрики из Prometheus-экспортёра Traefik — на уровне прокси, не контейнера. RPS-линия, стекованные бары кодов ответа (2xx зелёный / 3xx синий / 4xx оранжевый / 5xx красный), средняя задержка. Сразу видно, если начали расти 4xx — без поднятия логов.

TraefikPrometheusstacked barslatency
06

Runtime-логи

Эквивалент docker logs --follow — прямо в браузере. Переключил вкладку — стриминг включился, ушёл с вкладки — выключился. Особенно удобно для Nuxt SSR: серверные ошибки, предупреждения гидратации — всё в реальном времени без терминала.

docker logs streamWebSocketon-demand
07

Настройки

Форма с двумя слоями env-переменных: Build env запекается в бандл на этапе сборки (VITE_* / NUXT_PUBLIC_*), Runtime env передаётся контейнеру при запуске. Загрузка реальных веток через git ls-remote по кнопке. Автопресеты buildCmd / outDir при смене типа — но только если поле не редактировалось вручную. Автодеплой с настраиваемым интервалом из API.

Build / Runtime envgit ls-remoteавтодеплой
Под капотом

Нетривиальные
решения.

Детали, которые делают разницу между «работает» и «работает правильно».

  1. 01
    WebSocket: синхронизация состояния между вкладками

    Статус-точки обновляются в реальном времени через один WebSocket-канал. Нажал «Пересобрать» в одной вкладке — точка моргнула синей во всех других. Добавил приложение — новая карточка появилась у всех подключённых клиентов без перезагрузки. Сервер бродкастит изменения всем соединениям; клиент реагирует патчем состояния по ID приложения.

    broadcastpatch by IDno reload
  2. 02
    Умный автоскролл в логе

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

    scroll thresholdUX для логовansi_up
  3. 03
    Флаг queued: кнопки не мигают между 202 и первым WS-событием

    Между нажатием «Пересобрать» и первым статусным событием по WebSocket есть зазор: ответ API пришёл 202, но сборка ещё не началась. Без защиты кнопки разблокируются на долю секунды, потом снова блокируются — заметный мигательный артефакт. Ставится внутренний флаг queued, который держит кнопки заблокированными ровно до прихода первого обновления статуса через WebSocket.

    race conditionoptimistic UIqueued flag
  4. 04
    Дельта сетевого трафика: обработка сброса при рестарте

    Docker считает трафик кумулятивными счётчиками от момента запуска контейнера. На клиенте берётся дельта между соседними точками и делится на интервал — получаем байты в секунду. При рестарте контейнера счётчик обнуляется, дельта уходит в отрицательную зону. Такие выбросы молча пропускаются — чтобы не рисовать ложные отрицательные пики сразу после деплоя.

    delta calculationcounter resetdefensive filtering
  5. 05
    Маскировка чувствительных переменных окружения

    Если имя переменной содержит одно из слов TOKEN / SECRET / PASSWORD / KEY / PRIVATE / AUTH / CERT / SALT — она автоматически помечается как чувствительная. В таблице показываются первые 4 символа и «…». Кнопка-замок: нажал — раскрылось для текущей сессии, нажал снова — скрылось. Это не шифрование на сервере, а защита от случайного взгляда на экран.

    keyword detectionpartial revealenv security UX
Полный цикл

Нажал
«Задеплоить».
Готово.

Платформа выполняет пять шагов без участия пользователя. Клонирование — свежая копия указанной ветки с авторизацией через сохранённый PAT или SSH-ключ. Установка зависимостейnpm ci или кастомная команда. Сборкаnpm run build с переданными Build env-переменными. Docker build — образ пакуется и тегируется. Swap — старый контейнер останавливается, новый запускается.

Traefik автоматически получает новый маршрут и начинает отдавать трафик на нужный домен. Весь процесс занимает от 30 секунд до нескольких минут в зависимости от проекта, и всё это время лог летит прямо в браузер через WebSocket.

Автодеплой — опция. Платформа сама отслеживает HEAD ветки через git ls-remote и запускает пересборку при появлении нового коммита. Интервал проверки настраивается через переменную окружения оркестратора, реальное значение подтягивается в форму через API — никакого хардкода.

До платформы каждый новый проект — это новый Dockerfile, новый конфиг Traefik и ручной docker compose up. Теперь то же самое через форму в браузере.

Скриншоты

Как это
выглядит.

«Хороший инструмент — тот, который делает следующую задачу быстрее, а не просто автоматизирует текущую.»

— из опыта разработки Macrulez App Platform
Следующий шаг

Нужна DevOps автоматизация или админ инструменты?

Спроектирую и реализую: от CI/CD-пайплайна до full-featured admin-панели. Работаю с Vue 3, Node.js и Docker.