mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
feat(garmin): sync Garmin Connect through the Personal API
Garmin Connect now offers a Personal API: a person creates a key for their own account and any client can buy short-lived tokens with it. HealthLog takes that key in a new Garmin card, checks it with Garmin before storing it sealed on the user row, and polls every hour. - Transport: key exchange with a per-process token cache keyed on the key, one re-exchange on a refused token and then reauth_required, the 429 Retry-After carried to the caller, the China host tried when the global one does not know the key, answers read with a byte cap, all egress through safeFetch to the two fixed hosts. - Sync: daily summaries, sleep summaries, HRV, weigh-ins and the activity search, chunked to each resource's documented range, read one after another and stopped at the first rate limit; today's menstrual cycle summary adds logged period days the person has not logged. - Backfill: a resumable walk through the shared admission lane that learns where the history starts from the daily summaries and the activities, waits out Garmin's rate limit and stamps its marker. - Types: GARMIN source; BODY_BATTERY and STRESS_LEVEL for Garmin's own day scores, kept apart from the computed STRESS_SCORE; TRAINING_READINESS and ACUTE_TRAINING_LOAD for figures a bridge pushes through the ingest token. All four chart on the recovery page and sit on the ladders, units, ranges, labels and report groups like the device-native scores. - Key expiry: learned from a token capped short of a day, else estimated from the validity picked at connect; the card warns a week ahead and asks for a new key once Garmin refuses the old one. - Ledger, health, freshness, watchdog, backup classes, wipe plan, encrypted-column registry, rotation script, OpenAPI and all ten locales carry the new integration. The old "no Garmin connector" note is gone.
This commit is contained in:
1 parent
67909f2d9d
commit
e2ba86b12f
119 files changed
+5798
-400
No files matched your search
@@ -15,6 +15,7 @@ import type { ConnectionHealth } from "@/lib/integrations/connection-health";
|
||||
import type { MetricFreshnessEntry } from "@/lib/integrations/sync-verdict";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import type { PublishedFitbitRetirement } from "@/lib/fitbit/retirement";
|
||||
import type { GarminKeyExpiry } from "@/lib/garmin/key-expiry";
|
||||
|
||||
export type IntegrationKey =
|
||||
| "withings"
|
||||
@@ -30,7 +31,9 @@ export type IntegrationKey =
|
||||
// status and inherits the shared verdict.
|
||||
| "nightscout"
|
||||
// v1.43 — Dawarich movement context; publishes `health` only.
|
||||
| "dawarich";
|
||||
| "dawarich"
|
||||
// Garmin Personal API; a pasted key, like Nightscout.
|
||||
| "garmin";
|
||||
/**
|
||||
* One envelope entry as the cards read it. Since v1.42.4 the cards render
|
||||
* `health` alone; the older health-shaped fields (ledger `state`, `lastError`,
|
||||
@@ -54,7 +57,7 @@ export interface IntegrationStatusViewModel {
|
||||
failureThreshold?: number;
|
||||
configured?: boolean;
|
||||
connected?: boolean;
|
||||
/** WHOOP / Fitbit / Google Health: older history still loading. */
|
||||
/** WHOOP / Fitbit / Google Health / Garmin: older history still loading. */
|
||||
backfillCompleted?: boolean | null;
|
||||
/**
|
||||
* Google Health: data types whose history backfill ended before the start
|
||||
@@ -70,6 +73,8 @@ export interface IntegrationStatusViewModel {
|
||||
hasOwnCredentials?: boolean;
|
||||
/** Nightscout: a stored token. */
|
||||
hasToken?: boolean;
|
||||
/** Garmin: when the stored key stops working, resolved on the server. */
|
||||
keyExpiry?: GarminKeyExpiry | null;
|
||||
/** Per-metric last-seen timestamps with the server-computed stale flag. */
|
||||
metricFreshness?: MetricFreshnessEntry[];
|
||||
/** Fitbit: the end of the legacy Web API, resolved on the server. */
|
||||
|
||||
Reference in new issue
Block a user