|
| 1 | +# Realtime Updates System |
| 2 | + |
| 3 | +This document describes the optimized realtime update system that supports both Server-Sent Events (SSE) and Pusher for different deployment environments. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The realtime system automatically chooses the best transport method based on your deployment environment: |
| 8 | + |
| 9 | +- **SSE (Server-Sent Events)**: Default for traditional deployments (Docker, self-hosted) |
| 10 | +- **Pusher**: Automatically selected for serverless deployments (Vercel, Netlify) when configured |
| 11 | + |
| 12 | +## Architecture |
| 13 | + |
| 14 | +### Client-Side Components |
| 15 | + |
| 16 | +- `RealtimeService`: Main service that manages provider selection and operation |
| 17 | +- `SSEProvider`: Handles Server-Sent Events connections |
| 18 | +- `PusherProvider`: Handles Pusher Channels connections |
| 19 | +- `useRealtime()`: React hook for easy integration |
| 20 | + |
| 21 | +### Server-Side Components |
| 22 | + |
| 23 | +- `ServerRealtimeService`: Broadcasts messages to both SSE and Pusher |
| 24 | +- Backward-compatible with existing `broadcastUpdate()` function |
| 25 | + |
| 26 | +## Configuration |
| 27 | + |
| 28 | +### Environment Variables |
| 29 | + |
| 30 | +```bash |
| 31 | +# Optional: Force a specific provider (auto-detected if not set) |
| 32 | +NEXT_PUBLIC_REALTIME_PROVIDER="auto" # "sse", "pusher", or "auto" |
| 33 | + |
| 34 | +# SSE Configuration (used by default for non-serverless) |
| 35 | +NEXT_PUBLIC_SSE_ENDPOINT="/api/events" |
| 36 | +NEXT_PUBLIC_SSE_RECONNECT_INTERVAL="3000" |
| 37 | + |
| 38 | +# Pusher Configuration (required for Pusher support) |
| 39 | +PUSHER_APP_ID="your-app-id" |
| 40 | +NEXT_PUBLIC_PUSHER_KEY="your-pusher-key" |
| 41 | +PUSHER_SECRET="your-pusher-secret" |
| 42 | +NEXT_PUBLIC_PUSHER_CLUSTER="us2" |
| 43 | +PUSHER_USE_TLS="true" |
| 44 | +``` |
| 45 | + |
| 46 | +### Auto-Detection Logic |
| 47 | + |
| 48 | +1. Check `NEXT_PUBLIC_REALTIME_PROVIDER` for explicit preference |
| 49 | +2. Detect if running on Vercel (`VERCEL=1`) or Netlify |
| 50 | +3. Check if Pusher is properly configured |
| 51 | +4. Use Pusher for serverless + configured, otherwise SSE |
| 52 | + |
| 53 | +## Usage |
| 54 | + |
| 55 | +### Basic React Hook |
| 56 | + |
| 57 | +```typescript |
| 58 | +import { useRealtime } from '@/hooks/use-realtime'; |
| 59 | + |
| 60 | +function MyComponent() { |
| 61 | + const { connected, providerType, subscribe } = useRealtime(); |
| 62 | + |
| 63 | + useEffect(() => { |
| 64 | + const unsubscribe = subscribe('devlog-updated', (devlog) => { |
| 65 | + console.log('Devlog updated:', devlog); |
| 66 | + }); |
| 67 | + |
| 68 | + return unsubscribe; |
| 69 | + }, [subscribe]); |
| 70 | + |
| 71 | + return ( |
| 72 | + <div> |
| 73 | + Status: {connected ? 'Connected' : 'Disconnected'} |
| 74 | + ({providerType}) |
| 75 | + </div> |
| 76 | + ); |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +### Event-Specific Hooks |
| 81 | + |
| 82 | +```typescript |
| 83 | +import { useDevlogEvents } from '@/hooks/use-realtime'; |
| 84 | + |
| 85 | +function DevlogList() { |
| 86 | + const { onDevlogCreated, onDevlogUpdated } = useDevlogEvents(); |
| 87 | + |
| 88 | + useEffect(() => { |
| 89 | + const unsubscribeCreated = onDevlogCreated((devlog) => { |
| 90 | + // Handle new devlog |
| 91 | + }); |
| 92 | + |
| 93 | + const unsubscribeUpdated = onDevlogUpdated((devlog) => { |
| 94 | + // Handle updated devlog |
| 95 | + }); |
| 96 | + |
| 97 | + return () => { |
| 98 | + unsubscribeCreated(); |
| 99 | + unsubscribeUpdated(); |
| 100 | + }; |
| 101 | + }, [onDevlogCreated, onDevlogUpdated]); |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +### Server-Side Broadcasting |
| 106 | + |
| 107 | +```typescript |
| 108 | +import { serverRealtimeService } from '@/lib/api/server-realtime'; |
| 109 | + |
| 110 | +// In your API route or server action |
| 111 | +await serverRealtimeService.broadcastDevlogCreated(newDevlog); |
| 112 | + |
| 113 | +// Or use the generic broadcast method |
| 114 | +await serverRealtimeService.broadcast('custom-event', data); |
| 115 | +``` |
| 116 | + |
| 117 | +### Status Components |
| 118 | + |
| 119 | +```typescript |
| 120 | +import { RealtimeStatus, RealtimeDebugInfo } from '@/components/realtime/realtime-status'; |
| 121 | + |
| 122 | +// Minimal status indicator |
| 123 | +<RealtimeStatus /> |
| 124 | + |
| 125 | +// Detailed status (development only) |
| 126 | +<RealtimeDebugInfo /> |
| 127 | +``` |
| 128 | + |
| 129 | +## Event Types |
| 130 | + |
| 131 | +The system supports the following standard events: |
| 132 | + |
| 133 | +- `project-created` |
| 134 | +- `project-updated` |
| 135 | +- `project-deleted` |
| 136 | +- `devlog-created` |
| 137 | +- `devlog-updated` |
| 138 | +- `devlog-deleted` |
| 139 | +- `devlog-note-created` |
| 140 | +- `devlog-note-updated` |
| 141 | +- `devlog-note-deleted` |
| 142 | + |
| 143 | +## Deployment Scenarios |
| 144 | + |
| 145 | +### Traditional Docker Deployment |
| 146 | + |
| 147 | +Uses SSE by default. No additional configuration needed. |
| 148 | + |
| 149 | +```bash |
| 150 | +# .env |
| 151 | +# No realtime configuration needed - SSE works out of the box |
| 152 | +``` |
| 153 | + |
| 154 | +### Vercel Deployment |
| 155 | + |
| 156 | +1. Sign up for Pusher at https://pusher.com/channels |
| 157 | +2. Create a new app and get your credentials |
| 158 | +3. Add to Vercel environment variables: |
| 159 | + |
| 160 | +```bash |
| 161 | +PUSHER_APP_ID="123456" |
| 162 | +NEXT_PUBLIC_PUSHER_KEY="abcdef123456" |
| 163 | +PUSHER_SECRET="your-secret-key" |
| 164 | +NEXT_PUBLIC_PUSHER_CLUSTER="us2" |
| 165 | +``` |
| 166 | + |
| 167 | +The system will automatically detect Vercel and use Pusher. |
| 168 | + |
| 169 | +### Local Development |
| 170 | + |
| 171 | +SSE is used by default. To test Pusher locally: |
| 172 | + |
| 173 | +```bash |
| 174 | +# .env.local |
| 175 | +NEXT_PUBLIC_REALTIME_PROVIDER="pusher" |
| 176 | +PUSHER_APP_ID="123456" |
| 177 | +NEXT_PUBLIC_PUSHER_KEY="abcdef123456" |
| 178 | +PUSHER_SECRET="your-secret-key" |
| 179 | +NEXT_PUBLIC_PUSHER_CLUSTER="us2" |
| 180 | +``` |
| 181 | + |
| 182 | +## Migration from Previous System |
| 183 | + |
| 184 | +The new system is backward compatible. Existing code using `useRealtimeStore` will continue to work without changes. |
| 185 | + |
| 186 | +### Old API (still supported) |
| 187 | +```typescript |
| 188 | +const { connected, subscribe } = useRealtimeStore(); |
| 189 | +``` |
| 190 | + |
| 191 | +### New API (recommended) |
| 192 | +```typescript |
| 193 | +const { connected, subscribe } = useRealtime(); |
| 194 | +``` |
| 195 | + |
| 196 | +## Troubleshooting |
| 197 | + |
| 198 | +### Debug Information |
| 199 | + |
| 200 | +Enable debug mode in development: |
| 201 | + |
| 202 | +```typescript |
| 203 | +import { logRealtimeConfig } from '@/lib/realtime'; |
| 204 | + |
| 205 | +// Log current configuration |
| 206 | +logRealtimeConfig(); |
| 207 | +``` |
| 208 | + |
| 209 | +### Common Issues |
| 210 | + |
| 211 | +1. **Pusher not connecting**: Check that all environment variables are set correctly |
| 212 | +2. **SSE disconnecting**: Verify your deployment supports long-running connections |
| 213 | +3. **No events received**: Ensure server-side code is using `serverRealtimeService.broadcast()` |
| 214 | + |
| 215 | +### Testing |
| 216 | + |
| 217 | +You can test the system by checking the browser console and network tab: |
| 218 | + |
| 219 | +- SSE: Look for `/api/events` connection in Network tab |
| 220 | +- Pusher: Look for WebSocket connections to `ws-*.pusherapp.com` |
| 221 | + |
| 222 | +## Performance Considerations |
| 223 | + |
| 224 | +- **SSE**: Lower latency, uses one HTTP connection per client |
| 225 | +- **Pusher**: Higher latency but better for serverless, uses WebSockets |
| 226 | +- Both providers include automatic reconnection logic |
| 227 | +- Connection state is managed automatically |
| 228 | + |
| 229 | +## Security |
| 230 | + |
| 231 | +- Pusher keys marked as `NEXT_PUBLIC_*` are safe for client-side use |
| 232 | +- `PUSHER_SECRET` must be kept server-side only |
| 233 | +- SSE endpoints should validate client permissions as needed |
0 commit comments