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:
bilalgursen
2026-09-21 00:51:16 +03:00
parent d2651eadb9
commit 53eb07bb34
18 changed files with 718 additions and 73 deletions

View File

@@ -1,3 +1,4 @@
import type { Metadata } from "next";
import { URUNLER } from "./credits";
/**
@@ -14,6 +15,62 @@ export const SITE_URL =
export const SITE_NAME = "KolayTercih";
/**
* Katalog verisinin (data/yokatlas.db) en son yenilendiği gün. Sitemap lastmod,
* katalog sayfalarındaki görünür "son güncelleme" satırı buradan okunur.
* Yalnızca veri GERÇEKTEN değiştiğinde (ingest/refresh sonrası) elle güncelle;
* build zamanına bağlama — her deploy'da değişen lastmod sinyali değersizleşir.
*/
export const KATALOG_VERI_TARIHI = "2026-08-09";
type SayfaMetadataGirdi = {
title: string;
description: string;
/** Kanonik yol ("/bolumler"); metadataBase ile mutlak adrese çözülür. */
path: string;
/** Varsayılan "website"; rehber yazıları article alanlarını buradan verir. */
openGraph?: NonNullable<Metadata["openGraph"]>;
};
/**
* İndekslenebilir sayfaların metadata'sı için tek kaynak. Next, segmentler
* arası metadata'yı SIĞ birleştirir: sayfa `openGraph` vermezse kök layout'unki
* (ana sayfanın başlığı + og:url'i) olduğu gibi miras kalır; verirse nesnenin
* tamamı ezilir (siteName/locale kaybolur). Bu yüzden openGraph burada eksiksiz
* üretilir. twitter:title/description/image, openGraph'tan otomatik dolar.
*/
export function sayfaMetadata({
title,
description,
path,
openGraph,
}: SayfaMetadataGirdi): Metadata {
return {
title,
description,
alternates: { canonical: path },
openGraph: {
type: "website",
locale: "tr_TR",
siteName: SITE_NAME,
url: path,
title,
description,
...openGraph,
},
};
}
/**
* Üst segmentin dosya-tabanlı OG görselini (opengraph-image.tsx) alt rotaya
* elle bağlar. Gerekçe: alt rota `openGraph` tanımlayınca sığ birleştirme üst
* segmentten gelen `images`'ı da düşürür (ör. /bolum/[slug]/sayfa/[no]).
* Boyut, src/lib/og-sayfa.tsx içindeki OG_SIZE ile aynı olmalı.
*/
export function ustSegmentOgGorseli(path: string, alt: string) {
return [{ url: `${path}/opengraph-image`, width: 1200, height: 630, alt }];
}
type JsonLdData = Record<string, unknown>;
export function organizationJsonLd(): JsonLdData {