- README, CLAUDE.md, docs: назначение и границы, архитектура, конвенции, план - docs/local-research.md — 36 находок по формату Health Auto Export, снятых на живых данных; документация приложения местами расходится с тем, что оно шлёт - Taskfile, .golangci.yml, самодокументируемый config.example.toml
7.0 KiB
7.0 KiB
План
Живой документ: шаги отмечаем по мере готовности, отложенное копится внизу.
Ближайшая цель
Минимальное ядро, которое принимает и сохраняет пакет. Проверяем его локально: сервис поднят на рабочей машине, телефон шлёт на её IP по локальной сети. Ни деплоя, ни домена, ни TLS для этого не нужно — они понадобятся, когда сервис поедет на rivendell (шаг 8).
Это шаги 1–2.
Шаги
- 1. Каркас.
Taskfile.yml,.golangci.yml,CLAUDE.md, TOML-конфиг с валидацией на старте, логгер, подкомандаserveс/healthz. - 2. Приём без разбора.
POST /api/v1/ingest: лимит тела, gzip, запись тела в архив, строка вdelivery. Разбора ещё нет. ← подключаем телефон по локальной сети - 3. Разбор и хранилище. Миграции, часовые объекты метрик
(
bucket, ключметрика + слой + час) +workout/recordпо своимid, вывод слоя из выравнивания меток, канонизация с округлением чисел и хеш содержимого, слияние точек в объект, два формата дат,healthlog reindex. - 4. Read API. Каталог метрик со слоями и диапазонами, точки с пагинацией и выбором слоя (сборка из часовых объектов), тренировки, записи.
- 5. Самоописание. Каталог разрезов + выведенные из данных схемы содержимого со статистикой + статичная схема контракта API.
- 6.
healthlog import. Заливка полной истории кусками по годам. - 7. Наблюдаемость.
/stats: последняя доставка, счётчики, тишина. - 8. Деплой.
Dockerfile, сборка образа локально, доставка на rivendell, конфиг Caddy, поддомен, токены.
Порядок неслучаен. После шага 2 автоматизация в Health Auto Export включена и копит настоящие пакеты в сыром архиве. Документация формата HAE скудная, поэтому разбор на шаге 3 пишем по реальным данным, а не по догадкам — и заодно видим фактический объём и характер потока.
Отложено
Отсев идентичных тел доставок поВычеркнуто: находка 2 показала, что порядок ключей в JSON нестабилен, поэтому два пакета с одними и теми же данными почти никогда не совпадают побайтно — хеш тела не сработает. Дедупликация возможна только по канонизированному содержимому, а это и делает хеш часового объекта. Отдельная механика не нужна.sha256.- Ретеншен сырого архива (
storage.raw_retention, 14 дней) — удаление старых тел. Пока архив не подчищается; включить после того, как разбор устоится, иначе страховка исчезнет раньше, чем перестанет быть нужна. - Порог
sealed— с какого возраста час считается запечатанным. Ставим по факту: сначала пишемWARNна изменение старых объектов и смотрим, какая глубина досчёта встречается в жизни (наблюдалось до 22 минут, находка 10). - Алерт «данных нет N часов». Тихо сломавшаяся автоматизация — главный
эксплуатационный риск коллектора. В v1 факт виден в
/stats; активное уведомление добавим после. - Аннотации к схемам — человеческие описания метрик поверх выведенных схем, либо рукописный каталог. Выбор зависит от того, насколько стабильным окажется формат; меняться он может только с обновлением Health Auto Export, а это отслеживается.
- Схема тренировок — глубину вывода определим по факту, когда увидим, как приходят маршруты.
- Пометка локализованных полей в схемах.
context,valueи подобные приходят на языке телефона и изменятся при смене языка iOS — клиентам не стоит завязываться на конкретные строки (local-research.md, находка 8). - Вторая автоматизация без группировки для
sleep_analysisиheart_rate_variability— минутная группировка съедает фазы сна и межударные интервалы ценой ~130 точек в сутки (находка 6). - Переход на более грубый нижний слой. Посекундный слой стоит ~730 МБ в
год против ~20 МБ у минутного. Когда решим, что мелкая подробность не нужна,
достаточно выключить несуммированную автоматизацию — старые данные останутся
в своих слоях, переписывать ничего не придётся. Обратный путь тоже есть:
ручной экспорт из Apple Health +
healthlog importвосстанавливает нижний слой. - NDJSON-поток для больших выборок из read API.
- Слой агрегаций поверх сырья (суточные/недельные срезы).
- Разворачивание маршрутов тренировок в отдельную таблицу — если появится клиент, которому мало отдачи тренировки одним пакетом.