feat(S2-a): ingest durable app MQTT points securely
This commit is contained in:
189
backend/src/ingest/dto/app-messages.dto.ts
Normal file
189
backend/src/ingest/dto/app-messages.dto.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { LocateMode, UtilityType } from '@prisma/client';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
ArrayMaxSize,
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsDateString,
|
||||
IsEnum,
|
||||
IsIn,
|
||||
IsInt,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
Max,
|
||||
MaxLength,
|
||||
Min,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
export const APP_FIX_TYPES = [
|
||||
'AUTONOMOUS',
|
||||
'DGPS',
|
||||
'FLOAT',
|
||||
'FIXED',
|
||||
'NO_FIX',
|
||||
] as const;
|
||||
export type AppFixType = (typeof APP_FIX_TYPES)[number];
|
||||
|
||||
// Frozen app MQTT wire profile v1 from meta/contracts/telemetry-schema.md. This is deliberately
|
||||
// separate from MqttPointDto: the app wire uses the normative fix tokens FLOAT/FIXED/NO_FIX,
|
||||
// while the legacy device path uses Prisma's FLOAT_RTK/FIXED_RTK/NONE storage tokens.
|
||||
export class AppLogPointDto {
|
||||
@ApiProperty({
|
||||
format: 'uuid',
|
||||
description: 'Client-generated UUIDv7 idempotency key.',
|
||||
})
|
||||
@IsUUID()
|
||||
pointId: string;
|
||||
|
||||
@ApiProperty({ format: 'date-time' })
|
||||
@IsDateString()
|
||||
createdAt: string;
|
||||
|
||||
@IsIn(['APP'])
|
||||
origin: 'APP';
|
||||
|
||||
@IsIn(['APP_MQTT'])
|
||||
uploadPath: 'APP_MQTT';
|
||||
|
||||
@IsNumber()
|
||||
@Min(-90)
|
||||
@Max(90)
|
||||
lat: number;
|
||||
|
||||
@IsNumber()
|
||||
@Min(-180)
|
||||
@Max(180)
|
||||
lng: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
alt?: number;
|
||||
|
||||
@IsDateString()
|
||||
ts: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(APP_FIX_TYPES)
|
||||
fix?: AppFixType;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
hAcc?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
vAcc?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
sats?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
hdop?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
depth?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
freqHz?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
currentMa?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
signalDb?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
gainDb?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(LocateMode)
|
||||
mode?: LocateMode;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
phaseDeg?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@Max(360)
|
||||
compassDeg?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@Max(100)
|
||||
distortionPct?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsEnum(UtilityType)
|
||||
utility?: UtilityType;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
seq?: number;
|
||||
|
||||
@IsIn(['IN_SPEC', 'OUT_OF_SPEC', 'MANUAL', 'PPK_CORRECTED', 'WAIVED', 'NONCOMPLIANT'])
|
||||
qualityFlag: 'IN_SPEC' | 'OUT_OF_SPEC' | 'MANUAL' | 'PPK_CORRECTED' | 'WAIVED' | 'NONCOMPLIANT';
|
||||
}
|
||||
|
||||
export class AppLogPointsMessageDto {
|
||||
@ApiPropertyOptional({ description: 'MQTT app-log wire profile version.' })
|
||||
@IsString()
|
||||
@MaxLength(16)
|
||||
schemaVersion: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Job id; exactly one of jobId or ticket is required.',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
jobId?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'Ticket number; exactly one of jobId or ticket is required.',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(64)
|
||||
ticket?: string;
|
||||
|
||||
@ApiProperty({ type: [AppLogPointDto] })
|
||||
@IsArray()
|
||||
@ArrayMinSize(1)
|
||||
@ArrayMaxSize(500)
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AppLogPointDto)
|
||||
points: AppLogPointDto[];
|
||||
}
|
||||
|
||||
export type AppLogAckOutcome = 'ACCEPTED' | 'DUPLICATE' | 'REJECTED';
|
||||
|
||||
export interface AppLogAckResult {
|
||||
pointId: string;
|
||||
outcome: AppLogAckOutcome;
|
||||
reasonCode?: string;
|
||||
}
|
||||
|
||||
export interface AppLogAck {
|
||||
schemaVersion: string;
|
||||
results: AppLogAckResult[];
|
||||
}
|
||||
Reference in New Issue
Block a user