feat(seo): GEO paketi — yapılandırılmış metadata, AI bot erişimi, veri tazelik sinyali
- sayfaMetadata(): indekslenebilir sayfalar için tek kaynaklı canonical + eksiksiz openGraph üretimi; sayfalar buna geçirildi - robots.ts: yalnızca /api/ engellenir; noindex'li sayfalar robots engeli yerine meta ile yönetilir, AI botları niyeti belgeleyen açık grupla izinli - next.config: htmlLimitedBots'a AI botları eklendi (JS çalıştırmayan botlara metadata head içinde bloklanarak verilir) - sitemap lastmod + katalogda görünür 'Kaynak/son güncelleme' satırı (VeriKaynagi) KATALOG_VERI_TARIHI tek kaynağından - docs/geo/geo-raporu.md: çalışmanın raporu Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
import { Suspense } from "react";
|
||||
import type { Metadata } from "next";
|
||||
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo";
|
||||
import { JsonLd, breadcrumbJsonLd, sayfaMetadata } from "@/lib/seo";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import {
|
||||
BolumKatalogu,
|
||||
BolumKataloguSkeleton,
|
||||
} from "@/features/katalog/components/bolum-listesi";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = sayfaMetadata({
|
||||
title: "Tüm Bölümlerin Taban Puanları ve Sıralamaları 2026",
|
||||
description:
|
||||
"Üniversite bölümlerinin 2025 yerleştirme taban puanları ve başarı sıralamaları: lisans ve önlisans programları kategori kategori, 2026 tercihleri için.",
|
||||
alternates: { canonical: "/bolumler" },
|
||||
};
|
||||
path: "/bolumler",
|
||||
});
|
||||
|
||||
export default function BolumlerPage() {
|
||||
return (
|
||||
|
||||
@@ -3,13 +3,14 @@ import Link from "next/link";
|
||||
import { Parallax } from "@/components/parallax";
|
||||
import { PagePixelDivider } from "@/components/pixel-decor";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import { sayfaMetadata } from "@/lib/seo";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = sayfaMetadata({
|
||||
title: "Gizlilik ve KVKK",
|
||||
description:
|
||||
"KolayTercih gizlilik politikası ve KVKK aydınlatma metni: hangi veriler toplanır, nasıl işlenir ve haklarınız nelerdir.",
|
||||
alternates: { canonical: "/gizlilik" },
|
||||
};
|
||||
path: "/gizlilik",
|
||||
});
|
||||
|
||||
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
|
||||
export default function GizlilikPage() {
|
||||
|
||||
@@ -3,13 +3,14 @@ import { Mail } from "lucide-react";
|
||||
import { Parallax } from "@/components/parallax";
|
||||
import { PagePixelDivider } from "@/components/pixel-decor";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import { sayfaMetadata } from "@/lib/seo";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = sayfaMetadata({
|
||||
title: "İletişim",
|
||||
description:
|
||||
"KolayTercih destek ekibine ulaş: sorular, geri bildirim ve iade talepleri için iletişim kanalları.",
|
||||
alternates: { canonical: "/iletisim" },
|
||||
};
|
||||
path: "/iletisim",
|
||||
});
|
||||
|
||||
export default function IletisimPage() {
|
||||
return (
|
||||
|
||||
@@ -3,13 +3,14 @@ import Link from "next/link";
|
||||
import { Parallax } from "@/components/parallax";
|
||||
import { PagePixelDivider } from "@/components/pixel-decor";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import { sayfaMetadata } from "@/lib/seo";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = sayfaMetadata({
|
||||
title: "Kullanım ve Satış Koşulları",
|
||||
description:
|
||||
"KolayTercih kullanım ve satış koşulları: hizmet kapsamı, ödeme, iade politikası ve sorumluluk sınırları.",
|
||||
alternates: { canonical: "/kosullar" },
|
||||
};
|
||||
path: "/kosullar",
|
||||
});
|
||||
|
||||
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
|
||||
export default function KosullarPage() {
|
||||
|
||||
@@ -7,13 +7,14 @@ import {
|
||||
MeraklisinaIcerik,
|
||||
MeraklisinaIcerikSkeleton,
|
||||
} from "@/features/pazarlama/components/meraklisina-icerik";
|
||||
import { sayfaMetadata } from "@/lib/seo";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = sayfaMetadata({
|
||||
title: "Meraklısına: KolayTercih nasıl çalışır?",
|
||||
alternates: { canonical: "/meraklisina" },
|
||||
path: "/meraklisina",
|
||||
description:
|
||||
"Sıralamanı girdiğinde arka planda ne oluyor? Dilimler, risk renkleri ve sihirbazın adım adım daralan filtreleri — öğrenci diliyle, sıralamana göre hesaplanan canlı görsellerle.",
|
||||
};
|
||||
});
|
||||
|
||||
export default function MeraklisinaPage() {
|
||||
return (
|
||||
|
||||
@@ -8,13 +8,14 @@ import {
|
||||
PaketSatinAl,
|
||||
PaketSatinAlSkeleton,
|
||||
} from "@/features/odeme/components/paket-satinal";
|
||||
import { sayfaMetadata } from "@/lib/seo";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = sayfaMetadata({
|
||||
title: "Tercih Dönemi Paketi",
|
||||
description:
|
||||
"Tek seferlik Tercih Dönemi Paketi: yapay zekâ destekli kişisel 24 tercihlik liste, risk analizi, liste revizyonları ve soru hakkı. Abonelik yok.",
|
||||
alternates: { canonical: "/paket" },
|
||||
};
|
||||
path: "/paket",
|
||||
});
|
||||
|
||||
export default function PaketPage({
|
||||
searchParams,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Suspense } from "react";
|
||||
import type { Metadata } from "next";
|
||||
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo";
|
||||
import { JsonLd, breadcrumbJsonLd, sayfaMetadata } from "@/lib/seo";
|
||||
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import { PagePixelDivider } from "@/components/pixel-decor";
|
||||
@@ -9,12 +9,12 @@ import {
|
||||
RehberListesiSkeleton,
|
||||
} from "@/features/rehber/components/rehber-listesi";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = sayfaMetadata({
|
||||
title: "Tercih Rehberi 2026 — YKS Tercih Dönemi Yazıları",
|
||||
description:
|
||||
"YKS tercih dönemi rehberi: tercih listesi nasıl yapılır, ölü tercih nedir, ek yerleştirme nasıl işler, sıralama mı puan mı — hepsi öğrenci diliyle.",
|
||||
alternates: { canonical: "/rehber" },
|
||||
};
|
||||
path: "/rehber",
|
||||
});
|
||||
|
||||
export default function RehberIndexPage() {
|
||||
return (
|
||||
|
||||
@@ -1,15 +1,44 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
import { SITE_URL } from "@/lib/seo";
|
||||
|
||||
// Yalnızca /api/ engellenir. /sonuc, /giris, /listem, /odeme/*, /rapor/*
|
||||
// bilinçli olarak ENGELLENMEDİ: bu sayfalar noindex meta taşıyor ve robots.txt
|
||||
// engeli o meta'nın görülmesini imkânsız kılardı (dış link alırlarsa
|
||||
// "robots.txt tarafından engellendi ama dizine eklendi" durumuna düşerlerdi).
|
||||
const DISALLOW = ["/api/"];
|
||||
|
||||
// Arama/cevap motorlarının tarayıcıları. `*` kuralı bunları zaten kapsıyor;
|
||||
// grup, niyeti belgelemek için açıkça yazıldı: AI aramalarında kaynak
|
||||
// gösterilmek istiyoruz. DİKKAT: bir bot kendi adına yazılmış grubu bulursa `*`
|
||||
// grubunu yok sayar — buraya eklenen her bot için DISALLOW tekrar edilmeli.
|
||||
const AI_BOTLARI = [
|
||||
// Arama indeksi + kullanıcı tetiklemeli fetch
|
||||
"OAI-SearchBot",
|
||||
"ChatGPT-User",
|
||||
"Claude-SearchBot",
|
||||
"Claude-User",
|
||||
"PerplexityBot",
|
||||
"Perplexity-User",
|
||||
"DuckAssistBot",
|
||||
"MistralAI-User",
|
||||
"meta-webindexer",
|
||||
"Amzn-SearchBot",
|
||||
// Model eğitimi: aramasız yanıtlarda markanın bilinmesi buradan geçer
|
||||
"GPTBot",
|
||||
"ClaudeBot",
|
||||
"Google-Extended",
|
||||
"Applebot-Extended",
|
||||
"meta-externalagent",
|
||||
"Amazonbot",
|
||||
"CCBot",
|
||||
];
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: {
|
||||
userAgent: "*",
|
||||
allow: "/",
|
||||
// /sonuc bilinçli olarak ENGELLENMEDİ: sayfa noindex meta taşıyor ve
|
||||
// robots.txt engeli o meta'nın görülmesini imkânsız kılardı.
|
||||
disallow: ["/api/", "/listem", "/odeme/", "/rapor/", "/giris"],
|
||||
},
|
||||
rules: [
|
||||
{ userAgent: "*", allow: "/", disallow: DISALLOW },
|
||||
{ userAgent: AI_BOTLARI, allow: "/", disallow: DISALLOW },
|
||||
],
|
||||
sitemap: `${SITE_URL}/sitemap.xml`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
import { SITE_URL } from "@/lib/seo";
|
||||
import { KATALOG_VERI_TARIHI, SITE_URL } from "@/lib/seo";
|
||||
import { getAllBolumSlugs, getAllUniversiteSlugs } from "@/lib/katalog";
|
||||
import { getAllRehberler } from "@/lib/rehber";
|
||||
|
||||
@@ -7,8 +7,18 @@ import { getAllRehberler } from "@/lib/rehber";
|
||||
// /odeme/sonuc, /rapor/yazdir (auth/utility sayfaları).
|
||||
const staticEntries: MetadataRoute.Sitemap = [
|
||||
{ url: `${SITE_URL}/`, priority: 1, changeFrequency: "weekly" },
|
||||
{ url: `${SITE_URL}/bolumler`, priority: 0.8, changeFrequency: "weekly" },
|
||||
{ url: `${SITE_URL}/universiteler`, priority: 0.8, changeFrequency: "weekly" },
|
||||
{
|
||||
url: `${SITE_URL}/bolumler`,
|
||||
lastModified: KATALOG_VERI_TARIHI,
|
||||
priority: 0.8,
|
||||
changeFrequency: "weekly",
|
||||
},
|
||||
{
|
||||
url: `${SITE_URL}/universiteler`,
|
||||
lastModified: KATALOG_VERI_TARIHI,
|
||||
priority: 0.8,
|
||||
changeFrequency: "weekly",
|
||||
},
|
||||
{ url: `${SITE_URL}/rehber`, priority: 0.8, changeFrequency: "weekly" },
|
||||
{ url: `${SITE_URL}/paket`, priority: 0.7, changeFrequency: "monthly" },
|
||||
{ url: `${SITE_URL}/meraklisina`, priority: 0.6, changeFrequency: "monthly" },
|
||||
@@ -20,12 +30,14 @@ const staticEntries: MetadataRoute.Sitemap = [
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const bolumler: MetadataRoute.Sitemap = getAllBolumSlugs().map((slug) => ({
|
||||
url: `${SITE_URL}/bolum/${slug}`,
|
||||
lastModified: KATALOG_VERI_TARIHI,
|
||||
priority: 0.7,
|
||||
changeFrequency: "monthly",
|
||||
}));
|
||||
const universiteler: MetadataRoute.Sitemap = getAllUniversiteSlugs().map(
|
||||
(slug) => ({
|
||||
url: `${SITE_URL}/universite/${slug}`,
|
||||
lastModified: KATALOG_VERI_TARIHI,
|
||||
priority: 0.6,
|
||||
changeFrequency: "monthly",
|
||||
}),
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Suspense } from "react";
|
||||
import type { Metadata } from "next";
|
||||
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo";
|
||||
import { JsonLd, breadcrumbJsonLd, sayfaMetadata } from "@/lib/seo";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
import {
|
||||
UniversiteKatalogu,
|
||||
UniversiteKataloguSkeleton,
|
||||
} from "@/features/katalog/components/universite-listesi";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
export const metadata: Metadata = sayfaMetadata({
|
||||
title: "Üniversitelerin Taban Puanları ve Sıralamaları 2026",
|
||||
description:
|
||||
"Devlet ve vakıf üniversitelerinin 2025 yerleştirme taban puanları, başarı sıralamaları ve kontenjanları — üniversite üniversite, 2026 tercihleri için.",
|
||||
alternates: { canonical: "/universiteler" },
|
||||
};
|
||||
path: "/universiteler",
|
||||
});
|
||||
|
||||
export default function UniversitelerPage() {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user