Add KeyStatic CMS integration

Features added:
- KeyStatic Core and Astro integration installed
- Markdoc integration for content rendering
- keystatic.config.ts with blog post schema
- Local storage mode for content management
- Conditional loading via SKIP_KEYSTATIC env var
- Netlify build command updated to skip KeyStatic in production

KeyStatic Admin UI available at:
- Local: http://localhost:4321/keystatic
- Production: Disabled for security

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Buğra Canata
2026-01-17 19:51:49 +03:00
parent 2467d5555f
commit 8f64fb1eea
5 changed files with 3203 additions and 106 deletions
+4 -1
View File
@@ -17,6 +17,9 @@ import { readingTimeRemarkPlugin, responsiveTablesRehypePlugin, lazyImagesRehype
import react from '@astrojs/react';
import markdoc from '@astrojs/markdoc';
import keystatic from '@keystatic/astro';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const hasExternalScripts = true;
@@ -60,7 +63,7 @@ export default defineConfig({
Logger: 1,
}), astrowind({
config: './src/config.yaml',
}), react()],
}), react(), markdoc(), ...(process.env.SKIP_KEYSTATIC ? [] : [keystatic()])],
i18n: {
defaultLocale: 'tr',
+69
View File
@@ -0,0 +1,69 @@
import { config, fields, collection } from '@keystatic/core';
export default config({
storage: {
kind: 'local',
},
collections: {
post: collection({
label: 'Blog Yazıları',
slugField: 'title',
path: 'src/data/post/*',
format: { contentField: 'body' },
schema: {
title: fields.slug({
name: {
label: 'Başlık',
description: 'Blog yazısının başlığı',
},
}),
date: fields.datetime({
label: 'Yayın Tarihi',
description: 'Yazının yayınlanma tarihi',
}),
author: fields.text({
label: 'Yazar',
description: 'Çağrı işareti veya yazar adı',
default: 'TA1SPH',
}),
categories: fields.array(
fields.text({
label: 'Kategori',
})
, {
label: 'Kategoriler',
description: 'Blog yazısının kategorileri',
}),
tags: fields.array(
fields.text({
label: 'Etiket',
})
, {
label: 'Etiketler',
description: 'Blog yazısının etiketleri',
}),
layout: fields.text({
label: 'Düzen',
default: 'post',
}),
image: fields.image({
label: 'Öne Çıkan Görsel',
description: 'Blog yazısı için kapak görseli',
}),
publishDate: fields.datetime({
label: 'Yayınlanma Tarihi',
description: 'Content collection için yayın tarihi',
}),
excerpt: fields.text({
label: 'Özet',
description: 'Blog yazısının kısa özeti',
multiline: true,
}),
body: fields.markdoc({
label: 'İçerik',
description: 'Blog yazısının ana içeriği',
}),
},
}),
},
});
+1 -1
View File
@@ -1,6 +1,6 @@
[build]
publish = "dist"
command = "npm run build"
command = "SKIP_KEYSTATIC=true npm run build"
ignore = "git log -1 --pretty=format:'%ae' | grep -v 'bcanata@hotmail.com'"
[build.processing.html]
pretty_urls = false
+3126 -104
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -21,12 +21,15 @@
"fix:prettier": "prettier -w ."
},
"dependencies": {
"@astrojs/markdoc": "^0.15.10",
"@astrojs/react": "^4.4.2",
"@astrojs/rss": "^4.0.11",
"@astrojs/sitemap": "^3.3.0",
"@astrolib/analytics": "^0.6.1",
"@astrolib/seo": "^1.0.0-beta.8",
"@fontsource-variable/inter": "^5.2.5",
"@keystatic/astro": "^5.0.6",
"@keystatic/core": "^0.5.48",
"@types/react": "^19.2.8",
"@types/react-dom": "^19.2.3",
"astro": "^5.7.3",