f833d429fc
Backend altyapısı: - 4 yeni Appwrite tablosu: blog_posts, testimonials, seo_pages, seo_settings - Appwrite Storage bucket: kovak-yazilim-media (görsel yüklemeleri) - Appwrite Auth ile session cookie tabanlı koruma Admin paneli (/admin): - Login akışı (email/password) + protected layout - Dashboard: sayım kartları + hızlı aksiyonlar - Blog CRUD: markdown content, kapak görseli, draft/published, SEO alanları - Services CRUD: lucide ikon seçici - Projects CRUD: teknoloji etiketleri, live URL - Testimonials CRUD: puanlama - SEO yöneticisi: global ayarlar + sayfa bazlı override - Mesaj inbox: status filtreleme + güncelleme - Medya kütüphanesi: Appwrite Storage upload/delete Public: - /blog ve /blog/[slug] sayfaları (markdown render) - Anasayfaya Testimonials bölümü - Tüm public sayfalarda generateMetadata + seo_pages override - Header'a Blog linki Route yapısı: - app/(site)/ — public site, Header/Footer ortak - app/admin/login — auth dışı - app/admin/(protected)/ — requireUser() korumalı 23 route üretiliyor, public static, admin dynamic.
54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
import "server-only";
|
|
import type { Metadata } from "next";
|
|
import { getSeoPage, getSeoSettings } from "@/lib/data";
|
|
import { siteConfig } from "@/lib/site-config";
|
|
|
|
export async function buildMetadata(path: string, fallback?: Metadata): Promise<Metadata> {
|
|
const [settings, override] = await Promise.all([
|
|
getSeoSettings(),
|
|
getSeoPage(path),
|
|
]);
|
|
|
|
const siteName = settings?.site_name || siteConfig.name;
|
|
const siteDesc = settings?.site_description || siteConfig.tagline;
|
|
const ogDefault = settings?.default_og_image || "/logo.png";
|
|
|
|
const title =
|
|
override?.title ?? (fallback?.title as string | undefined) ?? siteName;
|
|
const description =
|
|
override?.description ??
|
|
(fallback?.description as string | undefined) ??
|
|
siteDesc;
|
|
const ogImage = override?.og_image || ogDefault;
|
|
|
|
return {
|
|
title,
|
|
description,
|
|
metadataBase: new URL(siteConfig.url),
|
|
openGraph: {
|
|
title,
|
|
description,
|
|
images: ogImage ? [{ url: ogImage }] : undefined,
|
|
type: "website",
|
|
locale: "tr_TR",
|
|
siteName,
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title,
|
|
description,
|
|
images: ogImage ? [ogImage] : undefined,
|
|
site: settings?.twitter_handle ?? undefined,
|
|
},
|
|
alternates: override?.canonical
|
|
? { canonical: override.canonical }
|
|
: undefined,
|
|
robots: override?.noindex
|
|
? { index: false, follow: false }
|
|
: undefined,
|
|
verification: settings?.google_site_verification
|
|
? { google: settings.google_site_verification }
|
|
: undefined,
|
|
};
|
|
}
|