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
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:
92
scripts/indexnow.ts
Normal file
92
scripts/indexnow.ts
Normal 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);
|
||||
});
|
||||
Reference in New Issue
Block a user