feat(geo): llms.txt, RSS, IndexNow, Article yazar/görsel, bölüm sayfasına veri özeti, vakıf bursu yazısı
All checks were successful
Deploy / deploy (push) Successful in 6m16s

GEO araştırması (docs/arastirma/2026-09-24-geo-seo) Dalga 1-2 uygulaması:
- /llms.txt ve /feed.xml route'ları (getAllRehberler'den otomatik), head'de RSS linki
- Article JSON-LD: author, image, mainEntityOfPage, publisher logo; görünür 'Yazan' satırı
- robots: ClaudeBot ayrı grupta Crawl-delay 1
- scripts/indexnow.ts (pnpm indexnow) + public anahtar dosyası
- bölüm sayfası açılışı veriden üretilen özgü cümleler (il/program/tür/kontenjan, taban aralığı, yıllık yön)
- ana sayfa fiyat iddiasına kaynak; iki yazıda 23.000 -> 21.000+; beş yazıya 'Kısa cevap' açılışı
- yeni yazı: vakif-universitesi-bursu-kesilir-mi (ÖSYM 2026 kılavuzu + YÖK ücret esasları)
- YAYIN-KUYRUGU: IndexNow adımı ve aylık AI görünürlük rutini

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-24 17:42:29 +03:00
parent 6094066c5e
commit 24d4bea32c
24 changed files with 1202 additions and 11 deletions

92
scripts/indexnow.ts Normal file
View File

@@ -0,0 +1,92 @@
/**
* IndexNow bildirimi (Bing + Yandex; Google desteklemiyor).
*
* Anahtar dosyası: public/<anahtar>.txt — canlıda https://kolaytercih.com/<anahtar>.txt
* olarak sunulur; IndexNow her bildirimde bu dosyayı okuyup anahtarı doğrular.
* Anahtar gizli değildir (dosya zaten herkese açık), ama değiştirilirse
* public/ altındaki dosya da birlikte değişmeli.
*
* Kullanım (canlıda 200 dönen adresler için, yayından SONRA):
* pnpm indexnow https://kolaytercih.com/rehber/<slug> [...]
* pnpm indexnow --rehber # content/rehber altındaki tüm yazılar
* pnpm indexnow --sitemap # canlı sitemap.xml'deki tüm adresler (10.000'e kadar)
*
* Bing Webmaster → IndexNow sekmesinde alınan bildirimler görünür; Yandex
* Webmaster'da "Indexing → IndexNow" altında. Aynı adres günde bir kez yeter.
*/
import fs from "node:fs";
import path from "node:path";
const HOST = "kolaytercih.com";
const SITE = `https://${HOST}`;
const UC = "https://api.indexnow.org/indexnow";
function anahtarOku(): string {
const dizin = path.join(process.cwd(), "public");
const dosya = fs
.readdirSync(dizin)
.find((f) => /^[0-9a-f]{32}\.txt$/.test(f));
if (!dosya) {
throw new Error("indexnow: public/ altında <32 hex>.txt anahtar dosyası yok");
}
const anahtar = fs.readFileSync(path.join(dizin, dosya), "utf8").trim();
if (anahtar !== dosya.replace(/\.txt$/, "")) {
throw new Error(`indexnow: ${dosya} içeriği dosya adıyla eşleşmiyor`);
}
return anahtar;
}
function rehberAdresleri(): string[] {
const dizin = path.join(process.cwd(), "content", "rehber");
return fs
.readdirSync(dizin)
.filter((f) => f.endsWith(".md"))
.map((f) => `${SITE}/rehber/${f.replace(/\.md$/, "")}`);
}
async function sitemapAdresleri(): Promise<string[]> {
const res = await fetch(`${SITE}/sitemap.xml`);
if (!res.ok) throw new Error(`indexnow: sitemap ${res.status}`);
const xml = await res.text();
return [...xml.matchAll(/<loc>([^<]+)<\/loc>/g)].map((m) => m[1].trim());
}
async function main() {
const args = process.argv.slice(2);
let adresler: string[];
if (args.includes("--rehber")) adresler = rehberAdresleri();
else if (args.includes("--sitemap")) adresler = await sitemapAdresleri();
else adresler = args.filter((a) => a.startsWith("http"));
adresler = [...new Set(adresler)].filter((a) => a.startsWith(SITE));
if (adresler.length === 0) {
console.error("indexnow: adres yok. Kullanım: pnpm indexnow <url...> | --rehber | --sitemap");
process.exit(1);
}
if (adresler.length > 10_000) {
throw new Error("indexnow: tek istekte en fazla 10.000 adres");
}
const key = anahtarOku();
const res = await fetch(UC, {
method: "POST",
headers: { "Content-Type": "application/json; charset=utf-8" },
body: JSON.stringify({
host: HOST,
key,
keyLocation: `${SITE}/${key}.txt`,
urlList: adresler,
}),
});
// 200 = işlendi, 202 = alındı (anahtar sonra doğrulanacak), 4xx = hata.
console.log(`indexnow: ${adresler.length} adres → HTTP ${res.status}`);
if (!res.ok && res.status !== 202) {
console.error(await res.text());
process.exit(1);
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});