Program (oylama + muadil önerisi + liderlik) artık iki katmanlı kapının arkasında: PostHog "oem-expert-program" ana şalteri (local-eval, fail-open) VE kullanım kriteri — en az 2 FARKLI başarılı VIN decode VE en az 2 FARKLI OEM kodu kopyası. Karar sunucuda tek noktada (ExpertAccessService.check); web GET /oem-votes/access ile bir kez sorar, kriterler istemciye sızmaz. Kapalıyken: sidebar nav linki görünmez, OEM detayındaki oy kartı + öneri bölümü render edilmez, /dashboard/uzmanlar kademeli-açılış mesajı gösterir ve liderlik sorgusu atılmaz. Yazma uçları (oy, öneri) sunucu tarafında da ForbiddenException ile korunur — UI gizlemek tek başına güven sınırı değil. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
import { BadRequestException, Body, Controller, Get, Post, Query } from "@nestjs/common";
|
||
import { Throttle } from "@nestjs/throttler";
|
||
import { CurrentUser } from "../common/decorators/current-user.decorator";
|
||
import { ExpertAccessService } from "../oem-votes/expert-access.service";
|
||
import { createOemSuggestionSchema } from "./oem-suggestions.dto";
|
||
import { OemSuggestionsService } from "./oem-suggestions.service";
|
||
|
||
@Controller("oem-suggestions")
|
||
export class OemSuggestionsController {
|
||
constructor(
|
||
private readonly oemSuggestionsService: OemSuggestionsService,
|
||
private readonly expertAccess: ExpertAccessService,
|
||
) {}
|
||
|
||
// Spam koruması: dakikada en fazla 10 öneri.
|
||
@Post()
|
||
@Throttle({ default: { limit: 10, ttl: 60_000 } })
|
||
async create(@CurrentUser("id") userId: string, @Body() body: unknown) {
|
||
await this.expertAccess.assert(userId);
|
||
const parsed = createOemSuggestionSchema.safeParse(body);
|
||
if (!parsed.success) {
|
||
throw new BadRequestException(parsed.error.issues[0]?.message ?? "Geçersiz öneri");
|
||
}
|
||
return this.oemSuggestionsService.create(userId, parsed.data);
|
||
}
|
||
|
||
@Get()
|
||
async list(@CurrentUser("id") userId: string, @Query("code") code: string) {
|
||
const oemCode = (code ?? "").trim();
|
||
if (oemCode.length < 2 || oemCode.length > 100) {
|
||
throw new BadRequestException("OEM kodu geçersiz");
|
||
}
|
||
return { suggestions: await this.oemSuggestionsService.list(userId, oemCode) };
|
||
}
|
||
}
|