Hızlı başlangıçlar, kavramlar, entegrasyon kılavuzları, geçiş playbook'ları ve SDK örnekleri — Nuvi destekli bir mağazayı baştan sona yayınlamak için ihtiyacın olan her şey.
HIZLI BAŞLANGIÇ
Beş dakikalık başlangıç
Kayıttan canlı mağazaya, baştan sona.
Nuvi, dakikalar içinde şık ve production'a hazır bir mağazayı çalışır hale getirmek üzere kurgulandı (domain/DNS daha uzun sürebilir). Beş adım:
Kaydol usenuvi.com/register — e-posta + parola, kart yok.
Tema seç. Her tema örnek ürünler ve 8–12 önceden yapılandırılmış bölümle gelir. İçeriğini kaybetmeden istediğin zaman tema değiştirebilirsin.
Domain bağla (opsiyonel ama önerilir). Panelden ara ya da elindeki domaini yapıştır — DNS otomatik kurulabilir; SSL genellikle dakikalar içinde inişer.
Stripe ya da Iyzico bağla. Tek tıkla OAuth ya da Nuvi üzerinden taze merchant hesabı aç.
Hero'nu düzenle Tema Editöründe — yeni bölüm sürükle, başlığı değiştir, renkleri ayarla. Yayınla'ya bas.
Hepsi bu. Artık checkout, müşteri hesabı, blog ve AI asistanlı tam çalışan bir mağazan var.
TIP
22 modülün hepsi ilk günden dahil — Starter'dan Enterprise'a her planda. Blog'u veya form yapıcıyı "açmak" için plan seçmiyorsun — panelinde seni bekliyorlar. Her planda işlem ücreti %0 ve 24 AI yeteneğinin tümü aynı şekilde çalışır; planlar yalnızca site sayısı ve performansta farklılaşır.
HIZLI BAŞLANGIÇ
Temel kavramlar
Modüller, temalar, bölümler ve sayfalar nasıl birleşir.
Nuvi dört kavramsal katmandan oluşur. Aralarındaki ilişkiyi anlamak her şeyi kolaylaştırır:
Modüller
Platformun yetenekleri: blog, sayfa, teklif, arama, tema, para birimi, vs. 22 tane var, her planda dahil. Modül = bir yetenek için veri + API + admin UI. Modülleri kurmazsın ya da yapılandırmazsın — her zaman açıklar.
Temalar
Mağazanın görünüşü. Tema; tasarım token'larını (renk, font, boşluk), bölüm tanımlarını ve varsayılan ayarları paketler. Tema değiştirmek içeriği kaybetmeden görünümü değiştirir.
Bölümler
Birleştirilebilir görsel bloklar: hero, özellik grid'i, fiyat, referanslar, SSS, CTA. Her bölüm içeriğini JSON ayarlarından okuyan bir React komponenti. Editör üzerinden sürükleyip sıralarsın.
Sayfalar
Storefront'undaki rotalar. Ana sayfa, ürün detayı, blog yazısı ve özel sayfalar (hakkında, kariyer vs.) sayfadır. Sayfa = bölüm listesi + içerik verisi (başlık, slug, SEO meta). Sayfalar page modülünde tutulur.
NOTE
Aynı bölüm bileşeni anasayfada, özel bir landing page'de veya blog yazısının içinde render olur. Beğendiğin bir hero'yu bir kez yapılandırınca her yere bırakabilirsin.
KAVRAMLAR
Modüller nasıl çalışır
Mimari, bağımlılık, yaşam döngüsü.
Her modül bağımsız bir NPM paketidir — @nuvi/module-blog, @nuvi/module-quote, vs. packages/modules/ altında yaşarlar; kendi veri modeli, servis sınıfı, manifest ve admin route'ları ile gelirler.
Modüller standartları paylaşır ama bağımsızdır: birini etkinleştirmek/kapatmak (module-setting üzerinden) diğerlerini bozmaz. Çoğu modülün sıkı bağımlılığı yoktur; olanlar bunu manifest.json'da belirtir.
Yaşam döngüsü
Platform boot olduğunda module loader ENABLED_MODULES env değişkenini okur, her modülün MikroORM entity'lerini kaydeder, admin ve storefront API route'larını mount eder ve servis sınıfını cross-module çağrılar için açar.
Cross-module çağrılar
Bir modül başka modülün servisini Medusa container üzerinden çağırabilir; örn. quote modülü page modülünden veri okuyup template render eder. HTTP atlama yok — servisler DI container'ından çözülen JS sınıfları.
TIP
Modüller indeksini tek sayfa özet için, /modules/<slug> daha derin pazarlama sayfaları için oku.
KAVRAMLAR
Kimlik doğrulama
API anahtarları, müşteri JWT, admin token.
Üç kimlik bağlamı ile çalışırsın:
Publishable API anahtarı
Her storefront isteğinde x-publishable-api-key olarak gönderilir. Çağrının hangi mağaza / bölgeye ait olduğunu belirler; istemci kodda paylaşmak güvenli. Her site için otomatik oluşturulur — Panel'inde bulabilirsin.
Müşteri JWT
POST /auth/customer/emailpass ile alınır. Giriş yapmış alıcıyı tanımlar. /store/customers/me, /store/orders/me, /store/subscriptions uçları için gerekli. Authorization: Bearer ... olarak gönder.
Admin token
Sunucu tarafı admin araçları ve entegrasyonlar için. İstemci kodda asla paylaşma. Sadece kendi backend'inden ya da CLI'dan kullan.
Modüller packages/modules/<ad>/ altında bağımsız NPM paketleridir. En temiz referans @nuvi/module-blog — şekli kopyala, adı değiştir. Minimum beş dosya yeterli.
@nuvi/module-sdk'nin validateManifest()'i tarafından okunur. Alanlar: name, version, displayName, description, icon, category, isCore, dependencies (diğer modül paket adları) ve modules, adminRoutes, apiRoutes bildiren bir medusa bloğu.
3. MikroORM modeli
Medusa'nın model.define() DSL'ini kullan. Multi-tenant izolasyon için store_id'yi her zaman koy ve indexle:
import { model } from "@medusajs/framework/utils"
const LoyaltyAccount = model.define("loyalty_account", {
id: model.id().primaryKey(),
store_id: model.text().default("default").searchable(),
customer_id: model.text(),
points: model.number().default(0),
}).indexes([{ on: ["store_id", "customer_id"], unique: true }])
export default LoyaltyAccount
4. Servis sınıfı
service.ts her modeli MedusaService({...}) ile genişletir — CRUD metodları (listLoyaltyAccounts, createLoyaltyAccounts, …) bedava gelir. module/index.ts bunu Module("loyalty", { service }) ile sarar ve LOYALTY_MODULE = "loyalty" sabitini export eder.
bootstrap/medusa/src/lib/module-registry.ts'a resolve, key, frontendId, apiRoutes, adminRoutes, isCore ile kaydet.
Cross-module çağrı için bootstrap/medusa/src/types/module-services.ts'a tipli arayüz ekle.
Yeniden kur: cd bootstrap && docker compose up -d --build medusa.
TIP
Paylaşılan tipler — ModuleManifest, ModuleCategory, ThemeBlockManifest — @nuvi/module-sdk'dan import edilir. SDK ayrıca modül sıralaması için resolveDependencies()'yı ve Zod benzeri guard isValidManifest()'i de export eder.
KILAVUZLAR
İlk ürününü ekle
Ürün oluşturmadan canlı PDP'ye.
En hızlı yol: Panel → Ürünler → Yeni ürün. Dolduracağın alanlar:
Başlık + handle. Handle URL slug olur (/products/your-handle).
Açıklama. Görsel gömme destekli rich-text editör. PDP'de olduğu gibi render olur.
Varyantlar. Ürünün seçenekleri varsa (boy, renk) varyant satırları olarak ekle. Her varyantın kendi fiyatı ve SKU'su var.
Görseller. Sürükle-bırak yükleme. İlk görsel kapak olur; sürükle ile sırala.
SEO. Opsiyonel meta başlık + açıklama. Boşsa ürün başlığı + ilk 160 karakter açıklamaya düşer.
Yayınla'ya bas. Ürün https://magazan.com/products/your-handle'de saniyeler ila birkaç dakika içinde canlı — Next.js sayfayı statik olarak regenerate eder.
TIP
Import modülünü CSV'den toplu ürün eklemek ya da mevcut Shopify / WooCommerce mağazandan çekmek için kullan. Yaygın kolonlar için alan eşleştirme otomatik.
KILAVUZLAR
Özel domain bağla
yourstore.nuvi.app'tan markaadi.com'a geç.
İki yol var:
Nuvi üzerinden satın al
Panel → Domain'ler'de domain ara. 500+ TLD arasından seç. Yıllık $15–30 (fiyatlara bak). Ödeme onaylandıktan sonra DNS otomatik kurulabilir; SSL genellikle dakikalar içinde inişer.
Kendi domainini getir
Zaten başka yerde domainin var mı? Aynı ekrandan ekle. Registrar'ında eklemen gereken CNAME / A kayıtlarını gösteririz. Yayılma tamamlanınca (genellikle 10–30 dk) SSL otomatik kurulur.
NOTE
Hem www.markaadi.com hem markaadi.com kutudan çıkar çıkmaz çalışır — apex (www-suz) kanoniktir, www 301 ile ona yönlendirilir.
Başka platformdan taşınıyorsan, Redirect modülü eski URL'lerine 301 önceden üretir; SEO değeri yeni URL'lere akar.
KILAVUZLAR
E-postanı kur
Kendi domain'inde mailbox — webmail + her mail uygulaması.
Kendi domain'inde profesyonel e-posta ([email protected]) — 15 GB alan, tam IMAP/SMTP/POP3, webmail, takvim & kişiler.
1. Mailbox oluştur
Panel → Ayarlar → E-posta'dan domain'ini ekle ve mailbox oluştur. Mailbox $5/ay ek hizmettir (hizmete bak). DNS kayıtları (MX, SPF, DKIM, DMARC) Nuvi-yönetilen domain'lerde otomatik eklenir; harici domain'de registrar'ına yapıştıracağın kayıtları gösteririz.
2. Webmail
mail.usenuvi.com'u aç ve tam e-posta adresin + şifrenle giriş yap. Webmail'de mail, takvim, kişiler, filtreler ve spam kontrolü var.
3. Kendi uygulamana bağla
Apple Mail, Outlook, Gmail ya da telefonunun mail uygulamasını mı tercih ediyorsun? Şu ayarları kullan:
iPhone / iPad: Ayarlar → Mail → Hesaplar → Hesap Ekle → Diğer → Posta Hesabı Ekle, sonra yukarıdaki değerleri gir. Apple Mail / Outlook / Gmail app / Android: "Diğer" / "IMAP" seç, aynı ayarları kullan. POP3 da 995 (SSL) portunda mevcut.
TIP
Çoğu kişi kendi mail uygulamasını kullanır — bağlanınca mailbox'ın diğer hesaplarının yanında, tamamen kendi markanda görünür.
4. Şifre, 2FA & alias
Mailbox panelinden şifreni değiştir, iki adımlı doğrulamayı aç ya da uygulama-başı şifre oluştur. Alias'lar (örn. satis@ → gelen kutun) ücretsiz — Ayarlar → E-posta → Yönlendirme'den ekle.
NOTE
Mail gelmiyor mu? Kurulumdan sonra DNS yayılması için bir saate kadar bekle ve MX/SPF/DKIM kayıtlarının olduğunu doğrula (Ayarlar → E-posta'da listeli).
KILAVUZLAR
Tema özelleştir
Token, bölüm, düzen — kodsuz.
Panelden Tema Editörü'nü aç. Genelden özele üç katman özelleştirme:
1. Token'lar (site geneli)
Vurgu rengini, font ailesini, köşe yuvarlamayı değiştir. var(--color-primary) kullanan her bölüm anında güncellenir. Inspector'da bir renge tıkla ya da hex yapıştır.
2. Bölümler (sayfa bazlı)
Her sayfa bir bölüm yığınıdır. Sol panelden yeni bölüm sürükle; sırayla taşı; satır menüsünden çoğalt ya da gizle. Bölümlerin kendi ayarları var (başlık, görsel, hizalama, vs.).
3. Bölüm ayarları (bölüm bazlı)
Bölüme tıkla, inspector açılsın. Başlığı düzenle, görsel değiştir, padding ayarla. Canlı önizleme yazdıkça güncellenir. Değişiklikler taslak olarak otomatik kaydedilir; Yayınla'ya basınca canlıya gider.
TIP
Editördeki AI asistan butonunu kullan — değişikliği düz dille anlat ("hero'yu daha kalın yap", "basın logoları stripi ekle") ve sana onaylayabileceğin bölüm + token düzenlemeleri önerir.
KILAVUZLAR
Webhook'larla event tüketimi
Bugün yerleşik workflow'lar, yarın imzalı outbound webhook'lar.
WARNING
Outbound webhook'lar (Nuvi'nin senin URL'ine POST atması) henüz hazır değil — yakın vadeli yol haritasında. API referansı / Webhook bölümü planlanan event şeklini gösterir. Bugün event'lere gerçek zamanlı tepki vermenin yolu workflow motoru.
Bugün: yerleşik workflow'lar
Her Nuvi tenant'ı gömülü bir workflow motoru ile gelir. Panel → Workflows (admin route: bootstrap/medusa/src/admin/routes/workflows/) altında açılır. Bir Nuvi trigger'ı seç — sipariş oluştu, ödeme alındı, müşteri oluştu, teklif gönderildi — ve action bloklarından birini bağla (HTTP, Slack, Sheets, özel kod).
Arka planda workflow motoru, ileride outbound webhook'ları besleyecek aynı Medusa event bus'ına abone olur — bugün kurduğun akışlar gerçek webhook çıkışı şipşırlanınca da çalışmaya devam eder.
Her teslimat x-nuvi-signature header'ı taşıyacak — endpoint'inin imza secret'ı ile raw body'nin HMAC-SHA256'sı. Node.js alıcısında doğrulama:
x-nuvi-signature doğrulama (planlanan)
import crypto from 'node:crypto'
export function verifyNuviSignature(rawBody: Buffer, header: string, secret: string) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
// Yanıt süresinden sızdırmamak için timingSafeEqual kullan
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(header, 'hex')
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
NOTE
Alıcılar, herhangi bir JSON parser dokunmadan ÖNCE raw body'yi okumalı — parse edilmiş objeyi yeniden serialize etmek HMAC'i bozar. Express'te express.raw({ type: 'application/json' }); Next.js route handler'ında JSON.parse yerine önce await req.text().
KILAVUZLAR
Özel ödeme sağlayıcı
Medusa v2 payment provider yaz, checkout'a tak.
Nuvi, Medusa v2'nin payment provider plugin modelini miras alır. Repo iki örnek taşır — Stripe (resmi @medusajs/payment-stripe) ve iyzico (bootstrap/medusa/src/modules/iyzico-payment/ içinde). Yeni bir sağlayıcı eklerken iyzico'yu kopyala.
1. Modül şekli
Provider, payment modülüne kayıtlı bir Medusa module provider'ıdır:
src/modules/myprov-payment/index.ts
import { ModuleProvider, Modules } from "@medusajs/framework/utils"
import MyProvService from "./service"
export default ModuleProvider(Modules.PAYMENT, {
services: [MyProvService],
})
2. Yaşam döngüsü metodları
AbstractPaymentProvider<Options>'i bir static identifier ile genişlet ve checkout sırasında Medusa'nın çağırdığı metodları yaz:
authorizePayment — geri dönen token'ı doğrula (redirect / 3DS sonrası).
capturePayment — yetkili tahsilatı sonlandır.
refundPayment — tam ya da kısmi iade.
retrievePayment / getPaymentStatus — mevcut durumu oku.
cancelPayment, deletePayment, updatePayment — bitmemiş oturumlar için bakım.
3. 3DS / redirect callback
Hosted-page sağlayıcılar (iyzico, PayTR, Mollie) redirect sonrası token doğrulamak için public callback ister. Kalıp bootstrap/medusa/src/api/store/iyzico-callback/route.ts'de: token formatını doğrula, beklenen cart'a ait olduğunu kontrol et, sonra sağlayıcının retrieve API'sini çağır.
WARNING
Callback'inde sunucuda her zaman tutarı ve basket ID'yi tekrar doğrula. Gateway yanıtında status === "SUCCESS" yetmez — tahsil edilen tutarın cart toplamına, basket ID'nin onaylanan kaynağa eşit olduğunu teyit et. Aksi halde fiyat manipülasyonu açığı kalır.
4. medusa-config'e kaydet
bootstrap/medusa/medusa-config.ts'teki @medusajs/medusa/payment providers array'ine, sağlayıcının yapılandırıldığını gösteren env'e bağlı koşullu bir giriş ekle. iyzico çalışan bir şablon:
Import'u çalıştır'a bas. Ürünler, varyantlar, müşteriler ve siparişler import edilir. Medya Nuvi storage'a yeniden yüklenir.
Redirect modülü eski Shopify URL'lerinden otomatik 301 üretir (/products/foo → Nuvi'de aynı path) — SEO korunur.
WARNING
Domainini Nuvi'ye yönlendirmeden önce test subdomaininde soft launch yap. Ürün sayfalarının render olduğunu, checkout'un baştan sona tamamlandığını ve redirect haritasının en çok trafiğin geldiği 50 URL'i kapsadığını doğrula.
Yardım gerekir mi? Premium Destek birebir geçiş yardımı içerir.
GEÇİŞ
WooCommerce'dan geç
WP export → Nuvi import.
WooCommerce geçişi Shopify ile aynı şekilde ilerler, bir farkla: WordPress hem storefront'u hem blog içeriğini barındırır, ikisini de import edersin.
WordPress admin'den Customer / Order / Coupon export eklentisini kur ve tam export çalıştır.
Blog yazılarını WP REST API ile çek: /wp-json/wp/v2/posts.
Nuvi'de: Panel → Import → Yeni import → WooCommerce. Her iki dosyayı yükle.
Import çalıştır. Ürün + siparişler ticarete; yazılar yazar + kategori korunarak Blog modülüne gider.
Hem /product/slug hem /blog/slug kalıpları için redirect'ler otomatik üretilir.
NOTE
Özel eklenti özellikleri (Yoast meta, Elementor sayfaları, Advanced Custom Fields) otomatik çevrilmez. Import modülü veriyi korur; Yoast meta'yı Nuvi SEO ayarı olarak yeniden yazarsın, Elementor sayfaları Nuvi bölümleri ile yeniden inşa edilmeli.
DEPLOYMENT
Ortam değişkenleri ve sırlar
Ne ayarlanır, nerede yaşar, nasıl sızdırılmaz.
Bootstrap konfigürasyonunu bootstrap/.env'deki tek bir dosyadan okur — bootstrap/.env.example'tan kopyalanır. docker-compose.yml değerleri otomatik alır ve her servise geçirir.
MEDUSA_JWT_SECRET, MEDUSA_COOKIE_SECRET — auth secret'ları. Dev dahil her ortamda zorunlu.
STORE_CORS, ADMIN_CORS, AUTH_CORS — Medusa'nın izin verdiği virgülle ayrılmış origin'ler.
REDIS_PASSWORD — cache + kuyruk.
NUVI_INTERNAL_TOKEN + INTERNAL_API_KEY — servisler arasında eşleşmeli.
Nesne depolama (Hetzner)
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_BUCKET, S3_FILE_URL. Bucket ürün medyası + tema varlıklarını taşır; S3_FILE_URL, Medusa'nın image yanıtlarında ürettiği public URL önekidir.
Modüller
ENABLED_MODULES, boot edecek modül paket adlarının virgülle ayrılmış listesi. Boş bırakılırsa tüm modüller yüklenir. Çekirdek modüller (theme, page, module-setting) yazmasan da her zaman yüklenir.
Aynı değişkenler, farklı kaynaklar. Dev'de bootstrap/.env düz metin — lokal için sorun değil. Production'da:
Sunucudaki deploy.sh repo dışında yönetilen bir .env tutar ve docker compose up'a bağlar.
Tenant başına override'lar (Stripe key, OAuth token, GA ID) OPS_ENCRYPTION_KEY (Fernet key) ile şifrelenip control plane tarafından saklanır, her tenant container'ı başlarken runtime'da enjekte edilir.
Cloudflare ve SHARED_HOST_IP değerleri ayrı bir ops vault'tan gelir, asla katkıcı laptopundan değil.
WARNING
.env, .env.local ya da gerçek sır içeren herhangi bir dosyayı commit etme. bootstrap/.env git-ignored. Track edilmesi güvenli olan tek env dosyası bootstrap/.env.example — o da yalnızca placeholder içermeli.
TIP
JWT ve cookie secret'larını ortam başına rotate et. Sızmış bir MEDUSA_JWT_SECRET, herkesin admin token'ı forge edebilmesi demek. Dev için openssl rand -hex 32; production secret'ları secret manager'ından (1Password, Doppler, AWS Secrets Manager) gelmeli.
TEMALAR
30 dakikada tema yap
Baştan sona — clone'dan canlı önizlemeye.
Nuvi'de tema iki yarımdan oluşur: JSON config (token, bölüm, varsayılan) packages/modules/theme/src/lib/theme-configs/<slug>.json ve React komponent ağacıbootstrap/tenant-front/src/themes/<slug>/. İkisi beraber bir mağazanın nasıl görüneceğini ve render olacağını tanımlar.
Adım 1 — Starter'ı kopyala
Mevcut starter teması üzerinden başla — en temiz minimal şekli o:
Yeni mybrand.json'u aç. id, name, description, author alanlarını değiştir. id alanı her yerde kullanılan URL-güvenli slug — küçük harf ve tire kullan.
Adım 3 — Token'ları ayarla
tokens.colors içinde vurgu paletini değiştir. CSS katmanı her giriş için var(--color-primary), var(--color-foreground) vb. otomatik üretir. Dark mode destekliyorsan tokens.colorsDark'ı da güncelle.
Adım 4 — Bölümleri bağla
bootstrap/tenant-front/src/lib/section-map.ts'i aç ve bölümlerini temana ait yorum bloğu altında kaydet:
Dev stack'i ayağa kaldır: cd bootstrap && docker compose up -d. http://localhost:3000'a gir, http://localhost:9000/app admine giriş yap, Ayarlar → Temalar → Mybrand seç. Değişiklikler hot-reload olur.
TIP
nuvi-base ve starter temaları kasıtlı olarak minimal. Çoğu production teması (usenuvi, editorial, gourmet) 15–30 bölüm taşır — en yakın olanı kopyalamak en hızlı yol.
TEMALAR
Tema anatomisi
Ne nerede yaşar, neden.
Tam bir tema repo'da dört yere dokunur:
Tema dosya haritası
packages/modules/theme/src/lib/theme-configs/
└── mybrand.json # JSON config — tek kaynak
bootstrap/tenant-front/src/themes/mybrand/
├── manifest.ts # Section'ları kendisi bildirir + tema metadata
├── tokens.ts # cs helper — radius / padding / tipografi
├── schemas.ts # Bu temaya ait Zod şemaları
├── locales/
│ ├── en.json # Tema-spesifik string override'ları
│ └── tr.json
└── sections/
├── MybrandHeader.tsx # Her bölüm tipi için React komponent
├── MybrandHero.tsx
└── MybrandFooter.tsx
bootstrap/medusa/src/lib/
└── section-schemas/mybrand.ts # Admin form alanları
JSON config tek kaynak. Temanın kimliğini, tasarım token'larını ve varsayılan bir sections array'ini bildirir — bu temayla render olan her sayfa buradan başlar. Runtime'da DB override'ları (admin özelleştirmeleri) üstüne biner.
React komponentler dummy render'lardır. Konfigürasyonlarını prop'tan okur (section.settings) ve JSX render eder. State yok, data fetch yok — sadece layout ve stil.
Zod şemaları admin ve storefront arasındaki sözleşmedir. Build sırasında ve API sınırlarında JSON config'i doğrular; admin section-editor form alanlarını da bunlar yönetir.
NOTE
Bölümler convention gereği tema-önekli (usenuvi-header, gourmet-product-carousel) — aynı bölüm tipi temalar arası çakışmasın. Paylaşımlı bölümler (header, hero, footer) themes/shared/ altında yaşar, herhangi bir tema yeniden kullanabilir.
TEMALAR
Tasarım token'ları
Renk, tipografi, boşluk, dark mode.
Token'lar runtime'da CSS custom property'lere dönen JSON değerleri. Şekil:
SSR tema helper'ı her yaprak değeri --<namespace>-<key>'a açar. tokens.colors.primary → --color-primary; tokens.layout.headerHeight → --layout-header-height. Dark varyantlar kullanıcı modu karanlıkken (ya da tema dark-only olduğunda) uygulanır.
Komponentlerde hex değerleri sabitleme. Her zaman var(--color-*) referansı kullan ki tema editörü kod değişikliği gerektirmeden adminin ayarlamasına izin versin — ve light/dark tutarlı davransın.
TEMALAR
Bir bölümün anatomisi
Şema + komponent + admin şeması.
Bir bölüm üç bağlı parçadan oluşur. Bu sırayla yap:
1. Zod şeması (storefront)
themes/mybrand/schemas.ts içinde:
import { z } from 'zod'
import { BaseSectionPropsSchema } from '@/themes/shared/schemas'
export const MybrandHeroSectionSchema = BaseSectionPropsSchema.extend({
type: z.literal('mybrand-hero'),
settings: z.object({
badge: z.string().optional(),
headline: z.string(),
subheading: z.string().optional(),
primaryCtaText: z.string().optional(),
primaryCtaHref: z.string().optional(),
}),
})
export type MybrandHeroSection = z.infer<typeof MybrandHeroSectionSchema>
2. React komponenti
themes/mybrand/sections/MybrandHero.tsx içinde:
'use client'
import type { z } from 'zod'
import type { MybrandHeroSectionSchema } from '@/themes/mybrand/schemas'
import { SectionComponentProps } from '@/lib/theme-types'
type Props = SectionComponentProps<z.infer<typeof MybrandHeroSectionSchema>>
export default function MybrandHero({ section }: Props) {
const { settings } = section
return (
<section style={{ padding: '4rem 1.5rem', textAlign: 'center' }}>
{settings.badge && <span>{settings.badge}</span>}
<h1>{settings.headline}</h1>
{settings.subheading && <p>{settings.subheading}</p>}
</section>
)
}
3. Kaydet
Section'ı tema kendi manifest.ts'ine ekler (artık merkezi bir registry düzenlemiyoruz — runtime her temanın manifest'inden global section map'i oluşturuyor):
Sonra mybrandManifest'i src/themes/index.ts'teki THEME_MANIFESTS'a ekle. Admin editör desteği için bootstrap/medusa/src/lib/section-schemas/mybrand.ts'a form alanlarını bildiren eşleşen şemayı da ekle.
WARNING
Zod şemasındaki type string'i section-map.ts'teki anahtar, JSON config'in section.type'ı VE admin şemasının type'ı ile eşleşmek zorunda. Aralarındaki herhangi bir kayma render'ı sessizce kırar.
TEMALAR
Block'lar: tekrarlı alt öğeler
Bölüm içinde liste modellemek.
Çoğu bölüm alt öğe listesine ihtiyaç duyar: feature kartı, müşteri sözü, fiyat tier'i, footer kolonu. Desteklenen iki kalıp:
Admin editörde sürükle-bırak / sıralama yapmak istiyorsan block kalıbı kazanır. Inline settings array'i daha basit ama görsel olarak düzenlemesi zor. Modern temalar (usenuvi, editorial) baştan sona block kullanır.
TEMALAR
Temanda i18n
ut() helper'ı ile EN/TR.
Temalar config'i fork etmeden dile göre metin override'ı destekler. İki parça:
1. Helper'ı bağla
Bölüm komponentinde useUsenuviLocale() (ya da tema eşdeğeri) çağırarak ut() fonksiyonunu al:
import { useUsenuviLocale } from './use-usenuvi-locale'
export default function MybrandHero({ section }: Props) {
const { ut } = useUsenuviLocale()
const headline = ut(section.id, 'headline', section.settings.headline)
return <h1>{headline}</h1>
}
{
"mybrand": {
"hero-1": {
"headline": "Her türlü web sitesi inşa et",
"subheading": "AI destekli, tek tıkla kurulum."
}
}
}
ut() çağrısı aktif locale'de mybrand.<sectionId>.<field>'a bakar; override yoksa JSON config'teki değere düşer. Yazarlar koda dokunmadan dil bazlı override yapabilir.
NOTE
Block listesi için anahtar index içerir: blocks.0.title, blocks.1.title. İç içe array'lerde aynı kalıp — columns.2.links.4.label.
TEMALAR
Test + yayınlama
Lokal önizlemeden canlı tenant'a.
NOTE
Bugün bir Nuvi CLI'sı ve ayrı bir tema paket formatı yok. Temalar monorepo içinde yaşar, medusa + tenant-front Docker imajlarının parçası olarak gönderilir. Bir @nuvi/theme-* paket modeli ve installer CLI yol haritasında — henüz başlamadı.
cd bootstrap && docker compose up -d --build medusa tenant-front — ikisi de değişikliklerinle yeniden kurulur (medusa yeni JSON + admin şemayı, tenant-front komponentleri + Zod şemayı alır).
http://localhost:3000'a gir — temanın render olur. Next.js dev mod tenant-front için hot-reload yapar; modül paketindeki değişiklik için container rebuild gerekir.
Canlı tenant'a yayınlama
Monorepo'ya PR aç. CI tenant-front + medusa için tsc --noEmit çalıştırır, path'leri değişen servisler için image build başlatır.
main'e merge et. GitHub Actions yeni ghcr.io/<org>/medusa ve ghcr.io/<org>/tenant-front imajlarını push'lar.
Production sunucudaki deploy.sh bu imajları çeker, her tenant'ın container'larını yeniden oluşturur (örn. medusa-usenuvi, front-usenuvi).
Tenant'ta: Ayarlar → Temalar → Mybrand → Etkinleştir. Tema JSON'u yeni medusa imajında bundle'lı olduğu için galeride otomatik görünür.
WARNING
Mevcut bir mağazada tema değiştirmek hangi bölümlerin geçerli olduğunu değiştirir — eski temanın bölümleri /store/theme API katmanında filtrelenir. Yoğun admin özelleştirmesi varsa, geçişten önce açıkça belgelenmeli ya da migrate edilmeli.
Şu an OLMAYANLAR
nuvi theme dev / nuvi theme build CLI yok — bugün Next.js dev server + docker compose kullanılıyor.
create-nuvi-theme generator yok — yeni tema başlatmanın yolu starter'ı ya da en yakın production temayı kopyalamak.
Tema marketplace'i yok — temalar şu an repo katkısı şeklinde.
@nuvi/module-sdk paketi var (modül yazarları için manifest validator + tipler) ama bu kütüphane, CLI değil.
Bir CLI / paket modeli senin için engelliyse, iletişim sayfasında haber ver — yol haritası önceliğini etkiler.
AI
Özel bir AI yeteneği yazmak
Skill tanımla, kaydet, agent ile gönder.
AI asistan, Skill'ler etrafında kuruludur — bir ad, açıklama, Gemini function declaration'ları, executor eşleştirmesi ve system prompt parçasını paketleyen Python nesneleri. Repo, bootstrap/nuvi-agent/app/skills/ altında SEO, tema editörü, pazarlama, dropshipping ve daha fazlasını kapsayan ~24 skill taşır. Yenisini eklemek tek dosya ve tek satır kayıt.
1. Yeni dosya bırak
bootstrap/nuvi-agent/app/skills/<skilladi>.py oluştur. Temiz bir referans olarak seo_auditor.py'yi kullan. Skill instance'ını oluştur ve üzerinde register_skill() çağır:
declarations — app/tools/declarations.py'dan Gemini function declaration tuple'ı.
tool_map — her declaration adını (executor, action)'a eşler; agent bir fonksiyon çağrısını doğru backend'e (medusa.read, medusa.write, theme, vb.) böyle yönlendirir.
system_prompt + localized_prompts — bu skill aktifken model'in system instruction'ına eklenir.
required_modules — listede yer alan herhangi bir modül mağaza için kapalıysa skill UI'da gizlenir.
model_tier — günlük hızlı işler için "flash", daha ağır akıl yürütme için "pro".
3. Import edilebilir yap
Skill'ler import sırasında register_skill() ile kaydolur. Dosyanı app/skills/__init__.py'dan import et (mevcut skill'ler aynı kalıbı izler) — agent boot ederken yüklensin.
4. Lokal stack'te test et
cd bootstrap && docker compose up -d --build nuvi-agent — yeni dosya ile Python imajı tekrar kurulur.
Medusa admin'de AI asistanı aç (http://localhost:9000/app/ai) ve skill'in seçicide göründüğünü doğrula.
Bir quick action tetikle; docker compose logs -f nuvi-agent'tan function call dispatch satırını izle.
NOTE
Yeni executor action'ları (medusa_executor.py ve theme_executor.py'nin sağladıklarının dışında her şey) executor tarafında eşleşen handler ister. Skill'ler kabloyu çeker; yetenekler executor'larda yaşar.
SDK
JS SDK kullanımı
Tarayıcı + Node için storefront client.
Resmi Nuvi JS SDK storefront API'yi tipli metodlarla sarar. Kur:
Çoğu B2B satıcı fiyatı açıkça listelemez — pazarlık eder. @nuvi/module-quote paketi tüm teklif-tahsilat döngüsünü sarar: genel talep endpoint'i, admin inceleme ekranı, markalı PDF'ler ve e-posta gönderimi. Veri modeli packages/modules/quote/src/module/models/ altında quote, quote-item, service-template, service-category ve isg-config olarak yaşar.
1. Müşteri talebi gönderir
Herhangi bir sayfaya teklif-talep formu koy (modül quote-request storefront bölümünü hazır verir) ve genel endpoint'e POST at. Handler ad, e-posta ve telefonu sunucuda doğrular, fiyatları hizmet şablonlarından yeniden hesaplar, benzersiz quote_number üretir ve quoteRequestWorkflow ile taslağı kaydeder.
Admin /app/quotes'u açar, satır kalemlerini düzenler ve durumu günceller (draft → sent → accepted). Yaşam döngüsü durumları quote modelindeki status enum'unda: draft, sent, accepted, rejected, expired. Süre valid_until ile yönetilir.
3. PDF + e-posta gönderimi
GET /admin/quotes/:id/pdf markalı PDF'i render eder. Mağaza-tarafı paylaşım linki workflow'da üretilen imzalı pdf_token'u kullanır — müşteri giriş yapmadan teklifini çekebilir. Bildirim alıcıları POST /admin/quotes/notification-recipients ile yapılandırılır.
4. ISG fiyatlandırma — çalışılmış örnek
Türk İSG firmaları çalışan sayısı ve tehlike sınıfına göre ücretlendirir. isg-config modeli service_durations, fulltime_thresholds, nurse_durations ve default_tax_rate'i tutar. Talep risk_class + employee_count içeriyorsa rota calculateIsgPrice()'ı sunucu tarafında çağırır — frontend fiyatları yok sayılır. Matrisi /app/services'ten ya da PUT /admin/quotes/isg-config ile düzenle.
5. Teklif-tahsilat devri
Teklif accepted olduğunda, quote.metadata ile Medusa order veya subscription'a bağla. Yaygın desen: quote_items'ı taslak order satırlarına kopyala, sonra Stripe / Iyzico ödeme linki gönder. Tekrarlanan servis sözleşmeleri — kabul edilen teklifi subscription modülünün subscription_plan'ı ile eşleştirip aylık tahsilata geç.
WARNING
Store endpoint ISG fiyatlarını her zaman DB'deki isg-config'den yeniden hesaplar. Client'tan gelen unit_price'a güvenme — handler sadece ISG-dışı satırlarda ipucu olarak kullanır.
TARİFLER
Abonelik ticareti — tekrarlayan tahsilat
Planlar, deneme süresi, dunning, yaşam döngüsü ve müşteri portalı — Stripe bağlı.
@nuvi/module-subscription paketi Medusa üzerine tekrarlayan tahsilat, müşteri portalı ve yaşam döngüsü hook'ları ekler. Her şeyi üç model yönetir: subscription_plan, customer_subscription ve subscription_payment (geçmiş). Stripe varsayılan sağlayıcı — plan üzerindeki stripe_price_id iki sistemi bağlar.
1. Plan oluştur
Planlar /app/subscriptions'tan ya da POST /admin/subscriptions/plans ile yönetilir. interval enum'u daily | weekly | monthly | yearly, interval_count ile çarpılır. trial_days deneme süresini, billing_cycles toplam yenileme sayısını sınırlar (sürekli için null).
Storefront POST /store/subscriptions/checkout'a plan_id, success_url, cancel_url ile çağrı yapar. Handler yönlendirme URL'lerini STORE_CORS / ADMIN_CORS'a karşı doğrular (açık-yönlendirme koruması) ve Stripe Checkout Session açar. Ödeme sonrası Stripe webhook'u customer_subscription kaydını trialing veya active durumunda ekler.
3. Deneme ve dunning
Müşteri deneme süresindeyken trial_end set edilir. Başarısız yenilemelerde durum past_due'a döner ve dunning politikası (varsayılan Stripe smart retries) devreye girer. Tükendiğinde durum canceled ya da expired olur.
4. Yaşam döngüsü endpoint'leri — müşteri portalı
Müşteri portalı dört yaşam döngüsü rotasını çağırır:
Müşteri-tarafı rotalar
POST /store/subscriptions/:id/pause
POST /store/subscriptions/:id/resume
POST /store/subscriptions/:id/change-plan { plan_id }
POST /store/subscriptions/:id/cancel { at_period_end?: boolean }
at_period_end: true ile iptal kayıtta cancel_at_period_end'i set eder — abonelik current_period_end'e kadar active kalır, sonra canceled'a döner. Pause paused_at'ı set eder; resume temizler ve sonraki tahsilat tarihini yeniden verir.
5. Yenileme webhook'ları
Her başarılı yenileme bir subscription_payment kaydı yazar. Modülün event bus'ında subscription.payment.succeeded olayını dinleyerek kendi mantığını ekle — e-posta gönder, kullanım kredisi yükle ya da kendi ledger'ını mutabakat et.
6. Ürünlerle eşleme
subscription_plan.product_id Medusa ürününü işaret edebilir, böylece aynı SKU hem tek seferlik hem tekrarlayan satışı destekler. Plan'ın features JSON sütunu serbest bir torba — site_limit, mailbox_limit vb. — uygulamanın diğer yerlerinde yetkilendirme kontrolü için kullanılır.
TIP
Webhook imza doğrulaması fail-closed. STRIPE_WEBHOOK_SECRET set değilse rota 500 döner. Staging dahil her ortamda ayarla.
TARİFLER
Travel modülü ile tur platformu
Turlar, destinasyonlar, rezervasyonlar ve yorumlar — travel teması ile.
@nuvi/module-travel Nuvi'yi tur operatör platformuna dönüştürür. Veri modeli tur işletmelerinin düşünüş şeklini yansıtır: bir touritinerary günlerine, included / excluded dizilerine, highlights'a, rehber bloğuna ve fiyatlandırmaya sahiptir. Turlar destination kayıtlarına (çoktan-çoğa, tour-destination üzerinden) ve tour-category taksonomisine bağlanır.
1. Tour varlığı
packages/modules/travel/src/module/models/tour.ts içinde tanımlı. Önemli alanlar: slug (mağaza başına benzersiz), duration_days, max_group_size, price_from, price_per_guest ve itinerary, included, excluded, quick_facts için zengin JSON sütunları. Durum enum'u draft | published | archived.
2. Destinasyonlar ve kategoriler
Destinasyonlar tekrar kullanılabilir yerler — Kapadokya, Patagonya, Fuji. Genel listeleme GET /store/destinations, detay GET /store/destinations/:slug. Kategoriler (şehir turu, macera, aile) kenar çubuğu filtrelerini GET /store/tour-categories ile besler.
3. Rezervasyon akışı
Rezervasyonlar Medusa sepetini yeniden kullanır. Adanmış cart line endpoint'i ile bir tur ekle:
Satır kalemi tur verisini standart Medusa checkout'undan geçirir — ödeme, adres, onay. Sipariş verildikten sonra siparişin metadata'sını tur kaydına bağlayıp rezervasyon onayını render edebilirsin.
4. Yorumlar
Storefront onaylanmış yorumları GET /store/tours/:slug/reviews ile çeker. Handler yalnızca status: 'approved' filtreler — taslak ya da reddedilmiş yorumlar asla sızmaz. Tour üzerindeki rating ve review_count moderasyon akışıyla güncellenen denormalize toplamlardır.
5. Admin tarafında içerik
/app/travel editörü sağlar. Slug benzersizliği GET /admin/tours/check-slug ile kontrol edilir. Fiyatlandırma ve kalkış-bazlı override'lar /admin/tours/:id/pricing altında; moderasyon kuyruğu /admin/tours/:id/reviews'da.
6. Travel teması ile eşleme
Travel teması bu veriye uyan bölüm bileşenlerini hazır verir: destinasyon aramalı hero, tour grid, gün-gün itinerary, dahil/dahil-değil checklist'leri ve rehber kartı. bootstrap/tenant-front/src/lib/section-map.ts'e kaydet — manifest'te listelenen tour-grid bölümü editörde görünür.
NOTE
Reviews endpoint'i sayfa boyutunda 1–100 sınırı ve varsayılan 20 uygular — storefront tarafında dump-all yerine sonsuz kaydırma kullan.
TARİFLER
Etkinlik + biletleme platformu
Etkinlik, bilet türleri, kayıtlar, katılımcı listesi ve müşteri portalı.
@nuvi/module-events etkinlik yayını ve biletleme verir. Üç temel model: event (etkinlik), event-ticket-type (fiyat seviyeleri + kapasite) ve event-registration (satılan koltuk). Storefront genel etkinlik sayfasını görür; admin katılımcı listesi, satış raporu ve check-in görür.
1. Event varlığı
packages/modules/events/src/module/models/event.ts içinde tanımlı. Model zamanlama (starts_at, ends_at, timezone, is_all_day), mekan (venue_name, venue_address, venue_city), online_url ile online flag ve üst-seviye capacity taşır. Durum: draft | published | cancelled | completed. registration_enabled sayfayı yayından kaldırmadan bilet satışını kapatır.
2. Bilet türleri
Her etkinlik birden çok event_ticket_type'a sahip: Early Bird, Genel Giriş, VIP. Her seviyenin price, capacity ve registered_count'u stok hesaplar. sales_start_at ve sales_end_at satış pencerelerini uygular. Store-tarafı endpoint türetilmiş flag'leri storefront yerine kendisi hesaplar:
Sipariş tamamlandığında modül her bilet için benzersiz confirmation_number ile bir event_registration oluşturur. Durum akışı: pending_payment → confirmed → checked_in (ya da cancelled).
4. Katılımcı listesi ve giriş
Admin katılımcı listesini GET /admin/events/:id/registrations, satış toplamlarını GET /admin/events/:id/stats ile çeker. Kapı görevlileri check-in endpoint'ine basar — durum checked_in'e döner ve checked_in_at damgalanır.
5. Müşteri portalı — biletlerim
Giriş yapmış müşteriler tüm biletlerini — geçmiş ve gelecek — tek endpoint'ten görür:
Email-marketing modülü ile birleştirip katılımcılara mesaj at: event_registrations'ı event_id ve status: 'checked_in' ile filtrele, sonra kayıt linki veya geri bildirim anketi gönder. Etkinliği iptal etmek için status'u cancelled'a çevir — my-registrations endpoint'i yeni durumu anında yansıtır.
TIP
my-registrations rotası fail-closed: istek auth_context.actor_id taşımıyorsa 401 döner, asla boş liste değil. Client'ta her zaman authenticated fetch ile sar.
Endpoint detayları mı arıyorsun?
Tam REST API referansı her storefront ucunu method etiketi, istek şekli ve cURL örnekleri ile kapsar.