feat: admin paneli + blog + testimonials + SEO yöneticisi
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.
This commit is contained in:
+53
@@ -0,0 +1,53 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user