Skip to main content

Overview

The Admin Gateway (/admin namespace) provides real-time monitoring capabilities for administrative dashboards, allowing operations teams to track driver availability, trip assignments, and authentication events.

Gateway Configuration

// From admin.gateway.ts:15
@WebSocketGateway({ namespace: '/admin', cors: true })
export class AdminGateway implements OnGatewayConnection, OnGatewayDisconnect

Connection

Authentication

Admin connections require a JWT token from a user with administrative privileges (not DRIVER type).
import { io } from 'socket.io-client';

const adminSocket = io('http://localhost:3000/admin', {
  auth: { token: adminAccessToken }
});

adminSocket.on('connect', () => {
  console.log('Connected to admin namespace');
});

adminSocket.on('connect_error', (error) => {
  console.error('Admin connection error:', error.message);
  // Common errors:
  // - "Drivers not allowed in /admin"
  // - "User inactive"
  // - "Invalid session"
});

Server-Side Validation

// From admin.gateway.ts:32
async handleConnection(client: Socket) {
  try {
    const token =
      (client.handshake.auth as any)?.token ||
      (client.handshake.headers?.authorization || '').replace(
        /^Bearer\s+/i,
        '',
      );
    if (!token) throw new Error('Missing token');

    const payload = this.tokenService.verifyAccessToken<any>(token);
    const userId = payload?.sub as string | undefined;
    const sid = payload?.sid as string | undefined;
    if (!userId || !sid) throw new Error('Missing sub/sid');

    const user = await this.usersRepo.findById(userId);
    if (!user) throw new Error('User not found');
    if (user.status !== UserStatus.ACTIVE) throw new Error('User inactive');

    // Reject drivers from admin namespace
    if (user.userType === UserType.DRIVER)
      throw new Error('Drivers not allowed in /admin');

    const session = await this.sessionRepo.findOne({ where: { jti: sid } });
    if (!session || session.revoked) throw new Error('Invalid session');

    // Store admin context
    (client as any).data = { userId, sid, role: user.userType };
    (client as any).join?.('admin:all');

    this.logger.log(`Admin WS connected user=${userId} sid=${sid}`);
  } catch (e) {
    this.logger.warn(`Admin WS handshake failed: ${(e as Error).message}`);
    client.disconnect();
  }
}

Admin Rooms

Admin sockets are automatically joined to specific rooms:
  • admin:all - All admin users (global broadcasts)
  • admin:drivers - Driver monitoring dashboard
  • trip:{tripId} - Specific trip monitoring (dynamically joined)

Driver Monitoring Events

driver:availability:update

Broadcast when any driver’s availability status changes. Payload:
{
  driverId: string;
  isOnline: boolean;
  isAvailableForTrips: boolean;
  availabilityReason?: string;
  currentTripId?: string | null;
  lastLocationTimestamp?: string;
  // ... full availability snapshot
}
Client Example:
adminSocket.on('driver:availability:update', (data) => {
  console.log('Driver availability changed:', data.driverId);
  console.log('Online:', data.isOnline);
  console.log('Available:', data.isAvailableForTrips);
  console.log('Current trip:', data.currentTripId);
  
  // Update driver list/map
  updateDriverStatus(data.driverId, {
    online: data.isOnline,
    available: data.isAvailableForTrips,
    tripId: data.currentTripId,
    reason: data.availabilityReason
  });
});
Server Implementation:
// From driver-availability.gateway.ts:140
this.server
  .to('admin:drivers')
  .emit('driver:availability:update', updated);

admin:driver:availability:update

Alternative broadcast for driver availability (domain event). Payload: Same as driver:availability:update Server Implementation:
// From driver-availability-realtime.publisher.ts:27
this.adminGateway.server?.emit(
  'admin:driver:availability:update',
  ev.snapshot,
);

driver:location:update

Broadcast when driver’s GPS location updates. Payload:
{
  driverId: string;
  lastLocation: {
    lat: number;
    lng: number;
  };
  lastLocationTimestamp: string;  // ISO timestamp
}
Client Example:
adminSocket.on('driver:location:update', (data) => {
  console.log('Driver location update:', data.driverId);
  console.log('Position:', data.lastLocation);
  
  // Update driver marker on admin map
  updateDriverMarkerOnMap(data.driverId, {
    lat: data.lastLocation.lat,
    lng: data.lastLocation.lng,
    timestamp: data.lastLocationTimestamp
  });
});

admin:driver:location:update

Alternative location broadcast (full snapshot). Payload: Full availability snapshot including location
// From driver-availability-realtime.publisher.ts:40
this.gateway.server?.emit('admin:driver:location:update', ev.snapshot);

driver:trip:update

Broadcast when driver’s trip assignment changes. Payload:
{
  driverId: string;
  currentTripId: string | null;
  availabilityReason?: string;
  isAvailableForTrips: boolean;
}
Client Example:
adminSocket.on('driver:trip:update', (data) => {
  console.log('Driver trip assignment:', data.driverId);
  console.log('Trip ID:', data.currentTripId);
  console.log('Available:', data.isAvailableForTrips);
  
  // Update dashboard
  updateDriverTripAssignment(data.driverId, data.currentTripId);
});

admin:driver:trip:update

Alternative trip update broadcast (full snapshot).
// From driver-availability-realtime.publisher.ts:49
this.gateway.server?.emit('admin:driver:trip:update', ev.snapshot);

Trip Monitoring Events

All trip lifecycle events are broadcast to the admin namespace on trip:{tripId} rooms:

Trip Request Events

// Trip requested
adminSocket.on('trip:requested', (data) => {
  console.log('New trip requested:', data.tripId);
  console.log('Passenger:', data.passengerId);
  console.log('Pickup:', data.pickup?.address);
  
  addTripToMonitor(data);
});

// Assignment started
adminSocket.on('trip:assigning_started', (data) => {
  console.log('Finding driver for trip:', data.tripId);
  updateTripStatus(data.tripId, 'assigning');
});

Assignment Events

// Driver offered
adminSocket.on('trip:assignment:offered', (data) => {
  console.log('Trip offered to driver:', data.driverId);
  console.log('Assignment ID:', data.assignmentId);
  console.log('Expires at:', data.expiresAt);
  
  addAssignmentOffer(data);
});

// Driver accepted
adminSocket.on('trip:assignment:accepted', (data) => {
  console.log('Driver accepted:', data.driverId);
  updateTripStatus(data.tripId, 'accepted');
});

// Driver rejected
adminSocket.on('trip:assignment:rejected', (data) => {
  console.log('Driver rejected:', data.driverId);
  console.log('Reason:', data.reason);
  logRejection(data);
});

// Assignment expired
adminSocket.on('trip:assignment:expired', (data) => {
  console.log('Assignment expired:', data.assignmentId);
  logExpiration(data);
});

// No drivers found
adminSocket.on('trip:no_drivers_found', (data) => {
  console.log('No drivers found for trip:', data.tripId);
  console.log('Reason:', data.reason);
  alertNoDrivers(data);
});

Trip Progress Events

// Driver assigned
adminSocket.on('trip:driver_assigned', (data) => {
  console.log('Driver assigned to trip:', data.tripId);
  console.log('Driver:', data.driverId);
  console.log('Passenger:', data.passengerId);
  
  updateTripAssignment(data);
});

// Arriving started
adminSocket.on('trip:arriving_started', (data) => {
  updateTripStatus(data.tripId, 'arriving');
});

// Driver en route
adminSocket.on('trip:driver_en_route', (data) => {
  console.log('Driver ETA:', data.etaMinutes);
  updateTripETA(data.tripId, data.etaMinutes);
});

// Driver arrived
adminSocket.on('trip:driver_arrived_pickup', (data) => {
  updateTripStatus(data.tripId, 'waiting_for_passenger');
});

// Trip started
adminSocket.on('trip:started', (data) => {
  updateTripStatus(data.tripId, 'in_progress');
});

// Trip completed
adminSocket.on('trip:completed', (data) => {
  console.log('Trip completed:', data.tripId);
  console.log('Fare:', data.fareTotal, data.currency);
  
  updateTripStatus(data.tripId, 'completed');
  logTripCompletion(data);
});

Authentication Events

admin:auth:user_logged_in

Broadcast when any user logs in. Payload:
{
  userId: string;
  userType: string;         // DRIVER, PASSENGER, ADMIN, etc.
  sid: string;              // Session ID
  sessionType: string;      // e.g., "mobile", "web"
  at: string;               // ISO timestamp
  deviceInfo?: object;      // Device information
}
Client Example:
adminSocket.on('admin:auth:user_logged_in', (data) => {
  console.log('User logged in:', data.userId);
  console.log('Type:', data.userType);
  console.log('Session:', data.sid);
  console.log('Device:', data.deviceInfo);
  
  // Add to active sessions log
  logUserLogin(data);
  
  // Update online users count
  if (data.userType === 'DRIVER') {
    incrementOnlineDriversCount();
  }
});
Server Implementation:
// From auth-realtime.publisher.ts:21
this.driverGateway?.server?.emit('admin:auth:user_logged_in', {
  userId: ev.userId,
  userType: ev.userType,
  sid: ev.sid,
  sessionType: ev.sessionType,
  at: ev.at,
  deviceInfo: ev.deviceInfo,
});

Complete Admin Dashboard Example

import { io } from 'socket.io-client';

class AdminDashboardClient {
  constructor(adminToken) {
    this.socket = io('http://localhost:3000/admin', {
      auth: { token: adminToken },
      reconnection: true,
    });

    this.drivers = new Map();
    this.trips = new Map();
    this.setupListeners();
  }

  setupListeners() {
    this.socket.on('connect', () => {
      console.log('✅ Admin dashboard connected');
      this.loadInitialData();
    });

    // Driver monitoring
    this.socket.on('driver:availability:update', (data) => {
      this.updateDriver(data);
    });

    this.socket.on('driver:location:update', (data) => {
      this.updateDriverLocation(data);
    });

    this.socket.on('driver:trip:update', (data) => {
      this.updateDriverTrip(data);
    });

    // Trip monitoring
    this.socket.on('trip:requested', (data) => {
      this.addTrip(data);
    });

    this.socket.on('trip:assignment:offered', (data) => {
      this.logAssignmentOffer(data);
    });

    this.socket.on('trip:assignment:accepted', (data) => {
      this.onTripAccepted(data);
    });

    this.socket.on('trip:no_drivers_found', (data) => {
      this.onNoDriversFound(data);
    });

    this.socket.on('trip:completed', (data) => {
      this.onTripCompleted(data);
    });

    // Auth monitoring
    this.socket.on('admin:auth:user_logged_in', (data) => {
      this.logUserLogin(data);
    });
  }

  async loadInitialData() {
    // Load current drivers and trips via REST API
    const [drivers, trips] = await Promise.all([
      fetch('/api/admin/drivers/active').then(r => r.json()),
      fetch('/api/admin/trips/active').then(r => r.json())
    ]);

    drivers.forEach(d => this.drivers.set(d.driverId, d));
    trips.forEach(t => this.trips.set(t.tripId, t));
    
    this.renderDashboard();
  }

  updateDriver(data) {
    this.drivers.set(data.driverId, {
      ...this.drivers.get(data.driverId),
      ...data
    });
    this.renderDriversList();
    this.updateDriverMarker(data.driverId);
  }

  updateDriverLocation(data) {
    const driver = this.drivers.get(data.driverId);
    if (driver) {
      driver.lastLocation = data.lastLocation;
      driver.lastLocationTimestamp = data.lastLocationTimestamp;
      this.updateDriverMarker(data.driverId);
    }
  }

  updateDriverTrip(data) {
    const driver = this.drivers.get(data.driverId);
    if (driver) {
      driver.currentTripId = data.currentTripId;
      driver.isAvailableForTrips = data.isAvailableForTrips;
      this.renderDriversList();
    }
  }

  addTrip(data) {
    this.trips.set(data.tripId, data);
    this.renderTripsList();
    this.updateMetrics();
  }

  logAssignmentOffer(data) {
    console.log(`📤 Offer sent to driver ${data.driverId} for trip ${data.tripId}`);
    this.addActivityLog({
      type: 'offer',
      message: `Trip ${data.tripId} offered to driver ${data.driverId}`,
      timestamp: new Date().toISOString()
    });
  }

  onTripAccepted(data) {
    const trip = this.trips.get(data.tripId);
    if (trip) {
      trip.status = 'accepted';
      trip.driverId = data.driverId;
      this.renderTripsList();
    }
  }

  onNoDriversFound(data) {
    console.warn(`⚠️  No drivers found for trip ${data.tripId}`);
    this.showAlert({
      type: 'warning',
      message: `No drivers available for trip ${data.tripId}`,
      reason: data.reason
    });
  }

  onTripCompleted(data) {
    const trip = this.trips.get(data.tripId);
    if (trip) {
      trip.status = 'completed';
      trip.fareTotal = data.fareTotal;
      this.trips.delete(data.tripId); // Remove from active trips
      this.renderTripsList();
      this.updateMetrics();
    }
  }

  logUserLogin(data) {
    this.addActivityLog({
      type: 'login',
      message: `${data.userType} ${data.userId} logged in`,
      timestamp: data.at,
      device: data.deviceInfo
    });
  }

  renderDashboard() {
    this.renderDriversList();
    this.renderTripsList();
    this.renderDriversMap();
    this.updateMetrics();
  }

  renderDriversList() {
    const container = document.getElementById('drivers-list');
    container.innerHTML = '';

    const onlineDrivers = Array.from(this.drivers.values())
      .filter(d => d.isOnline);

    onlineDrivers.forEach(driver => {
      const div = document.createElement('div');
      div.className = `driver-item ${driver.isAvailableForTrips ? 'available' : 'busy'}`;
      div.innerHTML = `
        <div class="driver-id">${driver.driverId}</div>
        <div class="driver-status">
          ${driver.isAvailableForTrips ? '🟢 Available' : '🔴 Busy'}
        </div>
        <div class="driver-trip">
          ${driver.currentTripId ? `Trip: ${driver.currentTripId}` : 'No active trip'}
        </div>
      `;
      container.appendChild(div);
    });
  }

  renderTripsList() {
    // Render active trips
  }

  renderDriversMap() {
    // Render drivers on map
  }

  updateDriverMarker(driverId) {
    // Update specific driver marker on map
  }

  updateMetrics() {
    const onlineDrivers = Array.from(this.drivers.values())
      .filter(d => d.isOnline).length;
    const availableDrivers = Array.from(this.drivers.values())
      .filter(d => d.isAvailableForTrips).length;
    const activeTrips = this.trips.size;

    document.getElementById('online-drivers').textContent = onlineDrivers;
    document.getElementById('available-drivers').textContent = availableDrivers;
    document.getElementById('active-trips').textContent = activeTrips;
  }

  addActivityLog(entry) {
    const log = document.getElementById('activity-log');
    const item = document.createElement('div');
    item.className = `log-item log-${entry.type}`;
    item.textContent = `${entry.timestamp} - ${entry.message}`;
    log.prepend(item);
  }

  showAlert(alert) {
    // Show alert notification
  }

  disconnect() {
    this.socket.disconnect();
  }
}

// Usage
const dashboard = new AdminDashboardClient(adminAccessToken);

Best Practices

Monitoring Strategy

  • Subscribe to all relevant events on connection
  • Maintain in-memory state for active drivers and trips
  • Load initial data via REST API, then update via WebSocket events
  • Use room subscriptions for specific trip monitoring

Performance Optimization

  • Batch UI updates to avoid excessive re-renders
  • Use virtual scrolling for large driver/trip lists
  • Throttle location updates on map (max 1 update per second per driver)

Error Handling

adminSocket.on('connect_error', (error) => {
  console.error('Admin connection error:', error.message);
  showConnectionError();
});

adminSocket.on('disconnect', (reason) => {
  console.warn('Admin disconnected:', reason);
  showDisconnectedState();
  
  if (reason === 'io server disconnect') {
    // Server forced disconnect - may need to re-authenticate
    checkAuthStatus();
  }
});

Next Steps

Overview

WebSocket architecture and concepts

Connection Setup

How to establish connections

Build docs developers (and LLMs) love