Files
thrilltrack-explorer/docs/NOVU_MIGRATION.md
2025-10-01 12:29:31 +00:00

8.1 KiB

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:

-- 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:

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:

// Direct database insert (legacy)
await supabase
  .from('notifications')
  .insert({
    user_id: userId,
    type: 'review_reply',
    content: 'Someone replied to your review',
  });

New Way:

// 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:

await supabase
  .from('user_preferences')
  .update({
    email_notifications: {
      review_replies: false,
    },
  })
  .eq('user_id', userId);

New Way:

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

// 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:

-- 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)