Живые данные · общественный сервис

Карта тревог — живые данные, которые встраивают к себе

Карта воздушных тревог, где страница — только часть продукта. Тревоги, движущиеся цели, линия фронта и термоточки приходят из пяти независимых источников, обновляются каждый по своим часам — 15, 10 и 20 секунд — и уходят наружу открытым JSON-API и виджетом, который чужие сайты ставят к себе в рамку.

Категория
Живые данные · общественный сервис
Оценка стоимости
от €20 000
Обновление
тревоги 15 с · цели 10 с · лента 20 с
Источники
пять, независимых друг от друга
Стек
PHP 8.3, воркеры на Python, SQLite, Leaflet
map.megainet.art
Alerts Map
Главная страница проекта — map.megainet.art

01О проекте

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

При этом страница — только часть продукта. Те же данные уходят наружу открытым JSON-API с ключами и лимитами и виджетом, который другие сайты ставят к себе в рамку. Мы спроектировали и сделали всё: сборщики, хранение, API, карту и виджет.

02Контекст и проблема

Данных, которые отвечают на вопрос «есть ли тревога здесь», не существует в одном месте. Официальные тревоги приходят по областям. Локальные тревоги громад — из другого сервиса. Где цели, словами пишут мониторинговые каналы. Линию фронта публикует картографический проект. Пожары видят спутники. Пять источников, пять форматов, пять разных представлений о том, что такое «сейчас».

Вторая проблема — молчание. Любой из источников может встать: канал замолчал, API начал отваливаться по таймауту, — и честное поведение здесь сказать об этом, а не продолжать рисовать вчерашнюю картину. Карта, которая выглядит спокойной, потому что у неё умер источник, хуже, чем отсутствие карты.

03Задачи проекта

  • одна картина, собранная из пяти независимых источников, у каждого свои часы обновления;
  • ответ за секунды с телефона, на слабой связи, ночью;
  • данные, которые могут уходить с сайта: открытый API с ключами, лимитами и тарифами, и виджет для встраивания;
  • три языка, потому что аудитория не только местная;
  • замолчавший источник должен быть виден как замолчавший, а не как спокойствие;
  • адрес самого сервера не должен доходить до источников, которые он читает.

04Пять источников — одна картина

  • Официальные тревоги по областям — основа карты;
  • локальные тревоги громад и районов, из отдельного сервиса;
  • мониторинговые каналы в Telegram — читаются и через публичные превью, и, для закрытых каналов, по самому протоколу мессенджера;
  • линия фронта — от картографического проекта, который её публикует;
  • термоточки со спутников NASA — пожары, видимые с орбиты.

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

05Четыре файла вместо одной базы

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

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

06Трое часов на одной странице

Разные данные стареют с разной скоростью — и обновляются с разной: тревоги раз в 15 секунд, движущиеся цели раз в 10, лента раз в 20. Один общий интервал был бы неправильным сразу в обе стороны: слишком медленным для целей, расточительным для ленты и лишним запросом каждые несколько секунд для тысяч открытых вкладок.

У каждого блока есть и своё «обновлено». Страница, которая молча перерисовывается, приучает доверять ей ровно тогда, когда доверять не стоит; остановившаяся отметка времени — самый быстрый способ сказать «эта часть устарела».

07Открытый API с ключами и тарифами

Карта публикует то, что знает: текущие тревоги, события, аналитику с тепловой картой «область × корзина времени», ритм суток, типы целей и направления. Аноним получает скромный лимит в минуту; ключ поднимает его, а тарифы растут ступенями для тех, кто на этом строит.

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

08Виджет, который живёт на чужих сайтах

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

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

09Пуши, которые знают ваш регион

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

Поверх тех же данных работает нативное приложение, и ходит оно в своё app-API, а не по страницам сайта. Отдельный адрес живости отвечает реестру приложений: проверка живости не должна стоить дороже той работы, которую проверяет.

10Три языка, включая виджет

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

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

11Читать источники, не показывая свой адрес

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

Ограничитель частоты стоит перед API по той же причине, только наоборот: публичная точка с живыми данными притягивает скрейперов, и лимит на ключ в минуту — это то, что оставляет общественный сервис пригодным для тех, ради кого он написан.

12Технологии

  • PHP 8.3 без фреймворка: маленький роутер, точки API и рендеры;
  • воркеры на Python под системным супервизором — один только на стандартной библиотеке, второй говорит по протоколу мессенджера;
  • SQLite, четыре отдельных файла, у каждого своя причина существовать;
  • Leaflet для карты, ассеты с хешем в имени, виджет фиксированных размеров;
  • веб-пуши с состоянием по регионам, нативное приложение на тех же данных;
  • SOCKS5 наружу для каждого исходящего запроса, лимиты на ключ перед API.

13Результат

Карта, которая отвечает за секунды и продолжает отвечать, когда один из пяти источников встал. Данные, которые можно забрать к себе: открытый API с ключами и тарифами и виджет на трёх языках в двух темах. Уведомления, срабатывающие на границе события, а не на каждый опрос. Нативное приложение на тех же данных и панель, из которой выпускают ключи и сбрасывают состояния.

14Выводы

Три вещи, которые мы унесём в любой проект с живыми данными. Отделять сборщик от страницы: сайт, который идёт к источнику, пока посетитель ждёт, — это сайт, падающий вместе с источником. Давать каждому блоку свои часы и свою отметку времени: один интервал неправильный сразу в обе стороны, а молчаливая перерисовка прячет устаревание. Говорить, когда не знаешь: пустой блок, который в этом признаётся, стоит дороже спокойной на вид карты со вчерашними данными.

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

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

На какие запросы отвечает эта страница

разработка карты воздушных тревог, разработка карты в реальном времени, дашборд живых данных разработка, виджет для встраивания на чужие сайты, публичный json api с ключами, лимиты api тарифы, sqlite для живых данных, воркеры на python systemd, парсер телеграм-каналов, термоточки nasa на карте, разработка приложения на leaflet, веб-пуши по регионам, нативное приложение для веб-сервиса, виджет карты в iframe, как сделать сервис живой карты, разработка открытого api данных, map megainet, карта тревог, где сейчас тревога.