store: scope GetProfile/UpdateProfile to a user_id

Part of per-user profile isolation: profile rows are no longer a global
singleton, so every read/write requires the caller's userID. This also
requires scoping ListActivities, ListWorkoutKinds, UpsertActivity,
CreateWorkoutKind, GetSyncState, UpdateSyncState, and ResetAllSyncedData
to userID, plus updating all related tests in the store package.
This commit is contained in:
2026-07-25 13:17:17 +02:00
parent b0a7462ebe
commit ef410863c6
10 changed files with 104 additions and 67 deletions

View File

@@ -52,15 +52,15 @@ type Activity struct {
// UpsertActivity inserts a new activity or updates the existing row for the
// same garmin_activity_id (idempotent, safe to call on every sync pass), and
// returns its internal id.
func (db *DB) UpsertActivity(ctx context.Context, a Activity) (int64, error) {
func (db *DB) UpsertActivity(ctx context.Context, userID int64, a Activity) (int64, error) {
_, err := db.ExecContext(ctx, `
INSERT INTO activities (
garmin_activity_id, event_type_key, workout_id, start_time_utc,
user_id, garmin_activity_id, event_type_key, workout_id, start_time_utc,
duration_seconds, distance_meters, avg_hr, max_hr,
avg_speed_mps, elevation_gain_m,
aerobic_training_effect, anaerobic_training_effect, vo2max_value,
raw_json, updated_at
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?, datetime('now'))
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?, datetime('now'))
ON CONFLICT(garmin_activity_id) DO UPDATE SET
event_type_key=excluded.event_type_key,
workout_id=excluded.workout_id,
@@ -77,18 +77,18 @@ func (db *DB) UpsertActivity(ctx context.Context, a Activity) (int64, error) {
raw_json=excluded.raw_json,
updated_at=datetime('now')
`,
a.GarminActivityID, a.EventTypeKey, a.WorkoutID, a.StartTimeUTC,
userID, a.GarminActivityID, a.EventTypeKey, a.WorkoutID, a.StartTimeUTC,
a.DurationSeconds, a.DistanceMeters, a.AvgHR, a.MaxHR,
a.AvgSpeedMps, a.ElevationGainM,
a.AerobicTrainingEffect, a.AnaerobicTrainingEffect, a.VO2MaxValue,
a.RawJSON,
)
if err != nil {
return 0, fmt.Errorf("upsert activity %d: %w", a.GarminActivityID, err)
return 0, fmt.Errorf("upsert activity %d for user %d: %w", a.GarminActivityID, userID, err)
}
var id int64
if err := db.QueryRowContext(ctx, `SELECT id FROM activities WHERE garmin_activity_id = ?`, a.GarminActivityID).Scan(&id); err != nil {
if err := db.QueryRowContext(ctx, `SELECT id FROM activities WHERE garmin_activity_id = ? AND user_id = ?`, a.GarminActivityID, userID).Scan(&id); err != nil {
return 0, fmt.Errorf("fetch id for activity %d: %w", a.GarminActivityID, err)
}
return id, nil
@@ -137,10 +137,10 @@ type ActivityFilter struct {
Offset int
}
// ListActivities returns activities newest-first, optionally filtered by date range.
func (db *DB) ListActivities(ctx context.Context, f ActivityFilter) ([]Activity, error) {
query := `SELECT ` + activityColumns + ` FROM activities WHERE 1=1`
var args []any
// ListActivities returns activities for userID newest-first, optionally filtered by date range.
func (db *DB) ListActivities(ctx context.Context, userID int64, f ActivityFilter) ([]Activity, error) {
query := `SELECT ` + activityColumns + ` FROM activities WHERE user_id = ?`
args := []any{userID}
if f.FromDate != "" {
query += ` AND start_time_utc >= ?`
args = append(args, f.FromDate)
@@ -157,7 +157,7 @@ func (db *DB) ListActivities(ctx context.Context, f ActivityFilter) ([]Activity,
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
return nil, fmt.Errorf("list activities: %w", err)
return nil, fmt.Errorf("list activities for user %d: %w", userID, err)
}
defer rows.Close()