Admin

Koleksiyon & Blok Oluşturma

Data Model'de bir alan/koleksiyon değişikliği yaptığında ve bunu bir Vue bileşeninde (blok) tipli olarak kullanmak istediğinde izlenecek adımlar.

1. Koleksiyon oluşturma

/admin/schema "Yeni collection". Ad, görüntü adı, görünüm şablonu ve ilk alanları girip oluştur. Tekil koleksiyonlar (header/footer gibi) için "Tekil kayıt" kutusunu işaretle.

Var olan bir koleksiyona sonradan alan eklemek/silmek/düzenlemek için /admin/schema/[collection] sayfasını kullan.

Arka planda ne oluyor

UI isteği POST /api/collections veya /api/fields/* ile server/engine/ddl/service.ts'e gidiyor. Orada fiziksel tablo (Knex) + mcms_collections/mcms_fields metadata'sı tek transaction'da, anında yazılıyor — ayrıca bir "apply" adımı yok. Bu yalnızca dev ortamında açık (capabilities.schemaEditing); prod'da şema düzenleme route'ları tamamen devre dışı/tree-shake'lenmiş.

Bir alanı localized yapmak (veya geri almak) da aynı sayfadan, anında ve mevcut veriyi otomatik taşıyarak çalışır — alan ana tablo ile <collection>_locales companion tablosu arasında veri kaybı olmadan taşınır.

2. Tip güvenliği: "Pull types"

Şema değişikliğinden sonra /admin/schema "Pull types"'a bas. Bu, drizzle-kit introspect'i canlı veritabanına karşı çalıştırıp server/db/schema-dynamic/schema.generated.ts dosyasını yeniden üretir.

Önemli — bu dosyanın rolü

  • Sadece tip çıkarımı (InferSelectModel) için var — component'lerde IntelliSense/tip güvenliği sağlar.
  • Runtime source of truth değil — içerik okuma/yazma hâlâ metadata-driven query motorundan (server/engine/query/*) gidiyor. Bu dosyadan hiçbir yerde import ile veri çekilmiyor/yazılmıyor.
  • Elle düzenlenmez — dosyanın başında bunu belirten bir uyarı var. Bir şema değişikliğinden sonra bayatlar; tekrar "Pull types" ile güncellenir.

3. Yeni bir blok (Page Builder) oluşturma

  1. Koleksiyonu oluştur — adı block_ ile başlasın (ör. block_testimonial_grid) ve "hidden" işaretli olsun (içerik listesinde tek başına görünmesin, sadece sayfa builder'dan eklensin). UI'dan ya da server/collections.config.ts'e kod olarak eklenebilir — ikisi de aynı motoru kullanır.
  2. Vue bileşenini yazapp/blocks/BlockX.vue, tek bir data prop'u alır.
  3. Registry'e ekleapp/blocks/registry.ts'teki blockRegistry map'ine block_x: BlockX satırını ekle.
  4. "Pull types" çalıştır (yukarıdaki adım 2), yeni koleksiyonun tipi schema.generated.ts'e düşsün.
  5. Prop tipini yazapp/blocks/BlockHero.vue'daki örneği takip et:
import type { InferSelectModel } from "drizzle-orm";
import type { block_x } from "~~/server/db/schema-dynamic/schema.generated";

defineProps<{ data: InferSelectModel<typeof block_x> | null }>();

4. Localized alanlar varsa

Localized alanlar (ör. çeviri gerektiren title/headline) ana tabloda değil, block_x_locales companion tablosunda yaşar — ama query motoru şu anki dil için değerleri satırın üzerine otomatik merge eder (server/engine/query/relations.ts'teki mergeLocales). Bu yüzden tip de iki tabloyu birleştirmeli:

import type { InferSelectModel } from "drizzle-orm";
import type { block_x, block_x_locales } from "~~/server/db/schema-dynamic/schema.generated";

type BlockXData = InferSelectModel<typeof block_x>
  & Omit<InferSelectModel<typeof block_x_locales>, "id" | "parent_id" | "locale">;

defineProps<{ data: BlockXData | null }>();

Omit tercih edilir, Pick<..., "title" | "headline"> gibi alan adlarını elle yazmak yerine — yeni bir localized alan eklendiğinde bu satırı değiştirmeden otomatik tipe dahil olur.

5. İlişkisel alanlar (m2o / o2m / m2m / m2a)

Bir buton grubu, galeri, SSS listesi gibi ilişkili alanlar public render'da (expandM2O ile) otomatik olarak dolu obje halinde gelir — editör tarafında ise ham id kalır (seçim input'u için). Bu alanların fiziksel şekli schema.generated.ts'e yansımaz (junction/ilişki tabloları introspect'te ayrı tablolar olarak görünür); component'te genelde tam tip yerine unknown ya da ilgili alt component'in kendi prop tipiyle çalışmak yeterli — bkz. app/blocks/BlockButtons.vue.

6. Mimari & Split-Runtime Modeli

Proje tek kod tabanı (Monolith Nuxt) ile iki farklı ortama derlenir:

Dev / Local (Node.js Target)

  • Tam Authoring & DDL Motoru (Knex DDL + Drizzle DML)
  • Canlı Şema ve Koleksiyon Düzenleme (capabilities.schemaEditing = true)
  • Server-side Sharp ile Görsel İşleme
  • Redis / Memory Cache ve Local FS Storage

Prod (Cloudflare Workers Edge Target)

  • İçerik Editörlüğü & Edge Serve (Drizzle DML + Hyperdrive)
  • Şema Düzenleme Kilitli & Tree-shake Edilmiş (capabilities.schemaEditing = false)
  • Client-side Resize / CF Images + R2 Storage
  • Cloudflare KV Cache + Request-scoped Hyperdrive DB Connection

Not: DDL mutasyonları sadece Dev ortamında gerçekleşir. Şema snapshot'ı Git'e commit edilir ve CI/CD pipeline'ı üzerinden Prod Neon veritabanına drizzle-kit migrate ile yansıtılır.

7. Draft / Publish & Live Preview (Canlı Önizleme)

İçerik yaşam döngüsü Draft (Taslak) ve Published (Yayınlanmış) durumlarıyla yönetilir:

  • Draft Veri: İçerik güncellendiğinde taslak delta mcms_revisions tablosunda saklanır. Public siteye yansımaz.
  • Live Preview: Özel preview_token ile çağrılan istekler public cache'i tamamen bypass eder ve ham taslak veriyi render eder.
  • Çift Sekme / Split View Senkronizasyonu: Editör panelinde yapılan değişiklikler BroadcastChannel API üzerinden önizleme penceresine anında iletilir ve sayfa/blok yeniden çekilir.
  • Publish (Yayınlama): Taslak revizyonu canlı tabloya yazılır ve versiyon token'ı güncellenerek cache invalidated edilir.

8. Cache & Versiyon-Namespace Stratejisi

Anonim ziyaretçiler için yüksek performanslı cache, yetkili editörler için ise sıfır-gecikmeli tazelik hedeflenmiştir:

  • Versiyon Token: Cache anahtarları koleksiyon bazlı versiyon token'larını içerir (articles:locale=tr:vToken). İçerik yayımlandığında token güncellenir ve eski cache bayatlar.
  • Admin Bypass: Oturum açmış içerik editörlerinin istekleri public cache'i otomatik bypass eder, böylece yapılan düzenlemeler anında gözlemlenir.
  • Hyperdrive Cache Ayarı: Hyperdrive'ın kendi dahili cache'i disabled konumdadır; read-your-writes tutarlılığı Nuxt seviyesindeki KV/Redis katmanıyla sağlanır.

9. Best Practices, Kod Kalitesi & Test Rehberi

Geliştirme Standartları & İpuçları:

  • AST -> SQL Güvenliği: Filtre sorgularında Drizzle sql şablonu kullanılarak SQL Injection riskleri tamamen engellenmiştir. Ham SQL birleştirmelerinden kaçının.
  • Request-scoped DB/Auth: Server handlers ve auth işlemlerinde modül seviyesinde tekil DB/Auth istemcileri tutmayın; request-scoped getDb() ve requireUser() kullanın.
  • Tip Çıkarımı: Vue bileşenlerinde manuel tip yazmak yerine InferSelectModel ve Omit türetimlerini kullanın.
  • Derleme Doğrulaması: Değişiklik sonrası npx tsc --noEmit ve npm run build:cf komutlarıyla hem Node hem Cloudflare build hedeflerini test edin.