- агрегация появилась в ответе на запрос: род метрики измеряется сверкой слоёв, нижний слой HAE не суммируется никогда - переведённые строки хранятся дословно с приписанным кодом HealthKit; снято правило «настройки данных у всех проходов одинаковы» — слой в ключе - добавлены устаревание нижнего слоя по проверенному экспорту и MCP поверх read API; план вырос до 11 шагов
110 lines
6.2 KiB
Markdown
110 lines
6.2 KiB
Markdown
# healthlog
|
||
|
||
Коллектор данных Apple Health. Принимает выгрузки из
|
||
[Health Auto Export](https://www.healthyapps.dev/apps/health-auto-export/),
|
||
складывает их в единое хранилище и отдаёт другим моим проектам через HTTP API.
|
||
|
||
## Зачем
|
||
|
||
Данные о здоровье и тренировках нужны сразу нескольким приложениям: анализ
|
||
здоровья, разбор тренировок, мотиватор по активности. Интегрировать каждое
|
||
из них с Health Auto Export по отдельности — значит в каждом писать приём,
|
||
дедупликацию и хранение заново.
|
||
|
||
healthlog делает это один раз. Телефон шлёт данные в него, все остальные
|
||
проекты берут данные из него.
|
||
|
||
## Границы
|
||
|
||
Это **хранилище**, а не аналитика. healthlog принимает, дедуплицирует,
|
||
хранит и отдаёт. Он не переименовывает поля Apple и не интерпретирует
|
||
значения — этим занимается тот, кто данные читает.
|
||
|
||
Одну уступку хранилище всё же делает: оно умеет свести метрику к запрошенной
|
||
сетке («шаги по дням»). Иначе каждый из клиентов повторял бы одну и ту же
|
||
логику выбора слоя, а ошибиться в ней легко — просуммировать не тот разрез и
|
||
получить завышение втрое. Но род свёртки не проставлен вручную, а **измерен**
|
||
сверкой слоёв между собой; где измерить не вышло, свёртка не предлагается
|
||
вовсе.
|
||
|
||
Источников два: Health Auto Export (куплен, пожизненный премиум) — ежедневный
|
||
поток, и родной экспорт Apple Health раз в 2–3 месяца — источник истины для
|
||
нижнего слоя.
|
||
|
||
## Как устроено
|
||
|
||
```
|
||
iPhone ──HTTPS POST──► healthlog ──► сырой архив (.json.gz, 14 дней)
|
||
│ │
|
||
│ └── страховка разбора, не склад
|
||
▼
|
||
SQLite ──┬──► HTTP read API ──► мои приложения
|
||
(точки по │
|
||
слоям) └──► MCP ───────────► агенты
|
||
▲
|
||
экспорт Apple ──────────────┘ нижний слой, раз в 2–3 месяца
|
||
```
|
||
|
||
Приём сначала кладёт тело запроса на диск как есть и только потом разбирает.
|
||
Значит, ошибка в нашем разборе не может привести к потере данных: хранилище
|
||
пересобирается из архива командой `healthlog reindex`. Архив при этом
|
||
недолговечен — дальше истина в самих точках, и потому точки хранятся
|
||
дословно.
|
||
|
||
Подробности — [docs/architecture.md](docs/architecture.md).
|
||
|
||
## Состояние
|
||
|
||
В разработке. Готовы шаги 1–2 из 11: сервис принимает пакеты и складывает их в
|
||
сырой архив. Разбора, хранилища и read API ещё нет — план в
|
||
[docs/plan.md](docs/plan.md).
|
||
|
||
Разведка формата закончена: 41 находка на живом потоке, половина расходится с
|
||
документацией Health Auto Export — [docs/local-research.md](docs/local-research.md).
|
||
|
||
## Команды
|
||
|
||
```
|
||
healthlog serve приём + read API + MCP
|
||
healthlog import родной экспорт Apple Health (шаг 8)
|
||
healthlog reindex пересборка хранилища из архива (шаг 3)
|
||
healthlog healthcheck проверка живости для docker HEALTHCHECK
|
||
```
|
||
|
||
## Локальный запуск
|
||
|
||
Конфиг необязателен — без него берутся умолчания (`:8080`, `./healthlog.db`,
|
||
`./raw`). Для своих значений скопируй `config.example.toml` в `config.toml`.
|
||
|
||
```
|
||
task run
|
||
```
|
||
|
||
Проверка:
|
||
|
||
```
|
||
curl localhost:8080/healthz
|
||
curl -X POST localhost:8080/api/v1/ingest -d '{"data":{"metrics":[]}}'
|
||
```
|
||
|
||
### Подключение телефона по локальной сети
|
||
|
||
Сервис слушает все интерфейсы (`addr = ":8080"`), так что телефон в той же
|
||
сети достучится по IP машины. В Health Auto Export заводится одна
|
||
автоматизация: **REST API**, формат **JSON**, минимальная гранулярность
|
||
(«Summarize Data» выключен), URL вида `http://<ip-машины>:8080/api/v1/ingest`.
|
||
|
||
Если `auth.write_tokens` пуст, проверка токена выключена — для доверенной
|
||
локальной сети этого достаточно, сервис пишет об этом `write auth disabled`
|
||
на старте. Для доступа снаружи понадобится и токен, и TLS (шаг 8).
|
||
|
||
|
||
## Документация
|
||
|
||
- [docs/architecture.md](docs/architecture.md) — устройство, схема данных, API, принятые решения
|
||
- [docs/conventions.md](docs/conventions.md) — как пишем код
|
||
- [docs/plan.md](docs/plan.md) — шаги и отложенное
|
||
- [docs/local-research.md](docs/local-research.md) — что показал реальный поток
|
||
Health Auto Export; источник истины по формату, документация приложения
|
||
местами расходится с тем, что оно шлёт
|