src/ai/ai.controller.ts
ai
Intelligence Center endpoints.
Guard chain (order matters):
Throttling: every endpoint has a tight per-IP cap because each call can incur real third-party LLM costs. The global throttler is a much looser fallback.
Methods |
|
| Async detectAnomalies | ||||||
detectAnomalies(orgId: string)
|
||||||
Decorators :
@Get('anomalies')
|
||||||
|
Defined in src/ai/ai.controller.ts:87
|
||||||
|
Parameters :
Returns :
unknown
|
| Async getEta |
getEta(orgId: string, tripId: string)
|
Decorators :
@Get('eta/:tripId')
|
|
Defined in src/ai/ai.controller.ts:68
|
|
Returns :
unknown
|
| Async getUsage |
getUsage(orgId: string, days?: string)
|
Decorators :
@Get('usage')
|
|
Defined in src/ai/ai.controller.ts:116
|
|
Returns :
unknown
|
| Async ocrDocument | |||||||||||||||
ocrDocument(orgId: string, userId: string, file: Express.Multer.File, req: Request)
|
|||||||||||||||
Decorators :
@Post('ocr')
|
|||||||||||||||
|
Defined in src/ai/ai.controller.ts:101
|
|||||||||||||||
|
Parameters :
Returns :
unknown
|
| Async optimizeRoutes | ||||||
optimizeRoutes(orgId: string)
|
||||||
Decorators :
@Get('optimize-routes')
|
||||||
|
Defined in src/ai/ai.controller.ts:79
|
||||||
|
Parameters :
Returns :
unknown
|
| Async query |
query(orgId: string, userId: string, dto: QueryDto, req: Request)
|
Decorators :
@Post('query')
|
|
Defined in src/ai/ai.controller.ts:54
|
|
Returns :
unknown
|
import {
BadRequestException,
Body,
Controller,
Get,
Param,
Post,
Query,
Req,
UploadedFile,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import { Request } from 'express';
import { FileInterceptor } from '@nestjs/platform-express';
import { Throttle } from '@nestjs/throttler';
import {
ApiBearerAuth,
ApiConsumes,
ApiOperation,
ApiTags,
} from '@nestjs/swagger';
import { AiService } from './ai.service';
import { AiEnabledGuard } from './ai-enabled.guard';
import { CombinedAuthGuard } from '../auth/guards/combined-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { QueryDto } from './dto/query.dto';
/**
* Intelligence Center endpoints.
*
* Guard chain (order matters):
* 1. CombinedAuthGuard — JWT or API key, populates request.user
* 2. RolesGuard — enforces @Roles() decorators
* 3. AiEnabledGuard — blocks all calls when org disabled the toggle
*
* Throttling: every endpoint has a tight per-IP cap because each call
* can incur real third-party LLM costs. The global throttler is a much
* looser fallback.
*/
@ApiTags('Intelligence')
@ApiBearerAuth()
@UseGuards(CombinedAuthGuard, RolesGuard, AiEnabledGuard)
@Controller('ai')
export class AiController {
constructor(private readonly aiService: AiService) {}
// ── Natural language query ───────────────────────────────────────────
@Post('query')
@Throttle({ default: { limit: 20, ttl: 60_000 } }) // 20/min/IP
@ApiOperation({ summary: 'Natural-language question about fleet data' })
async query(
@CurrentUser('organizationId') orgId: string,
@CurrentUser('id') userId: string,
@Body() dto: QueryDto,
@Req() req: Request,
) {
const ip = (req.headers['x-forwarded-for'] as string) || req.ip || 'unknown';
return this.aiService.naturalLanguageQuery(orgId, userId, dto.question, ip);
}
// ── Predictive ETA ───────────────────────────────────────────────────
@Get('eta/:tripId')
@Throttle({ default: { limit: 60, ttl: 60_000 } }) // 60/min/IP
@ApiOperation({ summary: 'Predictive ETA for a trip (org-scoped)' })
async getEta(
@CurrentUser('organizationId') orgId: string,
@Param('tripId') tripId: string,
) {
return this.aiService.predictEta(orgId, tripId);
}
// ── Route optimizer ──────────────────────────────────────────────────
@Get('optimize-routes')
@Throttle({ default: { limit: 30, ttl: 60_000 } })
@ApiOperation({ summary: 'Smart route consolidation suggestions' })
async optimizeRoutes(@CurrentUser('organizationId') orgId: string) {
return this.aiService.optimizeRoutes(orgId);
}
// ── Anomaly detection ────────────────────────────────────────────────
@Get('anomalies')
@Throttle({ default: { limit: 60, ttl: 60_000 } })
@ApiOperation({ summary: 'Heuristic anomaly scan over recent operations' })
async detectAnomalies(@CurrentUser('organizationId') orgId: string) {
return this.aiService.detectAnomalies(orgId);
}
// ── Document Intelligence (OCR) ──────────────────────────────────────
@Post('ocr')
@Throttle({ default: { limit: 10, ttl: 60_000 } }) // 10/min — vision is expensive
@UseInterceptors(
FileInterceptor('file', {
limits: { fileSize: 10 * 1024 * 1024, files: 1 },
}),
)
@ApiConsumes('multipart/form-data')
@ApiOperation({ summary: 'Extract structured data from a document image' })
async ocrDocument(
@CurrentUser('organizationId') orgId: string,
@CurrentUser('id') userId: string,
@UploadedFile() file: Express.Multer.File,
@Req() req: Request,
) {
if (!file) throw new BadRequestException('No file uploaded');
const ip = (req.headers['x-forwarded-for'] as string) || req.ip || 'unknown';
return this.aiService.extractFromDocument(orgId, userId, file, ip);
}
// ── Usage stats (admin only) ─────────────────────────────────────────
@Get('usage')
@Roles('SUPER_ADMIN', 'ADMIN')
@ApiOperation({ summary: 'Intelligence Center usage + cost report (admin)' })
async getUsage(
@CurrentUser('organizationId') orgId: string,
@Query('days') days?: string,
) {
const window = Math.min(Math.max(parseInt(days || '30', 10) || 30, 1), 365);
return this.aiService.getUsageStats(orgId, window);
}
}