api: build per-user garmin.Client/sync.Service via lazy caching

Server no longer holds one fixed Garmin/Sync pair -- garminFor/syncFor
build and cache one instance per user, keyed off their own profile's
Garmin credentials and a per-user token-store subdirectory. The background
incremental sync loop now iterates every provisioned user each tick
instead of syncing one global account.
This commit is contained in:
2026-07-25 18:10:09 +02:00
parent 2cda0adbf8
commit 5930cc4ef5
4 changed files with 216 additions and 92 deletions

View File

@@ -31,24 +31,12 @@ func main() {
}
defer db.Close()
profile, err := db.GetProfile(context.Background())
if err != nil {
log.Fatalf("load profile: %v", err)
if cfg.LegacyOwnerOIDCSub != "" {
if err := db.ClaimLegacyOwner(context.Background(), cfg.LegacyOwnerOIDCSub); err != nil {
log.Fatalf("claim legacy owner: %v", err)
}
}
garminClient := garmin.NewClient(garmin.Config{
PythonPath: cfg.GarminPythonPath,
ServerPath: cfg.GarminServerPath,
GarminEmail: profile.GarminEmail,
GarminPassword: profile.GarminPassword,
TokenStorePath: cfg.GarminTokenStore,
})
defer garminClient.Close()
syncSvc := appsync.NewService(garminClient, db, appsync.Config{
MinConfidence: cfg.MinConfidence,
}, nil)
authVerifier, err := auth.NewOIDCVerifier(context.Background(), auth.OIDCConfig{
IssuerURL: cfg.OIDCIssuerURL,
ClientID: cfg.OIDCClientID,
@@ -60,7 +48,13 @@ func main() {
log.Fatalf("oidc: %v", err)
}
server := api.NewServer(db, garminClient, syncSvc, authVerifier, api.SessionConfig{
server := api.NewServer(db, garmin.NewClient, garmin.Config{
PythonPath: cfg.GarminPythonPath,
ServerPath: cfg.GarminServerPath,
TokenStorePath: cfg.GarminTokenStoreRoot,
}, appsync.Config{
MinConfidence: cfg.MinConfidence,
}, authVerifier, api.SessionConfig{
Secret: cfg.SessionSecret,
Duration: cfg.SessionDuration,
Secure: cfg.SessionSecure,
@@ -70,7 +64,7 @@ func main() {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
go runIncrementalSyncLoop(ctx, syncSvc, cfg.IncrementalSyncEvery)
go runIncrementalSyncLoop(ctx, server, cfg.IncrementalSyncEvery)
httpServer := &http.Server{Addr: cfg.Addr, Handler: server.Router()}
go func() {
@@ -89,9 +83,10 @@ func main() {
}
}
// runIncrementalSyncLoop periodically syncs new activities in the
// background so the frontend doesn't need to trigger every sync manually.
func runIncrementalSyncLoop(ctx context.Context, svc *appsync.Service, every time.Duration) {
// runIncrementalSyncLoop periodically syncs new activities for every
// provisioned user in the background so the frontend doesn't need to
// trigger every sync manually.
func runIncrementalSyncLoop(ctx context.Context, server *api.Server, every time.Duration) {
ticker := time.NewTicker(every)
defer ticker.Stop()
for {
@@ -99,13 +94,7 @@ func runIncrementalSyncLoop(ctx context.Context, svc *appsync.Service, every tim
case <-ctx.Done():
return
case <-ticker.C:
if err := svc.IncrementalSync(ctx); err != nil {
log.Printf("incremental sync: %v", err)
continue
}
if err := svc.FillPendingDetails(ctx, 50); err != nil {
log.Printf("fill pending details: %v", err)
}
server.RunIncrementalSyncForAllUsers(ctx)
}
}
}