feat: Strava support — bulk-export .gz/.tcx import fix + .tcx parser; full Strava API OAuth live sync (activities via streams) with Profile connect UI; colour gym/no-GPS sports red
Build and push images / validate (push) Successful in 3s
Build and push images / build-backend (push) Successful in 6s
Build and push images / build-worker (push) Successful in 5s
Build and push images / build-frontend (push) Successful in 8s

This commit is contained in:
2026-06-21 10:18:09 +01:00
parent e8615dd12d
commit 07139120df
12 changed files with 1169 additions and 116 deletions
+22
View File
@@ -45,6 +45,7 @@ class User(Base):
named_routes = relationship("NamedRoute", back_populates="user", cascade="all, delete-orphan")
weight_logs = relationship("WeightLog", back_populates="user", cascade="all, delete-orphan")
garmin_connect_config = relationship("GarminConnectConfig", back_populates="user", uselist=False, cascade="all, delete-orphan")
strava_config = relationship("StravaConfig", back_populates="user", uselist=False, cascade="all, delete-orphan")
class GarminConnectConfig(Base):
@@ -67,6 +68,27 @@ class GarminConnectConfig(Base):
user = relationship("User", back_populates="garmin_connect_config")
class StravaConfig(Base):
"""Per-user Strava OAuth tokens and sync state. Tokens are Fernet-encrypted
(same SECRET_KEY scheme as Garmin credentials)."""
__tablename__ = "strava_configs"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False, unique=True, index=True)
athlete_id = Column(String(64), nullable=True)
athlete_name = Column(String(256), nullable=True)
access_token_enc = Column(String(512), nullable=False)
refresh_token_enc = Column(String(512), nullable=False)
expires_at = Column(DateTime(timezone=True), nullable=True) # access-token expiry
sync_enabled = Column(Boolean, default=True)
sync_lookback_days = Column(Integer, default=30) # -1 = all-time, first sync only
last_sync_at = Column(DateTime(timezone=True), nullable=True)
last_sync_status = Column(String(512), nullable=True)
created_at = Column(DateTime(timezone=True), default=now_utc)
user = relationship("User", back_populates="strava_config")
class WeightLog(Base):
"""Manual weight entries separate from health_metrics for easy tracking."""
__tablename__ = "weight_logs"