Files
sase.tr/apps/api/src/oem-suggestions/oem-suggestions.controller.ts
Semih Yesilyurt 6c414f9a59 feat(gamification): Parça Uzmanları programı feature-flag + kullanım kriteri kapısı
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>
2026-06-12 11:04:20 +03:00

36 lines
1.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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) };
}
}