refactor: merge internal/sync into internal/garmin, regroup api files and routes

Garmin auth/sync routes move under /api/garmin/*; sync.Service becomes
garmin.Sync with garmin.SyncConfig/ClientConfig; applog becomes
internal/log; the test mock moves into the garmin package as MockClient
(breaking the test-only import cycle the merge created); stale test
URLs and type names updated to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 16:04:18 +02:00
parent 19c9aecdeb
commit e2b2bf9611
61 changed files with 2007 additions and 982 deletions

View File

@@ -31,7 +31,7 @@ commit); it just no longer gates anything on its own.
### The core problem: Garmin auth needs an account today
`garminFor` builds a per-user `garmin.Client` keyed by a DB user id and
`clientFor` builds a per-user `garmin.Client` keyed by a DB user id and
reads Garmin credentials from that user's `profile` row. To authenticate
with Garmin *before* an account exists, onboarding needs a **temporary,
not-yet-persisted** Garmin session -- keyed by the OIDC subject (already
@@ -45,7 +45,7 @@ finish onboarding.
### Backend: ephemeral pre-account Garmin sessions
New type and `Server` field (`server.go`, alongside the existing
`userGarmin`/`userSync`/etc. per-user maps):
`userClient`/`userSync`/etc. per-user maps):
```go
// setupSession is a temporary, not-yet-persisted Garmin authentication
@@ -81,7 +81,7 @@ Helper methods on `Server` (`server.go`):
spawn new" semantics as `garmin.Client.UpdateCredentials`), builds a
fresh ephemeral client whose `TokenStorePath` is namespaced under
`{root}/setup/{sub}` (distinct from the permanent `{root}/{userID}`
namespacing `garminFor` uses, so two people onboarding concurrently never
namespacing `clientFor` uses, so two people onboarding concurrently never
collide, and so the ephemeral session's persisted Garmin tokens have
*some* home even though no user id exists yet).
- `recordSetupAuthResult(sub string, res garmin.AuthResult)` -- updates a
@@ -140,7 +140,7 @@ r.Route("/setup", func(r chi.Router) {
resumes the already-established Garmin session instead of requiring a
fresh login. Logged, not fatal, on error.
`internal/api/auth.go`'s existing `handleAuthLogin`/`handleAuthMFA`/`handleAuthStatus`
`internal/api/auth.go`'s existing `handleGarminAuthLogin`/`handleGarminAuthMFA`/`handleGarminAuthStatus`
(used by the Profile page's `GarminConnection.tsx` for reconnecting an
already-provisioned user) are untouched.
@@ -204,7 +204,7 @@ simpler than threading a separate `"mfa"` step through, and matches how
`/api/setup/complete` after a successful session creates the user with
`GarminEmail`/`GarminPassword`/`GarminConnectedAt` all set, and promotes
the *same* `*mock.Client` instance into `s.userGarmin[userID]` (asserted
via `garminFor` returning that exact pointer, and `ClosedCalled` still
via `clientFor` returning that exact pointer, and `ClosedCalled` still
false -- proving no redundant re-authentication happened);
`/api/setup/complete` with no prior login attempt → 409;
already-provisioned subject hitting either endpoint → 409; two different