From 507bf53a959b7f3cd1beed7bd913e38b599c5ba8 Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Mon, 21 Sep 2026 23:34:52 +0300 Subject: [PATCH] =?UTF-8?q?feat(seo):=20kapanan=2028=20b=C3=B6l=C3=BCm=20+?= =?UTF-8?q?=202=20=C3=BCniversite=20sayfas=C4=B1=20benzer=20sayfaya=20kal?= =?UTF-8?q?=C4=B1c=C4=B1=20y=C3=B6nlendirilir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arşivlenen slug'lar 200 + 'sayfa yok' dönüyordu (soft 404). Eşlemenin tek kaynağı src/lib/kapanan-sluglar.ts; alt yollar (/sayfa/N) hedefin köküne gider; zincir ya da çift kaynak build'i durdurur. Yıllık güncelleme kılavuzuna 5b adımı eklendi. Co-Authored-By: Claude Fable 5.1 --- docs/veri/yillik-guncelleme.md | 22 +++ next.config.ts | 7 + src/lib/kapanan-sluglar.ts | 249 +++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 src/lib/kapanan-sluglar.ts diff --git a/docs/veri/yillik-guncelleme.md b/docs/veri/yillik-guncelleme.md index f0636b2..c62f068 100644 --- a/docs/veri/yillik-guncelleme.md +++ b/docs/veri/yillik-guncelleme.md @@ -130,6 +130,28 @@ Neden gerekli: arşivlenmezse eski kodlu satırlar listelerde bir yıl önceki s görünmeye devam eder; öğrenci o kodu tercih ekranında bulamaz, kodu değişen program iki kez listelenir. +### 5b. Kapanan sayfaları yönlendir (5–15 dk) + +`--kuru` raporundaki `KAPANACAK /bolum/` ve `KAPANACAK /universite/` +listelerindeki her adres (ve `--kimlik-onar` sonrası kapanan sayfa olduysa onlar da) +`src/lib/kapanan-sluglar.ts` içindeki `KAPANAN_SLUGLAR` listesine eklenir; site bu +adresleri yaşayan en yakın sayfaya **kalıcı (308)** yönlendirir (`next.config.ts` → +`redirects()`, `/sayfa/N` alt yolları dahil). Eklemezsen kapanan adres Google'da +"sayfa yok" gövdesiyle kalır. + +- Hedef sırası: (1) program/kurum **ad değiştirdiyse yeni adı** — kanıt: aynı + üniversitede geçmiş yıl sıraları yeni adlı satıra birebir taşınmış, ya da Resmî + Gazete kararı; (2) aynı alanın lisans/önlisans karşılığı; (3) emin değilsen hub: + `/bolumler` ya da `/universiteler`. Uydurma eşleme yapma; `gerekce` alanına kanıtı yaz. +- Hedef slug `programs` tablosundan türemeli ve kendisi listede **kaynak** olmamalı + (zincir). Zinciri dosyadaki `kapananYonlendirmeleri()` build'de yakalar; hedefin + yaşadığını sen kontrol et: 8. adımda hedef adresi tarayıcıda aç. +- Bu yıl **yeniden açılan** bir sayfa listede kaynak olarak duruyorsa kaydını sil — + yönlendirme sayfadan önce çalışır, silmezsen yeni sayfa hiç görünmez. Kontrol: + 8. adımda listedeki `kaynak` adlarını `/bolumler` ve `/universiteler` sayfasında ara; + çıkan varsa kaydını sil. +- Hedef seçimi SEO'yu etkilediği için **[BİLAL] kararıdır**; listeyi commit'ten önce göster. + ## 6. İndeks + dosyayı toparla (1 dk) ```bash diff --git a/next.config.ts b/next.config.ts index 62de3d0..35f5fe0 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,5 +1,8 @@ import type { NextConfig } from "next"; import { HTML_LIMITED_BOT_UA_RE } from "next/dist/shared/lib/router/utils/html-bots"; +// Bağımlılıksız veri dosyası; config uygulama paketinden önce yüklendiği için +// göreli yolla alınır (Docker builder'da src/ build'den önce kopyalanır). +import { kapananYonlendirmeleri } from "./src/lib/kapanan-sluglar"; // Dockerfile builder katmanı BUILD_KAYNAK_KISITLI=1 verir: imaj build'i prod // VPS'te çalışan uygulamayla aynı makinede koştuğu için bellek/paralellik kısılır. @@ -75,6 +78,10 @@ const nextConfig: NextConfig = { { source: "/sohbet", destination: "/listem", permanent: false }, // Ayrı robot sayfası kaldırıldı; rehber içi linkler hero formuna düşer. { source: "/tercih-robotu", destination: "/#hero-form", permanent: true }, + // Veri güncellemesiyle tamamen kapanan /bolum ve /universite sayfaları + // (sayfalama alt yolları dahil) yaşayan en yakın sayfaya kalıcı (308) + // gider. Eşlemenin tek kaynağı src/lib/kapanan-sluglar.ts. + ...kapananYonlendirmeleri(), ]; }, }; diff --git a/src/lib/kapanan-sluglar.ts b/src/lib/kapanan-sluglar.ts new file mode 100644 index 0000000..a57652b --- /dev/null +++ b/src/lib/kapanan-sluglar.ts @@ -0,0 +1,249 @@ +/** + * Kapanan katalog sayfaları → yaşayan en yakın sayfa (kalıcı yönlendirme). + * + * Neden var: yıllık veri güncellemesinde son kılavuzda olmayan program satırları + * `programs_arsiv`'e taşınır; bir bölüm adının ya da üniversitenin bütün + * satırları giderse /bolum/ ya da /universite/ sayfası tamamen + * kapanır. Bu URL'ler arama sonuçlarında ve dış linklerde yaşamaya devam eder; + * "sayfa yok" göstermek yerine adı/alanı en yakın yaşayan sayfaya 308 verilir. + * Tek tüketici `next.config.ts` içindeki `redirects()`'tir. + * + * Yıllık güncellemede (docs/veri/yillik-guncelleme.md, 5. adım): + * `pnpm eski-arsivle --kuru` çıktısındaki "tamamen kapanan" slug'lar buraya + * eklenir. Hedef seçimi sırasıyla: (1) program/kurum ad değiştirdiyse yeni adı + * (kanıt: aynı üniversitede geçmiş yıl sıraları birebir taşınmış satır, ya da + * resmî karar), (2) aynı alanın lisans/önlisans karşılığı, (3) emin + * olunamıyorsa hub (`/bolumler`, `/universiteler`). Uydurma eşleme yapılmaz. + * + * Kurallar: + * - `hedef` yaşayan bir sayfa olmalı (slug `programs` tablosundan türemeli). + * - `hedef` başka bir kaydın `kaynak`'ı olamaz (zincir/döngü) — + * `kapananYonlendirmeleri()` bunu build'de sınar ve ihlâlde durur. + * - Kapanan bir sayfa ileride yeniden açılırsa (program kılavuza dönerse) + * kaydı SİL; yoksa yönlendirme dosya sisteminden önce çalıştığı için yeni + * sayfa hiç görünmez. + * + * Bu dosya `next.config.ts`'ten, uygulama paketinin dışında yüklenir: bağımlılıksız + * kalmalı — `server-only`, DB ya da başka bir uygulama modülü import ETME. + */ + +export type KapananSlug = { + /** Kapanan sayfanın yolu, sonda eğik çizgi yok: `/bolum/` | `/universite/` */ + kaynak: `/bolum/${string}` | `/universite/${string}`; + /** Yaşayan hedef sayfa ya da hub */ + hedef: `/bolum/${string}` | `/universite/${string}` | "/bolumler" | "/universiteler"; + /** Hedefin neden seçildiği (kanıt türüyle) */ + gerekce: string; +}; + +/** 2026 veri yayını (21 Eylül 2026): 28 bölüm + 2 üniversite sayfası kapandı. */ +export const KAPANAN_SLUGLAR: readonly KapananSlug[] = [ + // ---- Bölümler: ad değişikliği veriyle kanıtlı (geçmiş sıralar yeni ada taşınmış) ---- + { + kaynak: "/bolum/radyo-ve-televizyon-programciligi", + hedef: "/bolum/medya-ve-iletisim", + gerekce: + "Önlisans programı 2024'ten sonra kılavuzda yok; İzmir Ekonomi Ü. MYO'da geçmiş sıralar Medya ve İletişim'e taşınmış (ardıl ad), hedefte 21 önlisans programı var", + }, + { + kaynak: "/bolum/iletisim-sanatlari", + hedef: "/bolum/halkla-iliskiler-ve-reklamcilik", + gerekce: + "Tek kurum (İstanbul Kültür Ü. Sanat ve Tasarım Fak.); 3 satırın 2023 sıraları aynı fakültedeki Halkla İlişkiler ve Reklamcılık satırlarına taşınmış", + }, + { + kaynak: "/bolum/cevre-temizligi-ve-denetimi", + hedef: "/bolum/cevresel-olcum-ve-izleme-sistemleri-teknikerligi", + gerekce: + "Tek kurum (Kocaeli Ü. Hereke Asım Kocabıyık MYO); geçmiş sırası aynı MYO'daki yeni adlı programa taşınmış", + }, + { + kaynak: "/bolum/atik-yonetimi", + hedef: "/bolum/su-ve-atik-yonetimi-teknikerligi", + gerekce: + "Tek kurum (Karamanoğlu Mehmetbey Ü.); geçmiş sırası aynı üniversitedeki Su ve Atık Yönetimi Teknikerliği'ne taşınmış", + }, + { + kaynak: "/bolum/emlak-ve-emlak-yonetimi", + hedef: "/bolum/gayrimenkul-gelistirme-ve-yonetimi", + gerekce: + "Tek kurum (Ankara Hacı Bayram Veli Ü. Tapu Kadastro YO); geçmiş sırası aynı okuldaki Gayrimenkul Geliştirme ve Yönetimi'ne taşınmış", + }, + { + kaynak: "/bolum/meteoroloji-muhendisligi", + hedef: "/bolum/iklim-bilimi-ve-meteoroloji-muhendisligi", + gerekce: + "Tek kurum (Samsun Ü.); program aynı fakültede İklim Bilimi ve Meteoroloji Mühendisliği adıyla sürüyor", + }, + { + kaynak: "/bolum/endustri-yonetimi-muhendisligi", + hedef: "/bolum/endustri-muhendisligi", + gerekce: + "Tek kurum (Uluslararası Balkan Ü.); aynı program kimlikleri 2026 kılavuzunda Endüstri Mühendisliği adıyla", + }, + { + kaynak: "/bolum/taki-tasarimi", + hedef: "/bolum/kuyumculuk-ve-mucevher-tasarimi", + gerekce: + "Lisans; Altınbaş Ü. Uygulamalı Bilimler Fak.'nde Takı Tasarımı 2024'te bitti, aynı fakültede 2025'ten itibaren Kuyumculuk ve Mücevher Tasarımı var", + }, + { + kaynak: "/bolum/islam-bilimleri", + hedef: "/bolum/ilahiyat", + gerekce: + "Sayfanın tek satırı (Kırgızistan-Türkiye Manas Ü. İlahiyat Fak., kimlik 405210389) 2026 kılavuzunda İlahiyat adıyla", + }, + { + kaynak: "/bolum/bati-dilleri", + hedef: "/bolum/filoloji", + gerekce: + "Tek kurum (Manas Ü. Edebiyat Fak.); 2024'te kapandı, aynı fakültede 2025'ten itibaren Filoloji (İngiliz Dili ve Edebiyatı) açıldı", + }, + { + kaynak: "/bolum/dogu-dilleri", + hedef: "/bolum/filoloji", + gerekce: + "Tek kurum (Manas Ü. Edebiyat Fak.); Batı Dilleri ile aynı yeniden yapılanma, fakültenin dil programları Filoloji altında", + }, + + // ---- Bölümler: aynı alanın lisans karşılığı / ad yakınlığı ---- + { + kaynak: "/bolum/uygulamali-ingilizce-cevirmenlik", + hedef: "/bolum/ingilizce-mutercim-ve-tercumanlik", + gerekce: + "Önlisans programı 2024'ten sonra kılavuzda yok, önlisansta çeviri programı kalmadı; aynı alanın lisans karşılığı", + }, + { + kaynak: "/bolum/uygulamali-ispanyolca-cevirmenlik", + hedef: "/bolum/ispanyolca-mutercim-ve-tercumanlik", + gerekce: "Önlisans programı kapandı; aynı alanın lisans karşılığı", + }, + { + kaynak: "/bolum/menkul-kiymetler-ve-sermaye-piyasasi", + hedef: "/bolum/sermaye-piyasasi", + gerekce: "Önlisans programı kapandı; aynı alanın lisans karşılığı", + }, + { + kaynak: "/bolum/siyasal-bilimler", + hedef: "/bolum/siyaset-bilimi", + gerekce: "Tek kurum (Uluslararası Balkan Ü.); eş anlamlı ad", + }, + { + kaynak: "/bolum/uluslararasi-ekonomi", + hedef: "/bolum/ekonomi", + gerekce: "Tek kurum (Uluslararası Balkan Ü.); adı en yakın yaşayan bölüm", + }, + { + kaynak: "/bolum/girisimcilik", + hedef: "/bolum/uluslararasi-girisimcilik", + gerekce: "Tek kurum (Özyeğin Ü.); adı en yakın yaşayan bölüm", + }, + { + kaynak: "/bolum/film-tasarimi-ve-yazarligi", + hedef: "/bolum/film-tasarimi-ve-yonetimi", + gerekce: "Tek kurum (Dokuz Eylül Ü. GSF); aynı alan, adı en yakın yaşayan bölüm", + }, + { + kaynak: "/bolum/film-tasarimi-ve-yonetmenligi", + hedef: "/bolum/film-tasarimi-ve-yonetimi", + gerekce: "Tek kurum (Dokuz Eylül Ü. GSF); aynı alan, adı en yakın yaşayan bölüm", + }, + { + kaynak: "/bolum/muhendislik-programlari", + hedef: "/bolum/muhendislik-ve-doga-bilimleri-programlari", + gerekce: + "TED Ü. bölümsüz mühendislik girişi kapandı; aynı türden (fakülteye giriş, bölüm sonradan) yaşayan tek sayfa", + }, + { + kaynak: "/bolum/iktisadi-ve-idari-bilimler-programlari", + hedef: "/bolum/yonetim-bilimleri-programlari", + gerekce: + "TED Ü. bölümsüz İİBF girişi kapandı; aynı türden (fakülteye giriş, bölüm sonradan) en yakın yaşayan sayfa", + }, + + // ---- Bölümler: alan yakınlığı — ardıl ad kanıtı YOK, Bilal onayı bekliyor ---- + { + kaynak: "/bolum/spor-yoneticiligi", + hedef: "/bolum/rekreasyon", + gerekce: + "113 satırın tamamı 2026 kılavuzundan çıktı, ardıl ad yok; spor bilimleri fakültelerinde merkezi yerleştirmeyle öğrenci alan kardeş bölüm (Rekreasyon Yönetimi turizm fakültelerinde, o yüzden seçilmedi)", + }, + { + kaynak: "/bolum/spor-yonetimi", + hedef: "/bolum/rekreasyon", + gerekce: + "Önlisans programı 2024'ten sonra kılavuzda yok, ardıl ad yok; Spor Yöneticiliği ile aynı hedef", + }, + { + kaynak: "/bolum/bilgi-yonetimi", + hedef: "/bolum/bilgi-ve-belge-yonetimi", + gerekce: + "Önlisans programı 2024'ten sonra kılavuzda yok, ardıl ad yok; adı ve alanı en yakın yaşayan (lisans) bölüm", + }, + { + kaynak: "/bolum/ambalaj-tasarimi", + hedef: "/bolum/grafik-tasarimi", + gerekce: + "Tek kurum (Fatih Sultan Mehmet Vakıf Ü. MYO), ardıl ad yok; ambalaj tasarımı grafik tasarımın alt dalı, hedefte 119 önlisans programı var", + }, + + // ---- Bölümler: yakın sayfa yok → hub ---- + { + kaynak: "/bolum/nufus-ve-vatandaslik", + hedef: "/bolumler", + gerekce: "Önlisans programı kapandı; ardıl ad ya da alanı yakın yaşayan bölüm yok", + }, + { + kaynak: "/bolum/aile-ve-tuketici-bilimleri", + hedef: "/bolumler", + gerekce: "Tek kurum (Hacettepe Ü.); alanı yakın yaşayan bölüm yok", + }, + { + kaynak: "/bolum/ev-idaresi", + hedef: "/bolumler", + gerekce: "Tek kurum (Anadolu Ü. AÖF); alanı yakın yaşayan bölüm yok", + }, + + // ---- Üniversiteler: kurum ad değiştirdi ---- + { + kaynak: "/universite/izmir-kavram-meslek-yuksekokulu", + hedef: "/universite/izmir-konak-meslek-yuksekokulu", + gerekce: + "Ad değişikliği: 10929 sayılı Cumhurbaşkanı Kararı, Resmî Gazete 31.01.2026 / 33154; 2026 kılavuzunda yeni adla 72 program", + }, + { + kaynak: "/universite/kibris-ilim-universitesi", + hedef: "/universite/kibris-aydin-universitesi", + gerekce: + "Ad değişikliği (Mart 2025); veride Kıbrıs Aydın Ü. satırları Kıbrıs İlim Ü.'nün aynı programlarını ve 2024 sıralarını birebir taşıyor", + }, +]; + +export type KaliciYonlendirme = { + source: string; + destination: string; + permanent: true; +}; + +/** + * `next.config.ts` → `redirects()` için kurallar. `:yol*` sıfır ya da daha çok + * parça eşler: sayfanın kendisi, `/sayfa/N` sayfalama URL'leri ve diğer alt + * yollar hedefin KÖK sayfasına gider (hedefin sayfa sayısı farklıdır, N taşınmaz). + */ +export function kapananYonlendirmeleri(): KaliciYonlendirme[] { + const kaynaklar = new Set(); + for (const { kaynak } of KAPANAN_SLUGLAR) { + if (kaynaklar.has(kaynak)) { + throw new Error(`kapanan-sluglar: "${kaynak}" iki kez tanımlı`); + } + kaynaklar.add(kaynak); + } + return KAPANAN_SLUGLAR.map(({ kaynak, hedef }) => { + if (kaynaklar.has(hedef)) { + throw new Error( + `kapanan-sluglar: "${kaynak}" → "${hedef}" zincir oluşturuyor (hedef de kapanan listesinde)`, + ); + } + return { source: `${kaynak}/:yol*`, destination: hedef, permanent: true }; + }); +}