Перейти к содержанию

ADR-011: DOSEmax — две PWA (cloud + local) как entry-point'ы

Дата: 2026-05-22 Статус: Accepted (планируется к реализации) Связано: ADR-010


Контекст

В проекте уже есть две независимые веб-страницы:

  1. max-webapp/ (cloud) на https://awdc.nikolaev.world/ — Mini App для парка, команды через MAX-бота. Доступен из любой точки с интернетом.
  2. web-ui/ (local) внутри прошивки контроллера, отдаётся через ESP32 на http://awdc.local/. Полный интерфейс одной мойки. Доступен только из зоны Wi-Fi контроллера.

Обсуждался вопрос: имеет ли смысл сделать обе страницы устанавливаемыми на home screen как PWA (Progressive Web App), чтобы у оператора появилась иконка в Android-лаунчере и при тапе открывалось без браузерной обвязки.

Решение

Делаем PWA-обёртку для обеих страниц с общим названием «DOSEmax». Добавляем manifest.json, иконки 192/512 PNG (рендерим из существующего web-ui/public/logo.png — drop-логотип), <link rel="manifest"> в index.html обоих проектов.

Service worker — отложен, только manifest сейчас. Добавим позже, если появятся реальные сценарии «оператор открыл когда сеть отвалилась».

iOS — не поддерживаем. В парке iOS-операторов нет; apple-touch-icon и Safari meta-теги не делаем.

Прошивка меняется: в web-ui/ добавляются manifest.json + два PNG, gzipped, через тот же embed-pipeline (ожидаемый прирост Flash: ~10-30 КБ). max-webapp/ — чистая статика, прошивку не трогает.

Что это даёт

  • Оператор устанавливает «приложение» одним тапом («Добавить на главный экран» в Chrome).
  • Запускается fullscreen без адресной строки, иконка DOSEmax в переключателе приложений.
  • Cloud-PWA: парк виден везде, где есть интернет — без установки MAX-клиента.
  • Local-PWA: один контроллер виден из домашнего/офисного Wi-Fi — без MAX и без интернета вообще.

Что это НЕ даёт (важно — частая иллюзия)

  • Cloud-PWA не даёт независимости от MAX как транспорта. HTTP-запросы по-прежнему идут к platform-api.max.ru через /max-proxy/. Если MAX упадёт — cloud-PWA не работает. Mini App-канал и cloud-PWA-канал имеют одинаковый single point of failure.
  • Local-PWA — не замена cloud-канала. Она работает только когда телефон в Wi-Fi конкретного контроллера. Удалённый мониторинг — только через MAX.
  • Резервный канал на случай падения MAX не появляется. Для этого нужно принципиально другое решение (свой backend-брокер, туннель, LoRa) — отвергнуто отдельно как нерентабельное.
  • Полноценное offline-приложение не делаем сейчас. Service worker отложен; manifest даёт только иконку и fullscreen.

Альтернативы и почему они отвергнуты

Альтернатива Что даёт Почему отвергли
Только Mini App в MAX минимум кода оператор обязан ставить MAX
Capacitor (нативная обёртка Android) то же + mDNS / push +неделя работы, +RuStore модерация, +отдельный CI
Полностью native Kotlin app максимум платформенных фич +месяцы, +отдельная команда
Свой backend-брокер вместо MAX независимость от MAX API хранение токенов на сервере (security regression)
Cloudflare Tunnel с контроллера прямой доступ зависимость от CF, TLS-туннель на ESP32 = боль

PWA выбран потому что это минимальный шаг (~hours work, no firmware change), который даёт измеримую UX-выгоду (иконка-приложение) и не закрывает дверь для других вариантов в будущем.

Архитектура (без изменений)

Cloud PWA (max-webapp)

DOSEmax (home screen) → fetch /max-proxy/messages → nginx proxy_pass
       ▲                                                ▼
       │                                       platform-api.max.ru
       │                                                ▼
       │                                          чат-доска MAX
       │                                                ▲
       │                       контроллеры поллят outbound
       │                                                │
       └────── reply через тот же /max-proxy/ ─────────┘

Local PWA (web-ui)

DOSEmax Local (home screen) → fetch http://awdc.local/api/* → ESP32
       ▲                                                       ▼
       │                                              REST handlers
       │                                                       ▼
       └─────────────── ответ напрямую ─────────────────── состояние

Никакого MAX, никакого внешнего сервера — телефон ↔ контроллер по локальной Wi-Fi (AP контроллера или одна общая сеть).

Cloud-схема идентична существующей L2-lite. Local-схема — это просто HTTP-клиент к ESP32. PWA в обоих случаях — просто другой способ ОТКРЫТЬ уже существующую страницу.

Стоимость

  • Реализация: ~5 часов (см. разбивку в tasks/pwa-mini-app.md).
  • Поддержка: 0 — manifest и иконки не зависят от изменений транспорта.
  • Размер max-webapp/dist/: +20-40 КБ (иконки PNG).
  • Прошивка: +10-30 КБ в Flash (manifest.json + 2 PNG, gzipped, в PROGMEM через тот же embed-pipeline что и остальная статика).

Будущее

Если когда-нибудь захочется push-уведомлений без MAX:

  1. Поднять VAPID-ключи на awdc.nikolaev.world
  2. Endpoint для подписок (POST /push/subscribe — браузер отправляет endpoint+keys)
  3. Контроллер при аварии → POST на наш сервер → сервер шлёт push подписанным операторам через FCM/Mozilla Push

Это новый канал уведомлений, не транспорт команд. Команды по-прежнему через MAX. Этот шаг — отдельная задача, не в составе PWA.

Service worker можно добавить позже без поломки существующих установок — браузер регистрирует SW при следующем заходе на сайт, иконка и manifest продолжают работать как раньше.

Решённые вопросы (2026-05-22)

  • Какие entry-point оборачиваем? → Оба: cloud (max-webapp) + local (web-ui).
  • Иконка? → drop из существующего web-ui/public/logo.png, рендерим в 192/512 PNG.
  • Service worker сейчас? → Нет, отложено.
  • iOS? → Нет, только Android Chrome.
  • Имя? → DOSEmax.

См. также

  • tasks/pwa-mini-app.md — план реализации
  • tasks/local-https-ca.md — отложенная задача: HTTPS на ESP32 через локальный CA, чтобы DOSEmax Local работал как полноценный PWA (а не как bookmark)
  • doc/user/install-pwa.md — инструкция оператору
  • ADR-010 — основа board-режима и L2-lite