Sıralaman kontrol ediliyor…
diff --git a/src/lib/odeme.ts b/src/lib/odeme.ts
index 8601acc..8acb67b 100644
--- a/src/lib/odeme.ts
+++ b/src/lib/odeme.ts
@@ -1,4 +1,5 @@
import "server-only";
+import { after } from "next/server";
import { and, eq } from "drizzle-orm";
import { appDb, schema } from "./appdb";
import { grantCredits, URUNLER } from "./credits";
@@ -62,15 +63,19 @@ export async function odemeyiSonuclandir(
// Dönüşüm kaydı. Koşullu geçişin ardında olduğu için sipariş başına tam bir
// kez düşer (self-healing fallback tekrar çağrılsa da rowsAffected 0 döner).
- await sunucuOlayi("odeme_tamamlandi", {
- kullaniciId: order.userId,
- pathname: "/odeme/sonuc",
- ozellikler: {
- urun: order.product,
- kredi: order.credits,
- tutar: order.amountKurus / 100,
- },
- });
+ // after(): yanıt gönderildikten sonra koşar — callback redirect'ini ve
+ // /odeme/sonuc render'ını Rybbit'in 3 sn'lik timeout'u bloklayamaz.
+ after(() =>
+ sunucuOlayi("odeme_tamamlandi", {
+ kullaniciId: order.userId,
+ pathname: "/odeme/sonuc",
+ ozellikler: {
+ urun: order.product,
+ kredi: order.credits,
+ tutar: order.amountKurus / 100,
+ },
+ }),
+ );
return "paid";
}
diff --git a/src/lib/rehber.ts b/src/lib/rehber.ts
index 6bcd6e5..de62431 100644
--- a/src/lib/rehber.ts
+++ b/src/lib/rehber.ts
@@ -25,6 +25,13 @@ export type RehberOzet = {
tarih: string;
};
+/**
+ * Header'daki ⌘K aramasına giden asgari alan seti. `tarih` client'ta hiç
+ * okunmadığı için RSC payload'ına taşınmaz (root layout üzerinden her
+ * sayfaya serileşiyordu).
+ */
+export type RehberAramaKaydi = Pick;
+
export type RehberSema = {
/** Mermaid tanımı (aria/altyazi yönergeleri ayıklanmış hâli) */
tanim: string;
@@ -40,6 +47,16 @@ export type RehberYazi = RehberOzet & { parcalar: RehberParca[] };
const REHBER_DIZIN = path.join(process.cwd(), "content", "rehber");
+// SiteHeader root layout'ta olduğu için getAllRehberler her sayfa
+// render/prerender'ında koşuyor; cache'siz hâli sayfa başına 24 dosya okuma +
+// 24 marked.parse demekti. Yalnızca prod'da cache'lenir — dev'de içerik
+// düzenlenirken taze fs okuması korunur (katalog.ts/db.ts globalThis deseni).
+const globalRehber = globalThis as unknown as {
+ rehberYazilar?: Map;
+ rehberOzetler?: RehberOzet[];
+};
+const CACHE_AKTIF = process.env.NODE_ENV === "production";
+
function parseFrontmatter(raw: string): {
meta: Record;
body: string;
@@ -105,21 +122,62 @@ function parcala(slug: string, body: string): RehberParca[] {
return parcalar;
}
-function oku(slug: string): RehberYazi | null {
- const dosya = path.join(REHBER_DIZIN, `${slug}.md`);
- if (!fs.existsSync(dosya)) return null;
- const { meta, body } = parseFrontmatter(fs.readFileSync(dosya, "utf8"));
+function metaDogrula(
+ slug: string,
+ meta: Record,
+): asserts meta is { baslik: string; aciklama: string; tarih: string } {
if (!meta.baslik || !meta.aciklama || !meta.tarih) {
throw new Error(
`rehber: ${slug}.md frontmatter eksik (baslik/aciklama/tarih zorunlu)`,
);
}
+}
+
+function oku(slug: string): RehberYazi | null {
+ if (CACHE_AKTIF) {
+ const onbellek = globalRehber.rehberYazilar?.get(slug);
+ if (onbellek !== undefined) return onbellek;
+ }
+ const dosya = path.join(REHBER_DIZIN, `${slug}.md`);
+ let yazi: RehberYazi | null = null;
+ if (fs.existsSync(dosya)) {
+ const { meta, body } = parseFrontmatter(fs.readFileSync(dosya, "utf8"));
+ metaDogrula(slug, meta);
+ yazi = {
+ slug,
+ baslik: meta.baslik,
+ aciklama: meta.aciklama,
+ tarih: meta.tarih,
+ parcalar: parcala(slug, body),
+ };
+ }
+ if (CACHE_AKTIF) {
+ (globalRehber.rehberYazilar ??= new Map()).set(slug, yazi);
+ }
+ return yazi;
+}
+
+/**
+ * Liste görünümleri için fast-path: yalnızca frontmatter parse edilir,
+ * `parcala()`/`marked.parse` hiç çağrılmaz (özetler `parcalar`ı kullanmıyor).
+ * Tam parse zaten cache'teyse dosyaya inmeden onu kırpar.
+ */
+function ozetOku(slug: string): RehberOzet | null {
+ const tam = CACHE_AKTIF ? globalRehber.rehberYazilar?.get(slug) : undefined;
+ if (tam !== undefined) {
+ if (tam === null) return null;
+ const { slug: s, baslik, aciklama, tarih } = tam;
+ return { slug: s, baslik, aciklama, tarih };
+ }
+ const dosya = path.join(REHBER_DIZIN, `${slug}.md`);
+ if (!fs.existsSync(dosya)) return null;
+ const { meta } = parseFrontmatter(fs.readFileSync(dosya, "utf8"));
+ metaDogrula(slug, meta);
return {
slug,
baslik: meta.baslik,
aciklama: meta.aciklama,
tarih: meta.tarih,
- parcalar: parcala(slug, body),
};
}
@@ -139,14 +197,22 @@ export function getRehber(slug: string): RehberYazi | null {
}
export function getAllRehberler(): RehberOzet[] {
- return getAllRehberSlugs()
- .map((slug) => oku(slug))
- .filter((y): y is RehberYazi => y !== null)
- .map(({ slug, baslik, aciklama, tarih }) => ({
- slug,
- baslik,
- aciklama,
- tarih,
- }))
+ if (CACHE_AKTIF && globalRehber.rehberOzetler) {
+ return globalRehber.rehberOzetler;
+ }
+ const ozetler = getAllRehberSlugs()
+ .map((slug) => ozetOku(slug))
+ .filter((y): y is RehberOzet => y !== null)
.sort((a, b) => b.tarih.localeCompare(a.tarih));
+ if (CACHE_AKTIF) globalRehber.rehberOzetler = ozetler;
+ return ozetler;
+}
+
+/** Header aramasının RSC payload'ına giden kırpılmış liste. */
+export function getRehberAramaKayitlari(): RehberAramaKaydi[] {
+ return getAllRehberler().map(({ slug, baslik, aciklama }) => ({
+ slug,
+ baslik,
+ aciklama,
+ }));
}
\ No newline at end of file
diff --git a/src/types/yokatlas.ts b/src/types/yokatlas.ts
index f069b14..55b1c9e 100644
--- a/src/types/yokatlas.ts
+++ b/src/types/yokatlas.ts
@@ -41,6 +41,20 @@ export type RankResults = {
toplam: Record;
};
+/**
+ * /sonuc ham tablosunun satırı: tablo `fakulte` ve `puan2025`'i hiç
+ * kullanmadığı için bu alanlar client'a serileştirilmez (satır başına en
+ * uzun string alanlardan biri fakulte). Katalog sayfaları tam Program kullanır.
+ */
+export type ProgramListeSatiri = Omit;
+
+export type RankResultsListe = {
+ hayal: ProgramListeSatiri[];
+ dengeli: ProgramListeSatiri[];
+ garanti: ProgramListeSatiri[];
+ toplam: Record;
+};
+
// Vakıf sayılan unitur değerleri (bkz. DISTINCT unitur: VAKIF, VAKIF MYO,
// YURTDISI VAKIF). Devlet dışı her şey "vakıf" grubuna girer.
export type UniturGrubu = "devlet" | "vakif";