Real-Time (SignalR)

SignalR hubs (AuditHub, NotificationHub), React hooks, connection management, and tenant group scoping.

SCRIPE uses SignalR for real-time bi-directional communication between the backend and frontend. Two hubs are available: AuditHub for live audit log streaming and NotificationHub for instant notification delivery.

Real-Time Architecture

Real-Time Communication Architecture
ASP.NET Core
Custom Hooks
uses context
SignalRProvider — React Context
connects
SignalR Hubs — WebSocket + fallback
AuditHub
NotificationHub

SignalR Hubs

HubPathPurposeEvents
AuditHub/hubs/auditReal-time audit log streamingAuditLogCreated
NotificationHub/hubs/notificationPush notificationsNotificationReceived, UnreadCountChanged

SignalR Provider

SignalRProvider — Connection ManagementTypeScript
// Manages SignalR connections with automatic reconnection
/**
 * Exported function defining parameters and fields for signal r provider configurations.
 */
export function SignalRProvider({ children }: { children: ReactNode }) {
  const { token } = useAuthStore();
  const [auditConnection, setAuditConnection] = useState<HubConnection | null>(null);
  const [notifConnection, setNotifConnection] = useState<HubConnection | null>(null);

  useEffect(() => {
    if (!token) return;

    // Create connection with auto-reconnect
    const audit = new HubConnectionBuilder()
      .withUrl(`${API_URL}/hubs/audit`, {
        accessTokenFactory: () => token,
      })
      .withAutomaticReconnect([0, 2000, 5000, 10000, 30000])
      .configureLogging(LogLevel.Warning)
      .build();

    const notif = new HubConnectionBuilder()
      .withUrl(`${API_URL}/hubs/notification`, {
        accessTokenFactory: () => token,
      })
      .withAutomaticReconnect([0, 2000, 5000, 10000, 30000])
      .build();

    audit.start().catch(console.error);
    notif.start().catch(console.error);

    setAuditConnection(audit);
    setNotifConnection(notif);

    return () => {
      audit.stop();
      notif.stop();
    };
  }, [token]);

  return (
    <SignalRContext.Provider value={{ auditConnection, notifConnection }}>
      {children}
    </SignalRContext.Provider>
  );
}

React Hooks

useAuditStream.tsTypeScript
export function useAuditStream(maxItems = 100) {
  const [logs, setLogs] = useState<AuditLogDto[]>([]);
  const { auditConnection } = useSignalR();

  useEffect(() => {
    if (!auditConnection) return;

    const handler = (log: AuditLogDto) => {
      setLogs(prev => [log, ...prev].slice(0, maxItems));
    };

    auditConnection.on("AuditLogCreated", handler);
    return () => auditConnection.off("AuditLogCreated", handler);
  }, [auditConnection, maxItems]);

  return { logs, clearLogs: () => setLogs([]) };
}

Connection States

StateDescriptionRetry Timing
ConnectedActive WebSocket connection—
ReconnectingLost connection, attempting reconnect0s → 2s → 5s → 10s → 30s
DisconnectedAll retry attempts exhaustedManual reconnect required
ConnectingInitial connection in progress—