diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index d4eca65..6908279 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -20,9 +20,47 @@ const jetbrainsMono = JetBrains_Mono({ weight: ["400", "500", "600", "700"], }); +const BASE_URL = 'https://ayrisai.xyz'; + export const metadata: Metadata = { - title: "DevHub YouTube | Educational Code & Resource Portal", - description: "Official resource portal featuring tabbed code snippets, lesson notes, starter packs, and project downloads for my YouTube tutorials.", + metadataBase: new URL(BASE_URL), + title: { + default: 'ayris.tech YouTube | Code & Resource Portal', + template: '%s | ayris.tech YouTube', + }, + description: + 'Official resource portal for the ayris.tech YouTube channel. Browse source code, lesson notes, cheatsheets, AI prompts, and project downloads for every tutorial.', + keywords: [ + 'Next.js', 'React', 'TypeScript', 'Tailwind CSS', 'Python', + 'YouTube tutorial', 'source code', 'ayris.tech', 'web development', + 'AI agents', 'Prisma', 'Docker', 'full-stack', + ], + authors: [{ name: 'ayris.tech', url: BASE_URL }], + creator: 'ayris.tech', + openGraph: { + type: 'website', + locale: 'en_US', + url: BASE_URL, + siteName: 'ayris.tech YouTube Portal', + title: 'ayris.tech YouTube | Code & Resource Portal', + description: + 'Source code, lesson notes, cheatsheets, and project downloads for every ayris.tech YouTube tutorial.', + images: [{ url: `${BASE_URL}/logo.jpeg`, width: 400, height: 400, alt: 'ayris.tech Logo' }], + }, + twitter: { + card: 'summary_large_image', + title: 'ayris.tech YouTube | Code & Resource Portal', + description: 'Source code, cheatsheets, AI prompts and downloads for every ayris.tech tutorial.', + images: [`${BASE_URL}/logo.jpeg`], + }, + robots: { + index: true, + follow: true, + googleBot: { index: true, follow: true }, + }, + alternates: { + canonical: BASE_URL, + }, }; export function generateStaticParams() { diff --git a/app/robots.ts b/app/robots.ts new file mode 100644 index 0000000..b131dad --- /dev/null +++ b/app/robots.ts @@ -0,0 +1,32 @@ +import type { MetadataRoute } from 'next'; + +const BASE_URL = 'https://ayrisai.xyz'; +const PROTECTED = ['/admin/', '/api/']; + +export default function robots(): MetadataRoute.Robots { + return { + rules: [ + // Genel arama botları — tüm public içerik açık + { + userAgent: '*', + allow: '/', + disallow: PROTECTED, + }, + // AI arama / citation botları — izin ver + { userAgent: 'OAI-SearchBot', allow: '/', disallow: PROTECTED }, + { userAgent: 'ChatGPT-User', allow: '/', disallow: PROTECTED }, + { userAgent: 'PerplexityBot', allow: '/', disallow: PROTECTED }, + { userAgent: 'Perplexity-User', allow: '/', disallow: PROTECTED }, + { userAgent: 'Claude-SearchBot', allow: '/', disallow: PROTECTED }, + { userAgent: 'Claude-User', allow: '/', disallow: PROTECTED }, + // Training crawler'ları engelle + { userAgent: 'GPTBot', disallow: '/' }, + { userAgent: 'ClaudeBot', disallow: '/' }, + { userAgent: 'Google-Extended', disallow: '/' }, + { userAgent: 'CCBot', disallow: '/' }, + { userAgent: 'Bytespider', disallow: '/' }, + { userAgent: 'Meta-ExternalAgent', disallow: '/' }, + ], + sitemap: `${BASE_URL}/sitemap.xml`, + }; +} diff --git a/app/sitemap.ts b/app/sitemap.ts new file mode 100644 index 0000000..61918ef --- /dev/null +++ b/app/sitemap.ts @@ -0,0 +1,73 @@ +import type { MetadataRoute } from 'next'; +import { getLessons } from '@/lib/actions/lessonActions'; +import { getCheatsheets } from '@/lib/actions/cheatsheetActions'; +import { getPrompts } from '@/lib/actions/promptActions'; + +const BASE_URL = 'https://ayrisai.xyz'; +const LOCALES = ['en']; + +export default async function sitemap(): Promise { + const now = new Date(); + + // Static pages + const staticRoutes = [ + { path: '', priority: 1.0, changeFrequency: 'daily' as const }, + { path: '/lessons', priority: 0.9, changeFrequency: 'daily' as const }, + { path: '/cheatsheets', priority: 0.8, changeFrequency: 'weekly' as const }, + { path: '/prompts', priority: 0.8, changeFrequency: 'weekly' as const }, + { path: '/contact', priority: 0.5, changeFrequency: 'monthly' as const }, + ]; + + const staticEntries: MetadataRoute.Sitemap = LOCALES.flatMap((locale) => + staticRoutes.map(({ path, priority, changeFrequency }) => ({ + url: `${BASE_URL}/${locale}${path}`, + lastModified: now, + changeFrequency, + priority, + })) + ); + + // Dynamic: Lessons + let lessonEntries: MetadataRoute.Sitemap = []; + try { + const lessons = await getLessons(); + lessonEntries = LOCALES.flatMap((locale) => + lessons.map((lesson: any) => ({ + url: `${BASE_URL}/${locale}/lessons/${lesson.slug}`, + lastModified: lesson.updatedAt || now, + changeFrequency: 'weekly' as const, + priority: 0.85, + })) + ); + } catch {} + + // Dynamic: Cheatsheets + let cheatEntries: MetadataRoute.Sitemap = []; + try { + const sheets = await getCheatsheets(); + cheatEntries = LOCALES.flatMap((locale) => + sheets.map((sheet: any) => ({ + url: `${BASE_URL}/${locale}/cheatsheets/${sheet.slug}`, + lastModified: sheet.updatedAt || now, + changeFrequency: 'monthly' as const, + priority: 0.7, + })) + ); + } catch {} + + // Dynamic: Prompts + let promptEntries: MetadataRoute.Sitemap = []; + try { + const prompts = await getPrompts(); + promptEntries = LOCALES.flatMap((locale) => + prompts.map((prompt: any) => ({ + url: `${BASE_URL}/${locale}/prompts/${prompt.slug}`, + lastModified: prompt.updatedAt || now, + changeFrequency: 'monthly' as const, + priority: 0.7, + })) + ); + } catch {} + + return [...staticEntries, ...lessonEntries, ...cheatEntries, ...promptEntries]; +}