Перейти к содержимому
Эльдар Шахвалиев
Обсудить проект
← Все проекты
202604.2026 — presentВ продеDevOps

Трекер задач на своём сервере для веб-студии: запуск, перенос истории, русификация

веб-студия (small team, 5 человек) · Веб-разработка / внутренний инструмент проектного менеджмента

DevOps / инженер-интегратор (деплой, миграция данных, локализация)

Веб-студия из пяти человек вела проекты в чатах: история терялась, единого списка задач не было. Поднял свой трекер и перенёс в него накопленное.

  • Docker
  • docker-compose
  • Caddy
  • VPS
  • Ubuntu 24.04
  • PostgreSQL
  • Redis (Valkey)
  • RabbitMQ
  • MinIO/S3
  • Python
  • REST API
  • Telegram API
  • Next.js
  • TypeScript
  • SQL
  • Bash
  • Let's Encrypt/TLS
  • UFW/fail2ban

Ссылки и доступ — по запросу.

4 проекта / 198 задач / 357 комментариев / 9 страниц / 12 участников
импортировано из Telegram в Plane (всего 588 сущностей в state-журнале)
v1.3.0 (commit cf696d200)
зафиксированная версия Plane CE
Up 6 days (healthy)
12 контейнеров стека работали стабильно на момент снимка 2026-05-04
223 файла / 234 EN-фразы
пропатчено в apps/web и авто-переведено в рамках доработки RU-локализации
ru/translations.ts 95KB→143KB
рост объёма русского словаря после доработки
retention 7d / 4w / 3m
политика ротации ежедневных бэкапов (Postgres + MinIO + конфиги)
  • Перенёс из переписки 588 записей: 4 проекта, 198 задач, 357 комментариев, 9 страниц и 12 участников — с сохранением того, кто кому отвечал.
  • Конвейер переноса можно запускать повторно: при сбое на середине он продолжает с места остановки, а не создаёт дубли.
  • Оценки трудозатрат требовали строгой приватности — каждый видит только свои, директор все. Бесплатная версия трекера так не умеет, решил обходным путём.
  • Довёл русский перевод интерфейса: свёл глоссарий, сверился с российскими аналогами, перевёл 234 фразы, остававшиеся английскими.
  • Настроил ежедневные резервные копии с проверкой целостности и инструкцией восстановления: хранятся неделю, месяц и квартал.

Задача

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

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

Что сделано

  • Развернул трекер на арендованном сервере с нуля и закрыл сервер от типовых атак: доступ по ключу, автоматические обновления безопасности, настройки базы подобраны под объём памяти машины.
  • Настроил защищённое соединение поэтапно: пока адрес сайта ещё расходился по интернету, работал временный сертификат, после — постоянный. Иначе первые дни трекер был бы недоступен.
  • Перенёс историю из переписки в трекер. Программа разбирает выгрузки чатов, раскладывает сообщения по двенадцати категориям и превращает ответные цепочки в комментарии к задачам. Конвейер можно запускать повторно: при сбое на середине он продолжает с места остановки, а не создаёт дубли.
  • Решил задачу приватности оценок. Бесплатная версия трекера не умеет закрывать отдельные задачи, поэтому проекты с оценками сделаны полностью закрытыми, а правило изоляции финансовых данных работает в обе стороны: они не попадают в публичные проекты, а из закрытых не утекают.
  • Настроил ежедневные резервные копии базы, файлов и настроек — с проверкой целостности, ротацией и написанной инструкцией восстановления. Копия, которую не пробовали восстановить, копией не является.
  • Довёл русский перевод интерфейса: свёл глоссарий терминов, сверился с российскими аналогами и перевёл фразы, которые штатный перевод оставлял английскими.

Итог

Было

  • Проекты и задания — в разрозненных чатах, история терялась в прокрутке
  • Оценки трудозатрат — в личной переписке с директором, без разграничения доступа
  • Единого списка задач не существует

Стало

  • Свой трекер на своём сервере: защищённое соединение, ежедневные копии, инструкция восстановления
  • История переписки перенесена в проекты, задачи и комментарии
  • Оценки изолированы: их видит только тот, кто их дал, и директор

Студия работает в своём трекере на своём сервере, без зависимости от облачных сервисов и их доступности. Из переписки перенесено 588 записей: 4 проекта, 198 задач, 357 комментариев, 9 страниц и 12 участников — с сохранением того, кто кому отвечал.

Оценки трудозатрат видит только тот, кто их дал, и директор. Русский интерфейс доведён до состояния, когда команде не нужно догадываться: переведены 234 фразы, остававшиеся английскими, и правки затронули 223 файла интерфейса.

Технические детали

Решение и подход

Инфраструктура. Стек Plane запускается официальным релизным docker-compose (APP_RELEASE=v1.3.0, не stable) со встроенными Postgres 15.7, Valkey (Redis), RabbitMQ и MinIO. Поверх — собственный docker-compose.override.yml с healthcheck'ами, security_opt: no-new-privileges и тюнингом Postgres под 4 GB (max_connections=50, shared_buffers=512MB и др.); вариант с mem_limit/cpus сохранён отдельным .disabled-файлом для включения одной командой. Внешний Caddy v2 в отдельном compose-проекте проксирует на внутренний plane-proxy через external docker network. Поскольку домен был зарегистрирован за сутки и A-запись ещё не пропагирована, TLS поднимался поэтапно: старт на tls internal (self-signed) → после пропагации DNS переключение на Let's Encrypt без даунтайма (caddy reload).

Миграция. Дизайн целевой структуры зафиксирован в mapping-design.md до импорта. Сообщения Telegram классифицируются LLM по 12 категориям (requirements / task_actionable / status_update / estimate_request / noise и т.д.); связные ТЗ агрегируются в issue «Brief/ТЗ», reply-цепочки превращаются в дерево комментариев, ссылки на Figma/Drive — в issue links. Состояние пишется в import-state.jsonl (по строке на сущность с plane_id и привязкой к tg_message_id), что делает повторный --apply идемпотентным и даёт rollback. Ключевое нетривиальное решение — приватность оценок: в Plane CE нет per-issue ACL, поэтому оценки выносятся в отдельные private-проекты (Estimates — <Name>, members = [сотрудник, директор]); видимость network=0 выставляется прямым UPDATE в Postgres через SSH→docker exec psql (whitelisted SQL), а «зеркальное правило» вырезает любые суммы/часы из публичных проектов.

Локализация. Разобрана архитектура i18n Plane (кастомный mobx-store + intl-messageformat, локали как TS-модули export default {.} as const, EN-core.ts как fallback). На основе coverage-аудита и бенчмарка российских аналогов составлен глоссарий (Work item → «Задача», Intake → «Заявки», Due date → «Срок выполнения» и т.д.), затем — правки ru/*.ts, патчи hardcoded-строк в apps/web/, передача ru-локали в date-fns и авто-патчер EN→RU-ключей. Так как переводы «запекаются» в Next.js-билд, подготовлен runbook сборки кастомного frontend-образа локально (на VDS сборка падала бы по OOM) и переноса через docker save | ssh | docker load.

Стек и обоснование

  • Plane CE v1.3.0 — open-source, AGPL, self-hosted: независимость от облачных PM-сервисов и полный контроль данных команды.
  • Docker / docker-compose — официальный способ поставки Plane; воспроизводимость через override-файлы.
  • Caddy v2 — автоматический ACME/Let's Encrypt, HTTP/3, лаконичный Caddyfile; вынесен наружу, чтобы не трогать внутренний proxy Plane.
  • PostgreSQL / Valkey / RabbitMQ / MinIO — встроенные зависимости Plane; Postgres дополнительно затюнен под 4 GB и используется напрямую (psql) для операции, недоступной через API.
  • Python + REST API + Telegram API — пайплайн миграции; tenacity для backoff на 429/5xx, idempotent state в JSONL.
  • LLM-API (единый доступ к нескольким моделям) — компонент пайплайна миграции для классификации сообщений; кэширование удешевляет проход по 500+ сообщениям, смена модели — через env.
  • Next.js / TypeScript — слой локализации Plane; правки требуют пересборки frontend-образа.
  • UFW / fail2ban / unattended-upgrades / swap — базовая защита и устойчивость одиночного VDS.

Инженерные вызовы

  • DNS не пропагирован на старте (домен в зоне ~24 ч) — Let's Encrypt HTTP-01 сразу не прошёл бы; решение — двухфазный TLS (self-signed → LE) с переключением одним reload.
  • Жёсткие 4 GB RAM — тюнинг Postgres, лимиты как «потолок», сборка frontend только локально (на VDS — OOM), перенос образа через docker save/load.
  • Отсутствие per-issue ACL в Plane CE — приватность финансовых оценок реализована через private-проекты + прямой UPDATE projects SET network=0 в Postgres (whitelisted SQL по SSH), плюс автоматическая вырезка сумм из публичных проектов.
  • Идемпотентность и откат миграции — JSONL state-machine с привязкой каждой созданной сущности к исходному Telegram-сообщению; повторный прогон пропускает уже залитое.
  • Шумные неструктурированные данные Telegram — LLM-классификация по 12 категориям с отбраковкой noise (стикеры, «ок», «+») и ручной доревизией спорных кейсов.
  • Безопасность доступа — план изменения sshd_config с обязательной параллельной safety-сессией и rescue-консолью провайдера на случай локаута.
  • деплой
  • devops
  • миграция
  • локализация (i18n)
  • бэкап/восстановление
  • hardening
  • поддержка