- Add PocketBase admin auth to notifications endpoint (was returning 0 users)
- Store null instead of 100 for body battery when Garmin returns no data
- Update decision engine to skip body battery rules when values are null
- Dashboard and email already display "N/A" for null values
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add 15-minute notification granularity (*/15 cron) so users get emails
at their configured time instead of rounding to the nearest hour
- Add DailyLog fallback to most recent when today's log doesn't exist,
preventing 100/100/Unknown default values before morning sync
- Show "Last synced" indicator when displaying stale data
- Change Garmin sync to 6-hour intervals (0,6,12,18 UTC) to ensure
data is available before European morning notifications
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Apply 2x multiplier for vigorous intensity minutes (matches Garmin)
- Use calendar week (Mon-Sun) instead of trailing 7 days for intensity
- Add HRV yesterday fallback when today's data returns empty
- Add user-configurable phase intensity goals with new defaults:
- Menstrual: 75, Follicular: 150, Ovulation: 100
- Early Luteal: 120, Late Luteal: 50
- Update garmin-sync and today routes to use user-specific phase limits
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add OAuth1 to OAuth2 token exchange using Garmin's exchange endpoint
- Track refresh token expiry (~30 days) instead of access token expiry (~21 hours)
- Auto-refresh access tokens in cron sync before they expire
- Update Python script to output refresh_token_expires_at
- Add garminRefreshTokenExpiresAt field to User type and database schema
- Fix token input UX: show when warning active, not just when disconnected
- Add Cache-Control headers to /api/user and /api/garmin/status to prevent stale data
- Add oauth-1.0a package for OAuth1 signature generation
The system now automatically refreshes OAuth2 tokens using the stored OAuth1 token,
so users only need to re-run the Python auth script every ~30 days (when refresh
token expires) instead of every ~21 hours (when access token expires).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add daily email notification system that sends training decisions
at each user's preferred time in their timezone.
Features:
- Timezone-aware notification matching using Intl.DateTimeFormat
- DailyLog-based notifications with duplicate prevention
- Nutrition guidance integration via getNutritionGuidance
- Graceful error handling (continues processing on per-user failures)
- Summary response with detailed stats
Includes 20 tests covering:
- CRON_SECRET authentication
- Timezone matching (UTC and America/New_York)
- DailyLog existence and already-sent checks
- Email content assembly
- Error handling and response format
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>