import { Body, Controller, Delete, Get, Param, Post, Query } from "@nestjs/common"; import { CategoriesService } from "../categories/categories.service"; import { CurrentUser } from "../common/decorators/current-user.decorator"; import { Public } from "../common/decorators/public.decorator"; import { VinValidationPipe } from "../common/pipes/vin-validation.pipe"; import { EmailService } from "../email/email.service"; import { VehiclesService } from "./vehicles.service"; @Controller("vehicles") export class VehiclesController { constructor( private vehiclesService: VehiclesService, private categoriesService: CategoriesService, private emailService: EmailService, ) {} @Public() @Get("preview/:vin") async preview(@Param("vin", VinValidationPipe) vin: string) { return this.vehiclesService.previewVin(vin); } /** * Public catalog-stats teaser for /register?vin= — returns categories / * parts / schemas counts so the signup page can frame "what opens after * register". Real data when DB is meaningfully populated; otherwise a * deterministic VIN-seeded placeholder. Source/provider names are never * exposed. */ @Public() @Get(":vin/teaser-stats") async teaserStats(@Param("vin", VinValidationPipe) vin: string) { return this.vehiclesService.getTeaserStats(vin); } @Post("decode") async decode( @CurrentUser("id") userId: string, @Body("vin", VinValidationPipe) vin: string, // Deprecated: provider-specific picks from pre-stash frontend bundles. // New clients send only the opaque `candidate` key; the provider mapping // lives in the server-side stash. @Body("pcatCarId") pcatCarId?: string, @Body("emexCarIndex") emexCarIndex?: number, @Body("candidate") candidate?: string, ) { return this.vehiclesService.decodeVin(vin, userId, pcatCarId, emexCarIndex, candidate); } @Get("history") async history( @CurrentUser("id") userId: string, @Query("page") page?: string, @Query("limit") limit?: string, ) { return this.vehiclesService.getHistory( userId, page ? Number.parseInt(page, 10) : 1, limit ? Number.parseInt(limit, 10) : 20, ); } @Post("report-vin") async reportVin( @CurrentUser("id") userId: string, @CurrentUser("email") userEmail: string, @Body("vin", VinValidationPipe) vin: string, ) { await this.emailService.send({ to: "admin@sase.tr", subject: `Tanınmayan Şase Bildirimi — ${vin}`, html: `
Bir kullanıcı aşağıdaki şase numarasının doğru olduğunu bildirdi:
VIN: ${vin}
Kullanıcı: ${userEmail}
Tarih: ${new Date().toLocaleString("tr-TR", { timeZone: "Europe/Istanbul" })}
`, text: `Tanınmayan şase bildirimi: ${vin} — Kullanıcı: ${userEmail}`, tag: "vin-report", }); return { sent: true }; } @Get(":vehicleId/prefetch-status") async prefetchStatus(@Param("vehicleId") vehicleId: string) { await this.vehiclesService.getById(vehicleId); return this.vehiclesService.getPrefetchStatus(vehicleId); } @Get(":vehicleId/categories/:categoryId") async getCategoryParts( @Param("vehicleId") vehicleId: string, @Param("categoryId") categoryId: string, ) { // Decoded vehicles are shared — any authenticated user can read. await this.vehiclesService.getById(vehicleId); return this.categoriesService.getCategoryWithParts(categoryId); } // On-demand resolution of a "bk. tablo:" cross-reference code whose target // illustration wasn't seeded at category-load time — drills the relevant // main group, then returns the resolved category (or null if not found). @Get(":vehicleId/references/resolve") async resolveReference(@Param("vehicleId") vehicleId: string, @Query("code") code: string) { await this.vehiclesService.getById(vehicleId); return this.categoriesService.resolveReferenceCode(vehicleId, code ?? ""); } @Get(":id") async getById(@Param("id") id: string) { return this.vehiclesService.getById(id); } @Delete(":id") async delete(@Param("id") id: string, @CurrentUser("id") userId: string) { return this.vehiclesService.deleteVehicle(id, userId); } }