Files
maps-saas/apps/api/src/telemetry/telemetry.controller.ts
T

70 lines
3.0 KiB
TypeScript

import { Controller, Post, Get, Body, Query, Param, UseGuards, HttpException, HttpStatus } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiQuery, ApiParam, ApiSecurity } from '@nestjs/swagger';
import { TelemetryService } from './telemetry.service';
import { DriverTelemetryDto, DriverTelemetryBatchDto } from './dto/driver-telemetry.dto';
import { ApiKeyGuard } from '../common/guards/api-key.guard';
@ApiTags('telemetry')
@ApiSecurity('x-api-key')
@Controller('telemetry')
@UseGuards(ApiKeyGuard)
export class TelemetryController {
constructor(private readonly telemetryService: TelemetryService) {}
@Post()
@ApiOperation({
summary: 'Ingest real-time driver telemetry with elevation & distance 📡⛰️',
description: 'Receives GPS position, speed, heading, distance, and AMSL elevation from driver app.',
})
async ingest(@Body() data: DriverTelemetryDto) {
if (!data.driver_id) {
throw new HttpException('Missing driver_id', HttpStatus.BAD_REQUEST);
}
return this.telemetryService.ingest(data);
}
@Post('batch')
@ApiOperation({
summary: 'Queue navigation telemetry batch 📦',
description: 'Acknowledges a navigation batch immediately; the server persists it from Redis in the background.',
})
async ingestBatch(@Body() body: DriverTelemetryBatchDto) {
if (!body || !Array.isArray(body.points)) {
throw new HttpException('Invalid payload: expected { points: [...] }', HttpStatus.BAD_REQUEST);
}
if (body.points.length > 100) {
throw new HttpException('A telemetry batch may contain at most 100 points', HttpStatus.BAD_REQUEST);
}
return this.telemetryService.enqueueBatch(body.points);
}
@Get('nearby')
@ApiOperation({ summary: 'Query active drivers within spatial radius with elevation & bearing 🚗' })
@ApiQuery({ name: 'lat', required: true, type: Number, description: 'Center latitude' })
@ApiQuery({ name: 'lng', required: true, type: Number, description: 'Center longitude' })
@ApiQuery({ name: 'radius', required: false, type: Number, description: 'Radius in meters (default: 5000m)' })
async getNearby(
@Query('lat') lat: number,
@Query('lng') lng: number,
@Query('radius') radius?: number,
) {
const latNum = Number(lat);
const lngNum = Number(lng);
if (isNaN(latNum) || isNaN(lngNum)) {
throw new HttpException('lat and lng must be valid numbers', HttpStatus.BAD_REQUEST);
}
return this.telemetryService.getRecentDrivers(latNum, lngNum, radius ? Number(radius) : 5000);
}
@Get('driver/:driverId/profile')
@ApiOperation({ summary: 'Get 3D elevation profile, climb, and terrain grade for a driver 📈' })
@ApiParam({ name: 'driverId', required: true, description: 'Driver unique ID' })
@ApiQuery({ name: 'hours', required: false, description: 'Window in hours (default: 24)' })
async getDriverElevationProfile(
@Param('driverId') driverId: string,
@Query('hours') hours?: number,
) {
return this.telemetryService.getElevationProfile(driverId, hours ? Number(hours) : 24);
}
}