File

src/tracking/tracking.controller.ts

Prefix

tracking

Index

Methods

Methods

Async getTripTracking
getTripTracking(id: string)
Decorators :
@Get('trips/:id')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard)
@ApiOperation({summary: 'Get trip tracking data with ETA'})
Parameters :
Name Type Optional
id string No
Returns : unknown
Async getVehiclePosition
getVehiclePosition(id: string)
Decorators :
@Get('vehicles/:id')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard)
@ApiOperation({summary: 'Get single vehicle position with recent trail'})
Parameters :
Name Type Optional
id string No
Returns : unknown
Async getVehiclePositions
getVehiclePositions(orgId: string)
Decorators :
@Get('vehicles')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard)
@ApiOperation({summary: 'Get all vehicle positions for the organization'})
Parameters :
Name Type Optional
orgId string No
Returns : unknown
Async getVehicleTrail
getVehicleTrail(id: string, from: string, to: string)
Decorators :
@Get('vehicles/:id/trail')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard)
@ApiOperation({summary: 'Get GPS trail for a vehicle in a time range'})
Parameters :
Name Type Optional
id string No
from string No
to string No
Returns : unknown
Async mixTelematicsWebhook
mixTelematicsWebhook(body: any, secret: string)
Decorators :
@SkipThrottle()
@Post('webhook/mix-telematics')
@HttpCode(HttpStatus.OK)
@ApiOperation({summary: 'Webhook for Mix Telematics position updates'})
Parameters :
Name Type Optional
body any No
secret string No
Returns : unknown
Async updatePosition
updatePosition(id: string, body: literal type)
Decorators :
@Post('vehicles/:id/position')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard)
@HttpCode(HttpStatus.OK)
@ApiOperation({summary: 'Manually update vehicle position (testing/demo)'})
Parameters :
Name Type Optional
id string No
body literal type No
Returns : unknown
import {
  Controller,
  Get,
  Post,
  Body,
  Param,
  Query,
  UseGuards,
  HttpCode,
  HttpStatus,
  UnauthorizedException,
  Logger,
} from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { SkipThrottle } from '@nestjs/throttler';
import { TrackingService } from './tracking.service';
import { TrackingGateway } from './tracking.gateway';
import { CombinedAuthGuard } from '../auth/guards/combined-auth.guard';
import { CurrentUser } from '../auth/decorators/current-user.decorator';

@ApiTags('Tracking')
@Controller('tracking')
export class TrackingController {
  private readonly logger = new Logger(TrackingController.name);

  constructor(
    private readonly trackingService: TrackingService,
    private readonly trackingGateway: TrackingGateway,
  ) {}

  @Get('vehicles')
  @ApiBearerAuth()
  @UseGuards(CombinedAuthGuard)
  @ApiOperation({ summary: 'Get all vehicle positions for the organization' })
  async getVehiclePositions(
    @CurrentUser('organizationId') orgId: string,
  ) {
    return this.trackingService.getVehiclePositions(orgId);
  }

  @Get('vehicles/:id')
  @ApiBearerAuth()
  @UseGuards(CombinedAuthGuard)
  @ApiOperation({ summary: 'Get single vehicle position with recent trail' })
  async getVehiclePosition(@Param('id') id: string) {
    return this.trackingService.getVehiclePosition(id);
  }

  @Get('vehicles/:id/trail')
  @ApiBearerAuth()
  @UseGuards(CombinedAuthGuard)
  @ApiOperation({ summary: 'Get GPS trail for a vehicle in a time range' })
  async getVehicleTrail(
    @Param('id') id: string,
    @Query('from') from: string,
    @Query('to') to: string,
  ) {
    const fromDate = from ? new Date(from) : new Date(Date.now() - 24 * 60 * 60 * 1000);
    const toDate = to ? new Date(to) : new Date();
    return this.trackingService.getVehicleTrail(id, fromDate, toDate);
  }

  @Get('trips/:id')
  @ApiBearerAuth()
  @UseGuards(CombinedAuthGuard)
  @ApiOperation({ summary: 'Get trip tracking data with ETA' })
  async getTripTracking(@Param('id') id: string) {
    return this.trackingService.getTripTracking(id);
  }

  @Post('vehicles/:id/position')
  @ApiBearerAuth()
  @UseGuards(CombinedAuthGuard)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Manually update vehicle position (testing/demo)' })
  async updatePosition(
    @Param('id') id: string,
    @Body() body: { lat: number; lng: number; speed: number; heading: number },
  ) {
    const position = await this.trackingService.updateVehiclePosition(
      id,
      body.lat,
      body.lng,
      body.speed,
      body.heading,
      'manual',
    );

    // Broadcast via WebSocket
    this.trackingGateway.broadcastPosition(id, position);

    return position;
  }

  @SkipThrottle()
  @Post('webhook/mix-telematics')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Webhook for Mix Telematics position updates' })
  async mixTelematicsWebhook(
    @Body() body: any,
    @Query('secret') secret: string,
  ) {
    const webhookSecret = process.env.MIX_TELEMATICS_WEBHOOK_SECRET;

    if (!webhookSecret || secret !== webhookSecret) {
      throw new UnauthorizedException('Invalid webhook secret');
    }

    this.logger.log(
      `Mix Telematics webhook received: ${Array.isArray(body) ? body.length : 1} events`,
    );

    // Process position updates from webhook payload
    const events = Array.isArray(body) ? body : [body];

    for (const event of events) {
      try {
        if (event.vehicleId && event.lat !== undefined && event.lng !== undefined) {
          const position = await this.trackingService.updateVehiclePosition(
            event.vehicleId,
            event.lat,
            event.lng,
            event.speed || 0,
            event.heading || 0,
            'mix_telematics',
          );

          // Broadcast to connected WebSocket clients
          this.trackingGateway.broadcastPosition(event.vehicleId, position);
        }
      } catch (err: any) {
        this.logger.error(
          `Failed to process webhook event for vehicle ${event.vehicleId}: ${err.message}`,
        );
      }
    }

    return { received: events.length };
  }
}

results matching ""

    No results matching ""