Modern teknolojiler, edge computing ve AI-destekli geliştirme ile kurumsal düzeyde bir membership platformu.
Bu yazı, Astro 5, Paddle ve Cloudflare Pages ile nasıl ürünleştirilebilir bir membership platformu inşa edileceğini anlatmaktadır. Astro’nun static site generation gücünü, Paddle’ın subscription yönetimini ve Cloudflare’in edge computing altyapısını bir araya getirerek performans, güvenlik ve geliştirici deneyimini optimize eden bir mimari sunuyorum.
Amacım, tek başıma geliştirdiğim ve geliştirme sürecinde Claude Code, içeriklerin Grav CMS’den Astro’da uygun hale getirilmesinde LM Studio aracılığı ile local olarak kullandığım Qwen ile birlikte yönettiğim bu sistemi, benzer ihtiyaçları olan geliştiriciler için yeniden kullanılabilir bir şablona dönüştürmek. Geleneksel blog veya CMS’lerin ötesinde, tier-bazlı erişim kontrolü, premium içerik gating ve otomatik webhook işleme gibi kurumsal özellikler barındırır.
Neden Bu Mimari?
Modern bir içerik platformu kurarken üç temel zorlukla karşılaşırsınız:
- Performans vs Dinamik İçerik: Statik siteler hızlıdır ama üyelik sistemleri dinamik bileşenler gerektirir
- Ödeme Entegrasyonu: Subscription yönetimi, webhook’lar, fatura ve vergi karmaşıklığı
- Global Dağıtım: Düşük latency, CDN ve edge computing gereksinimleri
Bu mimari, bu problemleri hibrit bir yaklaşımla çözer:
- Statik sayfalar Astro ile build-time’da oluşturulur (hız)
- Dinamik API’ler Cloudflare Pages Functions ile edge’de çalışır (esneklik)
- Subscription mantığı Paddle’a devredilir (karmaşıklık dışarıda)
- Veritabanı Supabase ile yönetilir (gerçek zamanlı, güvenli)
Teknoloji Stack’i
Frontend ve Build
| Teknoloji | Versiyon | Kullanım Amacı |
|---|---|---|
| Astro | 5.15.2 | Static Site Generator + Edge Functions |
| TypeScript | 5.9.3 | Type-safe geliştirme |
| TailwindCSS | 3.4.17 | Utility-first CSS |
| MDX | 4.3.9 | Markdown + JSX bileşenleri |
| Pagefind | 1.4.0 | Client-side arama indeksleme |
Backend ve Servisler
| Teknoloji | Kullanım Amacı |
|---|---|
| Supabase | PostgreSQL veritabanı, authentication, real-time |
| Paddle | Subscription yönetimi, ödeme, fatura |
| Cloudflare Pages | Hosting, edge functions, global CDN |
| Cloudflare KV | Key-value cache (arama, premium içerik) |
| Cloudflare R2 | Object storage (görseller, assets) |
Proje Yapısı
ceaksan-v4.0/
├── src/
│ ├── components/ # 68+ Astro bileşeni
│ │ ├── pages/ # Sayfa-spesifik bileşenler
│ │ ├── ui/ # UI component library
│ │ └── sections/ # Yeniden kullanılabilir bölümler
│ ├── content/ # Astro Content Collections
│ │ ├── posts/ # Blog yazıları (MDX)
│ │ └── courses/ # Video kursları
│ ├── lib/ # 52 library modülü
│ │ ├── paddle/ # Paddle entegrasyonu
│ │ ├── supabase.ts # Server-side client
│ │ └── supabase-browser.js # Browser client
│ ├── pages/ # File-based routing
│ └── styles/ # Global stiller
├── functions/ # Cloudflare Pages Functions
│ └── api/ # 15 API endpoint
├── .claude/ # Claude Code entegrasyonu
│ ├── commands/ # Slash commands
│ ├── subagents/ # Specialized agents
│ └── hooks/ # Automatic hooks
└── public/ # Static assets
Astro Konfigurasyonu
Astro 5 ile çalışırken kritik bir kural var:
Asla output: 'hybrid' kullanmayın. Çünkü, bu mod Astro 5 ile artık mevcut değil.
Cloudflare Free Tier Challenge
Bu projede önemli bir karar noktası vardı: Cloudflare’in resmi Astro adapter’ını kullanmamak. Neden?
Cloudflare Pages’in free tier’ı belirli build limitlerine sahip. @astrojs/cloudflare adapter’ı kullanıldığında, adapter’ın ihtiyaç duyduğu bağımlılıklar ve build süreci bu limitleri zorluyordu. Detaylar için Cloudflare’in Astro rehberine bakılabilir.
Adapter kullanmadan çözüm:
// astro.config.mjs
export default defineConfig({
output: 'static', // KRİTİK: Her zaman 'static'
i18n: {
defaultLocale: 'tr',
locales: ['tr', 'en'],
routing: {
prefixDefaultLocale: true // /tr/page ve /en/page
}
}
// NOT: Cloudflare adapter KULLANILMIYOR
});
Bu Yaklaşımın Artıları ve Eksileri
Artıları:
- Cloudflare free tier içinde kalabilme
- Daha küçük build çıktısı
- Daha hızlı build süreleri
- Daha az bağımlılık
Eksileri:
- Cloudflare-spesifik özellikler (Image CDN, vb.) doğrudan kullanılamıyor
- Server-side rendering için manuel
functions/dizini yönetimi - Adapter’ın sağlayacağı otomatik optimizasyonlardan mahrum kalma
Dinamik Sayfalar
Dinamik sayfalar için functions/ dizininde Cloudflare Pages Functions kullanılır:
// functions/api/example.ts
export const onRequest: PagesFunction = async (context) => {
// KV, R2, D1 erişimi burada mümkün
return new Response(JSON.stringify({ data }));
};
Astro sayfaları için ise sayfa bazında prerender devre dışı bırakılabilir:
// Dinamik sayfa örneği
export const prerender = false;
Bu yaklaşımın avantajları:
- Statik sayfalar build-time’da oluşturulur (hızlı, ucuz)
- Dinamik sayfalar edge’de çalışır (KV, R2 erişimi)
- Bundle boyutu optimize edilir (gereksiz SSR kodu yok)
- Free tier limitleri içinde kalıyor
Paddle Subscription Entegrasyonu
Tier Sistemi
Platform üç katmanlı bir üyelik sistemi sunar:
| Tier | Erişim |
|---|---|
| Insider | İçerikler |
| Maker | İçerik + Dosyalar + Kodlar |
| Master | Her Şey + Kurslar |
Kritik Kural: Kurs erişimi sadece Master tier ile mümkündür. Bu, price_id bazlı doğrulanır—tier ismi değil. Çünkü, zaman içerisinde tier adı değişebilir ancak Paddle tarafında price_id korunur.
Webhook İşleme
Paddle webhook’ları /api/paddle/webhook endpoint’inde işlenir:
// Webhook event types
- subscription.created // Yeni abonelik
- subscription.updated // Plan değişikliği
- subscription.canceled // İptal
- transaction.completed // Ödeme onayı
Her webhook event’i:
- İmza doğrulaması yapılır
webhook_eventstablosuna loglanırsubscriptionstablosu güncellenir- Idempotency için
event_idkontrol edilir
Subscription Güncelleme Mantığı
// Upgrade: Anında, prorated
proration_billing_mode: 'prorated_immediately'
// Downgrade: Sonraki dönem
proration_billing_mode: 'prorated_next_billing_period'
// İptal: Dönem sonuna kadar erişim devam eder
effective_from: 'next_billing_period'
Supabase Veritabanı Mimarisi
Supabase, platformun temel veritabanı altyapısını oluşturur:
- Müşteri yönetimi: Kullanıcı bilgileri ve Paddle entegrasyonu
- Abonelik takibi: Subscription durumu, plan bilgisi, faturalama
- Kurs kayıtları: Enrollment yönetimi ve erişim kontrolü
Row Level Security (RLS) ile her tablo güvenlik altında. Kullanıcılar sadece kendi verilerine erişebilir.
Erişim Kontrolü Akışı
1. Kullanıcı premium içeriğe erişmek ister
2. Auth check → Supabase session
3. Tier validation → price_id bazlı kontrol
4. Sonuç: İçerik göster veya paywall
Cloudflare Entegrasyonu
KV Namespaces (6 adet)
| Namespace | Kullanım |
|---|---|
| PREMIUM_CONTENT | Premium post içerikleri |
| COURSE_CONTENT | Kurs içerikleri |
| ANALYTICS | Kullanım metrikleri |
| FEATURE_FLAGS | Özellik bayrakları |
| RATE_LIMITER | Rate limiting |
| SESSION | Session yönetimi |
R2 Object Storage
Bucket: your-assets
URL: https://assets.your-domain.com
Kullanım: Görseller, dosyalar, medya
API Endpoints
Platform, Cloudflare Pages Functions üzerinde çalışan API’ler içerir:
- Paddle entegrasyonu: Webhook işleme, subscription yönetimi
- İçerik erişimi: Premium içerik ve kurs delivery
- Fiyatlandırma: Dinamik fiyat bilgisi
Pagefind ile Arama
Algolia veya ElasticSearch yerine Pagefind tercih edildi:
- Client-side arama: Sunucu maliyeti yok
- Build-time indeksleme: Otomatik
- Çok dilli destek: TR ve EN ayrı indeksler
- Küçük bundle: ~100KB
// Build sonrası otomatik indeksleme
// dist/pagefind/ klasöründe oluşturulur
// Arama kullanımı
const pagefind = await import('/pagefind/pagefind.js');
await pagefind.init();
const results = await pagefind.search('astro');
Son güncelleme ile Pagefind araması header’daki arama butonuna bağlanmış durumda. Kullanıcı arama butonuna tıkladığında Pagefind modal’ı açılır ve dil bazlı filtreleme otomatik olarak uygulanır.
Premium İçerik Gating
Frontmatter ile Tier Belirleme
---
title: "Premium İçerik"
requiredTier: ['insider'] # Array formatında
member: true
---
Client-Side Access Check
import { checkContentAccess } from '@/lib/content-access-client.js';
const hasAccess = await checkContentAccess(getSupabaseClient, {
requiredTier: ['insider'],
onAccessGranted: () => showContent(),
onAccessDenied: () => showPaywall()
});
Premium İçerik Delivery
Premium içerikler client-side render edilmez. API üzerinden fetch edilir:
// /api/content/premium-post
// 1. Auth check
// 2. Tier validation
// 3. KV'den içerik fetch
// 4. Render edilmiş HTML döndür
i18n (Çok Dilli Destek)
URL-Based Routing
/tr/contents/ → Türkçe içerikler
/en/contents/ → English contents
Dil Algılama
const lang = window.location.pathname.startsWith('/en') ? 'en' : 'tr';
const text = lang === 'tr' ? 'Türkçe Metin' : 'English Text';
Content Collections
src/content/posts/2026/01/01.slug/
├── tr.mdx # Türkçe versiyon
└── en.mdx # İngilizce versiyon
Claude Code Entegrasyonu
Verification-First Approach
Give Claude a way to verify its work — Boris Mann
Bu prensip, Claude Code ile geliştirmenin temelini oluşturur. Her değişiklik doğrulanabilir olmalı.
Slash Commands
Projede tanımlı slash command’lar:
/test-and-build - Testleri çalıştır, build yap
/verify-changes - Kapsamlı doğrulama
/commit-push-pr - Commit + Push + PR oluştur
/preview-deploy - Preview ortamına deploy
/kv - KV yönetimi
Custom Skills
.claude/commands/ klasöründe tanımlı skill’ler:
- test-and-build.md: Build ve test otomasyonu
- verify-changes.md: Değişiklik doğrulama
- preview-deploy.md: Preview deployment
- kv.md: KV namespace yönetimi
Her skill, Claude Code’un belirli görevleri tutarlı ve doğrulanabilir şekilde yapmasını sağlar.
Hooks
Otomatik çalışan hook’lar:
.claude/hooks/
├── PostToolUse.sh # Her kod değişikliğinde formatting
└── AgentStop.sh # Her agent bitişinde doğrulama
CLAUDE.md Kuralları
Projeye özel kurallar CLAUDE.md dosyasında tanımlı. Bu dosya Claude Code’un proje kurallarını anlamasını sağlar:
## KRİTİK KURALLAR
### NO HARDCODED VALUES
// YANLIŞ:
const price = '$49.99'
const planName = 'Tier 2 - Profesyonel'
// DOĞRU:
API'den dinamik fetch
Database'den gerçek değerler
Bu yaklaşım, hardcoded değerlerin koda sızmasını engeller ve tutarlı bir geliştirme deneyimi sağlar.
Build ve Deployment
npm Scripts
# Development
npm run dev # Dev server
# Build
npm run build # Production build
npm run build:kv-data # Search KV data
npm run build:premium-kv # Premium content KV
# Deployment
npm run deploy:pages # Cloudflare Pages
npm run preview # Local preview
Build Süreci
1. Astro build → dist/
2. Pagefind indeksleme → dist/pagefind/
3. KV data generation (opsiyonel)
4. Cloudflare Pages deployment
Performans Optimizasyonları
Bundle Optimization
// Vite config
- Manuel chunk splitting (supabase, paddle ayrı)
- Asset inline limit: 0 (asla inline etme)
- CSS immutable (1 yıl cache)
Image Optimization
- Sharp ile build-time optimizasyon
- R2 CDN delivery
- Lazy loading
- WebP/AVIF formatları
KV Caching
- Arama içeriği: PREMIUM_CONTENT içinde
- Premium postlar: PREMIUM_CONTENT
- Kurs içerikleri: COURSE_CONTENT
Güvenlik
Headers
Content-Security-Policy: [detaylı CSP]
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Rate Limiting
// RATE_LIMITER KV namespace
// API endpoint bazlı throttling
Webhook Signature Verification
// Paddle webhook'ları imza ile doğrulanır
const isValid = verifyPaddleSignature(request, secret);
Metrikler
| Metrik | Değer |
|---|---|
| Astro Version | 5.15.2 |
| Components | 68+ |
| API Endpoints | 15 |
| KV Namespaces | 6 |
| Dil Desteği | 2 (TR, EN) |
| Subscription Tiers | 3 |
| Build Output | ~200MB |
Sonuç
Bu mimari, modern web teknolojilerinin bir araya geldiği, ürünleştirilebilir bir membership platformu şablonu sunmaktadır:
- Performans: Astro SSG + Cloudflare edge
- Esneklik: Hybrid rendering, dinamik API’ler
- Ölçeklenebilirlik: KV caching, R2 storage
- Güvenlik: CSP, rate limiting, webhook verification
- Geliştirici Deneyimi: Claude Code, slash commands, hooks
- Maliyet Optimizasyonu: Cloudflare free tier limitleri içinde
Cloudflare free tier’ında kalarak maliyet-verimli bir çözüm oluşturmak mümkün. Ancak bu, resmi adapter’ın bazı avantajlarından vazgeçmeyi gerektiriyor. Bu nedenle, projeyi ürünleştirdiğimde iki farklı paket halinde sunacağım. Gelişmelerle ve güncellemelerle ilgili takipte kalmak isterseniz X üzerinden beni takip edebilirsiniz.
Özet geçmek gerekirse, Ne yaptım? Blog kurmadım, yeniden kullanılabilir bir platform şablonu oluşturdum. Kime hitap ediyor? Benzer bir membership sistemi kurmak isteyen geliştiricilere. Neden farklı? Çünkü performans, güvenlik ve maliyet dengesi aynı anda düşünüldü.