mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 14:11:13 -05:00
Refactor notification system
This commit is contained in:
304
docs/NOVU_MIGRATION.md
Normal file
304
docs/NOVU_MIGRATION.md
Normal file
@@ -0,0 +1,304 @@
|
||||
# Migrating from Legacy to Novu Notifications
|
||||
|
||||
This guide covers migrating from the old notification system to the new Novu-powered system.
|
||||
|
||||
## Migration Overview
|
||||
|
||||
The new notification system:
|
||||
- Uses Novu for multi-channel delivery
|
||||
- Supports granular preferences per workflow
|
||||
- Includes frequency controls
|
||||
- Provides delivery tracking
|
||||
- Backwards compatible with existing preferences
|
||||
|
||||
## Data Migration
|
||||
|
||||
### Step 1: Migrate Existing User Preferences
|
||||
|
||||
The system automatically migrates old preferences from `user_preferences.email_notifications` and `user_preferences.push_notifications` to the new `user_notification_preferences` table.
|
||||
|
||||
Run this migration script to batch migrate existing users:
|
||||
|
||||
```sql
|
||||
-- Migrate existing users to new notification preferences
|
||||
INSERT INTO user_notification_preferences (
|
||||
user_id,
|
||||
channel_preferences,
|
||||
workflow_preferences,
|
||||
frequency_settings
|
||||
)
|
||||
SELECT
|
||||
user_id,
|
||||
jsonb_build_object(
|
||||
'in_app', true,
|
||||
'email', COALESCE((email_notifications->>'system_announcements')::boolean, true),
|
||||
'push', COALESCE((push_notifications->>'browser_enabled')::boolean, false),
|
||||
'sms', false
|
||||
),
|
||||
jsonb_build_object(
|
||||
'review-reply', COALESCE((email_notifications->>'review_replies')::boolean, true),
|
||||
'new-follower', COALESCE((email_notifications->>'new_followers')::boolean, true),
|
||||
'system-announcement', COALESCE((email_notifications->>'system_announcements')::boolean, true),
|
||||
'weekly-digest', COALESCE((email_notifications->>'weekly_digest')::boolean, false),
|
||||
'monthly-digest', COALESCE((email_notifications->>'monthly_digest')::boolean, true)
|
||||
),
|
||||
jsonb_build_object(
|
||||
'digest', 'daily',
|
||||
'max_per_hour', 10
|
||||
)
|
||||
FROM user_preferences
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM user_notification_preferences
|
||||
WHERE user_notification_preferences.user_id = user_preferences.user_id
|
||||
);
|
||||
```
|
||||
|
||||
### Step 2: Create Novu Subscribers for Existing Users
|
||||
|
||||
Create a batch script to register all existing users as Novu subscribers:
|
||||
|
||||
```typescript
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { notificationService } from '@/lib/notificationService';
|
||||
|
||||
async function migrateUsersToNovu() {
|
||||
const { data: profiles, error } = await supabase
|
||||
.from('profiles')
|
||||
.select('user_id, username, display_name')
|
||||
.is('banned', false);
|
||||
|
||||
if (error) {
|
||||
console.error('Error fetching profiles:', error);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const profile of profiles) {
|
||||
// Get user's auth data for email
|
||||
const { data: { user } } = await supabase.auth.admin.getUserById(profile.user_id);
|
||||
|
||||
if (user?.email) {
|
||||
await notificationService.createSubscriber({
|
||||
subscriberId: profile.user_id,
|
||||
email: user.email,
|
||||
firstName: profile.display_name?.split(' ')[0],
|
||||
lastName: profile.display_name?.split(' ').slice(1).join(' '),
|
||||
data: {
|
||||
username: profile.username,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Migrated user: ${profile.username}`);
|
||||
|
||||
// Rate limit to avoid overwhelming Novu API
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Migration complete!');
|
||||
}
|
||||
```
|
||||
|
||||
## Code Migration
|
||||
|
||||
### Updating Notification Triggers
|
||||
|
||||
**Old Way:**
|
||||
```typescript
|
||||
// Direct database insert (legacy)
|
||||
await supabase
|
||||
.from('notifications')
|
||||
.insert({
|
||||
user_id: userId,
|
||||
type: 'review_reply',
|
||||
content: 'Someone replied to your review',
|
||||
});
|
||||
```
|
||||
|
||||
**New Way:**
|
||||
```typescript
|
||||
// Using Novu via notificationService
|
||||
import { notificationService } from '@/lib/notificationService';
|
||||
|
||||
await notificationService.trigger({
|
||||
workflowId: 'review-reply',
|
||||
subscriberId: userId,
|
||||
payload: {
|
||||
reviewTitle: 'Great ride!',
|
||||
replyAuthor: 'Jane Doe',
|
||||
replyContent: 'Thanks for the review!',
|
||||
reviewUrl: `/reviews/${reviewId}`,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Updating Preference Management
|
||||
|
||||
**Old Way:**
|
||||
```typescript
|
||||
await supabase
|
||||
.from('user_preferences')
|
||||
.update({
|
||||
email_notifications: {
|
||||
review_replies: false,
|
||||
},
|
||||
})
|
||||
.eq('user_id', userId);
|
||||
```
|
||||
|
||||
**New Way:**
|
||||
```typescript
|
||||
import { notificationService } from '@/lib/notificationService';
|
||||
|
||||
await notificationService.updatePreferences(userId, {
|
||||
channelPreferences: {
|
||||
in_app: true,
|
||||
email: true,
|
||||
push: false,
|
||||
sms: false,
|
||||
},
|
||||
workflowPreferences: {
|
||||
'review-reply': false,
|
||||
},
|
||||
frequencySettings: {
|
||||
digest: 'daily',
|
||||
max_per_hour: 10,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If you need to rollback to the old system:
|
||||
|
||||
1. **Keep Old Tables**: Don't drop `user_preferences.email_notifications` or `push_notifications` columns
|
||||
2. **Disable Novu**: Clear `VITE_NOVU_APPLICATION_IDENTIFIER` environment variable
|
||||
3. **Revert Code**: The notification service gracefully handles missing Novu config
|
||||
|
||||
## Testing the Migration
|
||||
|
||||
### Test Checklist
|
||||
|
||||
- [ ] All existing users have entries in `user_notification_preferences`
|
||||
- [ ] User preferences match their previous settings
|
||||
- [ ] Test users receive notifications on all enabled channels
|
||||
- [ ] Webhook is receiving delivery events
|
||||
- [ ] Notification logs are being created
|
||||
- [ ] User preference changes sync to Novu
|
||||
- [ ] Unsubscribe links work correctly
|
||||
- [ ] Email templates render correctly
|
||||
- [ ] Push notifications work in supported browsers
|
||||
- [ ] In-app notifications display correctly
|
||||
|
||||
### Test Script
|
||||
|
||||
```typescript
|
||||
// Test notification delivery
|
||||
import { notificationService } from '@/lib/notificationService';
|
||||
|
||||
async function testNotifications(testUserId: string) {
|
||||
console.log('Testing notification delivery...');
|
||||
|
||||
// Test each workflow
|
||||
const workflows = [
|
||||
{
|
||||
id: 'review-reply',
|
||||
payload: {
|
||||
reviewTitle: 'Test Review',
|
||||
replyAuthor: 'Test User',
|
||||
replyContent: 'Test reply content',
|
||||
reviewUrl: '/test',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'new-follower',
|
||||
payload: {
|
||||
followerName: 'Test Follower',
|
||||
followerProfile: '/profile/test',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const workflow of workflows) {
|
||||
const result = await notificationService.trigger({
|
||||
workflowId: workflow.id,
|
||||
subscriberId: testUserId,
|
||||
payload: workflow.payload,
|
||||
});
|
||||
|
||||
console.log(`${workflow.id}: ${result.success ? '✓' : '✗'}`);
|
||||
|
||||
if (!result.success) {
|
||||
console.error(`Error: ${result.error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Batch Operations
|
||||
|
||||
When migrating large numbers of users:
|
||||
|
||||
1. **Rate Limiting**: Add delays between API calls
|
||||
2. **Batch Size**: Process in chunks of 100-500 users
|
||||
3. **Error Handling**: Log failures and retry
|
||||
4. **Progress Tracking**: Store migration state
|
||||
|
||||
### Optimization Tips
|
||||
|
||||
1. **Cache Templates**: Load notification templates once at startup
|
||||
2. **Async Processing**: Use edge functions for heavy operations
|
||||
3. **Database Indexes**: Already created on `user_notification_preferences`
|
||||
4. **Connection Pooling**: Supabase handles this automatically
|
||||
|
||||
## Monitoring Post-Migration
|
||||
|
||||
Track these metrics after migration:
|
||||
|
||||
```sql
|
||||
-- Delivery success rate
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COUNT(*) FILTER (WHERE status = 'delivered') * 100.0 / COUNT(*) as success_rate
|
||||
FROM notification_logs
|
||||
WHERE created_at > NOW() - INTERVAL '7 days'
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date DESC;
|
||||
|
||||
-- Channel usage
|
||||
SELECT
|
||||
channel,
|
||||
COUNT(*) as total_sent,
|
||||
COUNT(*) FILTER (WHERE status = 'delivered') as delivered,
|
||||
COUNT(*) FILTER (WHERE read_at IS NOT NULL) as read
|
||||
FROM notification_logs
|
||||
WHERE created_at > NOW() - INTERVAL '7 days'
|
||||
GROUP BY channel;
|
||||
|
||||
-- User engagement
|
||||
SELECT
|
||||
COUNT(DISTINCT user_id) as active_users,
|
||||
AVG(EXTRACT(EPOCH FROM (read_at - delivered_at))) as avg_time_to_read_seconds
|
||||
FROM notification_logs
|
||||
WHERE read_at IS NOT NULL
|
||||
AND created_at > NOW() - INTERVAL '7 days';
|
||||
```
|
||||
|
||||
## Support During Migration
|
||||
|
||||
1. **Backup Data**: Export old notification preferences before migration
|
||||
2. **Phased Rollout**: Enable for a subset of users first
|
||||
3. **Feature Flag**: Use admin settings to control Novu enablement
|
||||
4. **User Communication**: Notify users of new notification features
|
||||
5. **Monitor Logs**: Watch edge function logs during migration
|
||||
|
||||
## Timeline
|
||||
|
||||
Recommended migration timeline:
|
||||
|
||||
- **Week 1**: Test with internal users
|
||||
- **Week 2**: Rollout to 10% of users
|
||||
- **Week 3**: Rollout to 50% of users
|
||||
- **Week 4**: Complete rollout
|
||||
- **Week 5**: Remove legacy code (optional)
|
||||
Reference in New Issue
Block a user