ADR-011: DOSEmax — две PWA (cloud + local) как entry-point'ы¶
Дата: 2026-05-22 Статус: Accepted (планируется к реализации) Связано: ADR-010
Контекст¶
В проекте уже есть две независимые веб-страницы:
max-webapp/(cloud) наhttps://awdc.nikolaev.world/— Mini App для парка, команды через MAX-бота. Доступен из любой точки с интернетом.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:
- Поднять VAPID-ключи на
awdc.nikolaev.world - Endpoint для подписок (
POST /push/subscribe— браузер отправляет endpoint+keys) - Контроллер при аварии → 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