feat(app): offline-кэш GET-запросов на дашборде — фаза 5

CacheInterceptor кэширует каждый успешный GET по путь+параметры в drift
(sqlite нативно, wasm+OPFS в браузере) и подменяет им сетевую ошибку;
провайдер отдаёт Cached<T>, экран показывает баннер «данные на …».
Контракт для остальных экранов — docs/ai/offline-cache.md.

flake.nix: libsecret/pkg-config для линуксовой сборки, jq/curl для
just app-web-assets (сборка sqlite3.wasm + drift_worker.js).
This commit is contained in:
Dmitry
2026-09-19 12:40:08 +03:00
parent 95cd6f176e
commit ffc5ed959a
21 changed files with 15852 additions and 60 deletions
+27
View File
@@ -523,6 +523,33 @@ backup/restore, страница здоровья (`/sync/status`, счётчи
Проверка: приложение работает offline на кэше; APK подписывается и ставится; restore из
ночного `pg_dump` на чистом VPS + `fintracker metrics refresh` воспроизводит все метрики.
**Статус: в работе.** Готово до этой правки (не с нуля):
адаптивные раскладки (`app_shell.dart`, breakpoints 600/1200), тёмная тема
(`theme_controller.dart`), общий паттерн пустых/ошибочных состояний
(`AsyncValueView` — 25 экранов, `EmptyState` — 22), backup — `pg-backup`-сайдкар в
`docker-compose.yml` (ночной `pg_dump -Fc`, retention 14 дней) и runbook в `ops.md`.
Сделано в этом заходе:
- **Offline-кэш** — контракт целиком в [offline-cache.md](docs/ai/offline-cache.md):
`CacheInterceptor` в `apiProvider` кэширует каждый успешный `GET` по `путь+параметры` в
`drift` (sqlite нативно, wasm+OPFS в браузере) и подменяет им сетевую ошибку;
провайдер отдаёт `Cached<T>`, экран показывает один баннер «Нет соединения — данные на …».
Реализовано и протестировано целиком на `features/home/` (дашборд — самый частый экран);
остальные ~24 экрана переводятся по тому же контракту независимо друг от друга.
`app/web/sqlite3.wasm` + `app/web/drift_worker.js` закоммичены, пересборка —
`just app-web-assets`.
- `flutter build linux --debug` не пройден до конца: `flutter_secure_storage_linux` падает на
устаревшем `nlohmann/json.hpp` под `-Werror=deprecated-literal-operator` современных
компиляторов — не связано с кэшем, чинить отдельно перед пунктом CI/Linux. `libsecret` +
`pkg-config` для линуксовой сборки уже добавлены в `flake.nix`.
- `docs/ai/ops.md` поправлен: убрана ссылка на «появится в фазе 2» у `metrics refresh`.
Не начато: CI-джоба для Flutter (`just app-check` в CI, сборка Linux/Windows), консолидация
страницы здоровья (сейчас статусы синка на `/sync`, data quality — блоком на дашборде),
Android release-подпись (нужен keystore от пользователя), off-site бэкап (см. открытый
вопрос §7.9 — решения ещё нет).
## 7. Открытые вопросы (дефолт в скобках, можно менять по ходу)
1. Хранение refresh-токена в web: `localStorage` (дефолт: да, один пользователь, TLS) или