diff --git a/data/app.db b/data/app.db index 64ac060..e280c71 100644 Binary files a/data/app.db and b/data/app.db differ diff --git a/src/app/api/soru/route.ts b/src/app/api/soru/route.ts index 0422e29..35e41b2 100644 --- a/src/app/api/soru/route.ts +++ b/src/app/api/soru/route.ts @@ -8,6 +8,7 @@ import { krediBittiIsaretle, } from "@/lib/credits"; import { anahtarVar, SORUMLULUK_REDDI } from "@/lib/ai/client"; +import { kullanicininRaporu } from "@/lib/rapor-kaydi"; import { sohbetAkisi } from "@/lib/ai/cagri"; import { listeOzetiCikar, type RaporSonuc } from "@/lib/ai/rapor"; import type { RaporParams } from "@/lib/rapor-havuzu"; @@ -122,10 +123,8 @@ export async function POST(request: NextRequest) { return Response.json({ error: "Geçersiz mesaj." }, { status: 400 }); } - // Soru sormak için önce liste oluşturulmuş olmalı - const rapor = await appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, session.user.id), - }); + // Soru sormak için önce liste oluşturulmuş olmalı (bayat dev mock sayılmaz) + const rapor = await kullanicininRaporu(session.user.id); if (!rapor?.result) { return Response.json( { error: "Önce listeni oluşturmalısın.", code: "NO_REPORT" }, diff --git a/src/app/listem/page.tsx b/src/app/listem/page.tsx index a49a1a9..11c20cb 100644 --- a/src/app/listem/page.tsx +++ b/src/app/listem/page.tsx @@ -4,6 +4,7 @@ import { redirect } from "next/navigation"; import { MessageCircleQuestion, Sparkles } from "lucide-react"; import { verifySession, getCurrentUser } from "@/lib/session"; import { appDb, schema } from "@/lib/appdb"; +import { kullanicininRaporu } from "@/lib/rapor-kaydi"; import { URUNLER, MAX_REVIZYON } from "@/lib/credits"; import type { RaporSonuc } from "@/lib/ai/rapor"; import type { RaporParams } from "@/lib/rapor-havuzu"; @@ -44,9 +45,7 @@ export default async function ListemPage({ const [user, params, satir, sohbetGecmisi] = await Promise.all([ getCurrentUser(), searchParams, - appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, session.user.id), - }), + kullanicininRaporu(session.user.id), appDb .select({ id: schema.chatMessages.id, diff --git a/src/app/odeme/sonuc/page.tsx b/src/app/odeme/sonuc/page.tsx index 9d97762..0fad9b3 100644 --- a/src/app/odeme/sonuc/page.tsx +++ b/src/app/odeme/sonuc/page.tsx @@ -4,6 +4,7 @@ import { eq } from "drizzle-orm"; import { ArrowRight, CheckCircle2, XCircle } from "lucide-react"; import { verifySession } from "@/lib/session"; import { appDb, schema } from "@/lib/appdb"; +import { kullanicininRaporu } from "@/lib/rapor-kaydi"; import { odemeyiSonuclandir } from "@/lib/odeme"; import { Button } from "@/components/ui/button"; import { PagePixelDivider } from "@/components/pixel-decor"; @@ -48,12 +49,7 @@ export default async function OdemeSonucPage({ // Rapor zaten üretildiyse ödül bir tık uzakta: kullanıcıyı doğrudan // rapor sayfasına döndür ki blur çözülme animasyonuyla tam listeyi görsün. - const rapor = basarili - ? await appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, session.user.id), - columns: { id: true }, - }) - : null; + const rapor = basarili ? await kullanicininRaporu(session.user.id) : null; const listeHref = rapor ? "/listem?acildi=1" : null; return ( diff --git a/src/app/rapor/yazdir/page.tsx b/src/app/rapor/yazdir/page.tsx index 0c026e8..d931291 100644 --- a/src/app/rapor/yazdir/page.tsx +++ b/src/app/rapor/yazdir/page.tsx @@ -1,9 +1,8 @@ import type { Metadata } from "next"; -import { eq } from "drizzle-orm"; import Image from "next/image"; import { redirect } from "next/navigation"; import { verifySession, getCurrentUser } from "@/lib/session"; -import { appDb, schema } from "@/lib/appdb"; +import { kullanicininRaporu } from "@/lib/rapor-kaydi"; import { PUAN_TURLERI } from "@/lib/db"; import type { RaporSonuc } from "@/lib/ai/rapor"; import type { RaporParams } from "@/lib/rapor-havuzu"; @@ -25,9 +24,7 @@ export default async function YazdirPage() { // Kullanıcı satırı ile rapor sorgusu bağımsız — paralel çöz const [user, satir] = await Promise.all([ getCurrentUser(), - appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, session.user.id), - }), + kullanicininRaporu(session.user.id), ]); if (!user) redirect("/giris"); if (!user.hasPaket) redirect("/paket"); diff --git a/src/app/sonuc/actions.ts b/src/app/sonuc/actions.ts index f978cc3..6004e9c 100644 --- a/src/app/sonuc/actions.ts +++ b/src/app/sonuc/actions.ts @@ -3,6 +3,7 @@ import { and, eq, sql } from "drizzle-orm"; import { getSession, getCurrentUser } from "@/lib/session"; import { appDb, schema } from "@/lib/appdb"; +import { kullanicininRaporu } from "@/lib/rapor-kaydi"; import { PUAN_TURLERI, type PuanTuruKey } from "@/lib/db"; import { spendCredits, @@ -145,9 +146,7 @@ export async function listeOlustur(input: { "Önceki denemen başarısız olduğu için kredin iade edilmişti. Tekrar dener misin?", }; } - const mevcut = await appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, user.id), - }); + const mevcut = await kullanicininRaporu(user.id); if (mevcut?.result) { const rapor = mevcut.result as RaporSonuc; return { @@ -225,9 +224,7 @@ export async function listeRevize(input: { return { ok: false, code: "HATA", error: "Geçersiz istek." }; } - const rapor = await appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, user.id), - }); + const rapor = await kullanicininRaporu(user.id); if (!rapor?.result) { return { ok: false, code: "HATA", error: "Önce bir liste oluşturmalısın." }; } @@ -274,9 +271,7 @@ export async function listeRevize(input: { "Önceki denemen başarısız olduğu için kredin iade edilmişti. Tekrar dener misin?", }; } - const guncel = await appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, user.id), - }); + const guncel = await kullanicininRaporu(user.id); if (guncel?.result) { return { ok: true, diff --git a/src/app/sonuc/page.tsx b/src/app/sonuc/page.tsx index 02c2ca7..19967fc 100644 --- a/src/app/sonuc/page.tsx +++ b/src/app/sonuc/page.tsx @@ -1,5 +1,4 @@ import type { Metadata } from "next"; -import { eq } from "drizzle-orm"; import { PUAN_TURLERI, rankWindowFacets, @@ -7,7 +6,7 @@ import { type PuanTuruKey, } from "@/lib/db"; import { getSession } from "@/lib/session"; -import { appDb, schema } from "@/lib/appdb"; +import { kullanicininRaporu } from "@/lib/rapor-kaydi"; import { tadimlikSec } from "@/lib/tadimlik-havuzu"; import { SihirbazBolumu } from "./sihirbaz-cagri-karti"; import { TadimlikSatiri } from "./tadimlik-satiri"; @@ -55,10 +54,7 @@ export default async function SonucPage({ // için varlık bilgisi gerekir. const session = await getSession(); const raporSatiri = session - ? await appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, session.user.id), - columns: { result: true }, - }) + ? await kullanicininRaporu(session.user.id) : null; const facetler = rankWindowFacets(sira, turKey); diff --git a/src/lib/ai/rapor.ts b/src/lib/ai/rapor.ts index cb15fe7..23e53c1 100644 --- a/src/lib/ai/rapor.ts +++ b/src/lib/ai/rapor.ts @@ -78,6 +78,9 @@ export type RaporKapsam = { export type RaporSonuc = Omit, "uyarilar"> & { uyarilar?: string[]; kapsam?: RaporKapsam; + // Dev mock üretimi işareti: gerçek AI aktifken bayat mock kayıtları + // ayıklamak için (bkz. src/lib/rapor-kaydi.ts) + devMock?: boolean; // Render için havuzdan zenginleştirilmiş program bilgisi // efektifSira: COALESCE(sira2025, sira2024) — eski kayıtlı raporlarda yok // siraGecmisi: [2021..2025] taban sıralamaları — deterministik trend oku @@ -284,6 +287,7 @@ function mockRapor( ], programlar, kapsam: kapsamUret(hedef), + devMock: true, }; } diff --git a/src/lib/dev/actions.ts b/src/lib/dev/actions.ts index 9178183..1bd3608 100644 --- a/src/lib/dev/actions.ts +++ b/src/lib/dev/actions.ts @@ -7,6 +7,7 @@ import { eq } from "drizzle-orm"; import { headers } from "next/headers"; import { appDb, schema } from "@/lib/appdb"; +import { kullanicininRaporu } from "@/lib/rapor-kaydi"; import { auth } from "@/lib/auth"; import { getCurrentUser } from "@/lib/session"; import { URUNLER } from "@/lib/credits"; @@ -33,10 +34,7 @@ export async function devDurum(): Promise { devKontrol(); const user = await getCurrentUser(); if (!user) return { girisli: false }; - const rapor = await appDb.query.reports.findFirst({ - where: eq(schema.reports.userId, user.id), - columns: { params: true }, - }); + const rapor = await kullanicininRaporu(user.id); const params = rapor?.params as { sira?: number; tur?: string } | undefined; return { girisli: true, diff --git a/src/lib/rapor-kaydi.ts b/src/lib/rapor-kaydi.ts new file mode 100644 index 0000000..12a856e --- /dev/null +++ b/src/lib/rapor-kaydi.ts @@ -0,0 +1,34 @@ +import { eq } from "drizzle-orm"; +import { appDb, schema } from "@/lib/appdb"; +import { anahtarVar } from "@/lib/ai/client"; +import type { RaporSonuc } from "@/lib/ai/rapor"; + +// Kullanıcının kayıtlı raporunu okuyan TEK kapı. Rapor tablosunu okuyan her +// yer (sayfalar, action'lar, soru API'si) buradan geçmeli; doğrudan +// appDb.query.reports.findFirst kullanma. +// +// Neden: dev'de API anahtarı yokken üretilen mock rapor DB'ye kaydediliyor +// (akışın tamamı anahtarsız test edilebilsin diye — bilinçli). Ama anahtar +// sonradan eklenince kullanıcı başına tek rapor kuralı yüzünden o bayat mock, +// gerçek Yapay Zeka çıktısıymış gibi servis edilmeye devam ediyordu. Gerçek AI +// aktifken mock üretimi bir rapor "yok" sayılır; kullanıcı yeni (gerçek) +// listeyi üretir ve kayıt üzerine yazılır. + +function bayatMockMu(sonuc: RaporSonuc): boolean { + const mockUretimi = + sonuc.devMock === true || + // devMock alanı eklenmeden önce kaydedilmiş mock raporlar + sonuc.genelDegerlendirme?.startsWith("(Dev mock"); + if (!mockUretimi) return false; + const mockHalaAktif = + process.env.NODE_ENV !== "production" && !anahtarVar(); + return !mockHalaAktif; +} + +export async function kullanicininRaporu(userId: string) { + const satir = await appDb.query.reports.findFirst({ + where: eq(schema.reports.userId, userId), + }); + if (satir?.result && bayatMockMu(satir.result as RaporSonuc)) return null; + return satir ?? null; +}