111 lines
2.7 KiB
Markdown
111 lines
2.7 KiB
Markdown
# Notifications Specification
|
|
|
|
## Job to Be Done
|
|
|
|
When I wake up each morning, I want to receive an email with my training decision, so that I don't need to open the app to know if I should train.
|
|
|
|
## Email Notifications
|
|
|
|
### Daily Training Email
|
|
|
|
Sent at user's preferred `notificationTime` (default: 07:00).
|
|
|
|
**Subject:**
|
|
```
|
|
PhaseFlow: [STATUS] - Day [cycleDay] ([phase])
|
|
```
|
|
|
|
Example: `PhaseFlow: ✅ TRAIN - Day 12 (FOLLICULAR)`
|
|
|
|
**Body:**
|
|
```
|
|
Good morning!
|
|
|
|
Today's Decision: [STATUS]
|
|
Reason: [reason]
|
|
|
|
Current Metrics:
|
|
- Cycle Day: [cycleDay] ([phase])
|
|
- Body Battery: [bbCurrent]
|
|
- HRV: [hrvStatus]
|
|
- Week Intensity: [weekIntensity]/[phaseLimit] min
|
|
|
|
Nutrition Today:
|
|
- Seeds: [seeds]
|
|
- Carbs: [carbRange]
|
|
- Keto: [ketoGuidance]
|
|
|
|
[Optional: Seed switch alert if day 15]
|
|
[Optional: Token expiration warning]
|
|
|
|
---
|
|
PhaseFlow - Training with your cycle, not against it
|
|
```
|
|
|
|
### Token Expiration Warnings
|
|
|
|
**14 Days Before:**
|
|
Subject: `⚠️ PhaseFlow: Garmin tokens expire in 14 days`
|
|
|
|
**7 Days Before:**
|
|
Subject: `🚨 PhaseFlow: Garmin tokens expire in 7 days - action required`
|
|
|
|
## Email Provider
|
|
|
|
Using Resend via `resend` npm package.
|
|
|
|
**Configuration:**
|
|
- `RESEND_API_KEY` - API key for Resend
|
|
- `EMAIL_FROM` - Sender address (e.g., `PhaseFlow <noreply@phaseflow.app>`)
|
|
|
|
## Email Utilities (`src/lib/email.ts`)
|
|
|
|
**Functions:**
|
|
- `sendDailyNotification(user, decision, dailyData)` - Send morning email
|
|
- `sendTokenExpirationWarning(user, daysUntilExpiry)` - Send warning email
|
|
|
|
## Cron Jobs
|
|
|
|
### `/api/cron/notifications`
|
|
|
|
Protected by `CRON_SECRET` header.
|
|
|
|
**Trigger:** Daily at notification times
|
|
|
|
**Process:**
|
|
1. Find all users with `notificationTime` matching current hour
|
|
2. For each user:
|
|
- Fetch current decision from decision engine
|
|
- Send email via Resend
|
|
- Update `DailyLog.notificationSentAt`
|
|
|
|
### Timezone Handling
|
|
|
|
Users store preferred timezone (e.g., `America/New_York`).
|
|
|
|
Cron runs every hour. Check if current hour in user's timezone matches their `notificationTime`.
|
|
|
|
## Rate Limiting
|
|
|
|
- Max 1 notification per user per day
|
|
- Check `DailyLog.notificationSentAt` before sending
|
|
- Only send if null or different date
|
|
|
|
## Success Criteria
|
|
|
|
1. Email arrives within 5 minutes of scheduled time
|
|
2. Email contains all relevant metrics and guidance
|
|
3. Token warnings sent at 14 and 7 day thresholds
|
|
4. No duplicate notifications on same day
|
|
|
|
## Acceptance Tests
|
|
|
|
- [ ] Daily email contains decision status and reason
|
|
- [ ] Daily email includes nutrition guidance
|
|
- [ ] Seed switch alert included on day 15
|
|
- [ ] Token warning email sent at 14-day threshold
|
|
- [ ] Token warning email sent at 7-day threshold
|
|
- [ ] Duplicate notifications prevented
|
|
- [ ] Timezone conversion correct for notification time
|
|
- [ ] CRON_SECRET required for endpoint access
|