feat: SEO altyapısı + admin editör/favicon/menü düzeltmeleri

Admin & site:
- @tailwindcss/typography ekle → editör ve yayın içeriği prose stilleriyle düzgün render
- Favicon: logo.png'den kare app/icon.png + apple-icon.png, varsayılan favicon.ico kaldırıldı
- SEO keyword: seo_settings.default_keywords + seo_pages.keywords + buildMetadata birleştirme
- Menü düzeni admin'den yönetilebilir (site_settings.nav_items, /admin/menu, header & mobile-menu refactor)

SEO:
- app/sitemap.ts (statik + blog/hizmet/çözüm/proje/sektör dinamik)
- app/robots.ts (sitemap ref + /admin,/api disallow)
- app/llms.txt/route.ts (AI/LLM rehberi)
- BlogPosting/Service/FAQ/Article JSON-LD wire (json-ld bileşenleri bağlandı)
- buildMetadata: blog/proje OG görseli + type article + keywords birleştirme düzeltmesi
- blog tags → keyword
This commit is contained in:
egecankomur
2026-06-04 07:15:18 +03:00
parent a321ac5c9b
commit d49c9aa225
26 changed files with 780 additions and 151 deletions
+43 -2
View File
@@ -19,17 +19,58 @@ export async function buildMetadata(path: string, fallback?: Metadata): Promise<
override?.description ??
(fallback?.description as string | undefined) ??
siteDesc;
const ogImage = override?.og_image || ogDefault;
// Sayfanın kendi OG bilgisi (blog kapağı, type:"article" vb.) — fallback'ten
// oku. Öncelik: sayfa SEO override > sayfanın fallback OG görseli > varsayılan.
const fbOg = fallback?.openGraph as
| { images?: unknown; type?: string }
| undefined;
const fbOgImage = (() => {
const imgs = fbOg?.images;
if (typeof imgs === "string") return imgs;
if (Array.isArray(imgs) && imgs.length) {
const first = imgs[0];
if (typeof first === "string") return first;
if (first && typeof first === "object" && "url" in first)
return String((first as { url: unknown }).url);
}
return undefined;
})();
const ogImage = override?.og_image || fbOgImage || ogDefault;
const ogType = fbOg?.type ?? "website";
// Anahtar kelimeler: sayfa override + site geneli varsayılan + sayfanın kendi
// keyword'leri (örn. blog etiketleri) birleştirilir, tekrarlar ayıklanır.
const fbKeywords = fallback?.keywords;
const fbKeywordsStr = Array.isArray(fbKeywords)
? fbKeywords.join(",")
: typeof fbKeywords === "string"
? fbKeywords
: "";
const keywordsRaw = [override?.keywords, settings?.default_keywords, fbKeywordsStr]
.filter(Boolean)
.join(",");
const keywords = keywordsRaw
? Array.from(
new Set(
keywordsRaw
.split(",")
.map((k) => k.trim())
.filter(Boolean),
),
)
: undefined;
return {
title,
description,
keywords,
metadataBase: new URL(siteConfig.url),
openGraph: {
title,
description,
images: ogImage ? [{ url: ogImage }] : undefined,
type: "website",
type: ogType as "website" | "article",
locale: "tr_TR",
siteName,
},