Skip to content

Repository files navigation

NOUS

Düşüncenin Haritası — A Map of Thought

İnsanların felsefeyi interaktif bir bilgi grafiği üzerinden keşfetmesini sağlayan iki dilli (TR/EN) bir web platformu. Bir ansiklopedi değildir — bir düşünce navigasyon aracıdır.

286 düğüm (50 filozof, 78 kavram, 22 akım, 81 eser, 50 soru, 5 dönem) ve 692 ilişki içeren canlı bir bilgi grafiği üzerine kurulmuştur.


Teknoloji Stack'i

Katman Teknoloji
Framework Next.js 16 (App Router, Turbopack)
Dil TypeScript (strict)
Stil Tailwind CSS v4 (@theme)
Graph @xyflow/react (React Flow) + @dagrejs/dagre layout
Animasyon Framer Motion
State Zustand
i18n next-intl (TR varsayılan, EN)
Arama Fuse.js (client-side fuzzy search)
Veritabanı Supabase (PostgreSQL)
ORM Prisma 6
Auth Supabase Auth (yalnızca admin)
Hosting Vercel

Proje Yapısı

src/
├── app/
│   ├── [locale]/                  # i18n public site
│   │   ├── page.tsx               # Ana sayfa (hero + arama + Düşünceye Dal)
│   │   ├── explore/               # /explore — interaktif graph
│   │   ├── timeline/              # /timeline — yatay zaman çizelgesi
│   │   ├── {philosopher,concept,school,work,question,period}/[slug]/
│   │   └── about/
│   ├── admin/                     # Korumalı admin paneli
│   │   ├── login/
│   │   └── (protected)/{nodes,edges}/
│   ├── api/
│   │   ├── graph/                 # Trim'lenmiş graph payload (~82 KB)
│   │   ├── search-index/          # Sadece isim+slug+tip (~26 KB)
│   │   └── random-question/
│   ├── robots.ts
│   └── sitemap.ts                 # 580+ URL, hreflang dahil
├── components/{graph,detail,home,layout,search,timeline,ui}/
├── lib/
│   ├── db.ts                      # Prisma client singleton
│   ├── supabase-{server,browser}.ts
│   ├── queries.ts                 # getNodeBySlug (React.cache)
│   ├── seo.ts                     # buildNodeMetadata + buildNodeJsonLd
│   ├── graph-layout.ts            # dagre wrapper
│   └── timeline.ts                # lane-packing algoritması
├── store/{graphStore,filterStore}.ts
├── i18n/{routing,navigation,request}.ts
├── messages/{tr,en}.json
├── proxy.ts                       # next-intl + Supabase auth gate
└── types/{node,edge,graph}.ts

prisma/
└── schema.prisma                  # nodes + edges (UUID PK, JSONB metadata)

seed/
├── {philosophers,concepts,schools,works,questions,periods}.json
├── edges.json
└── seed.ts                        # tsx ile çalışır

Kurulum

1) Bağımlılıklar

npm install

2) Supabase kurulumu — sıfırdan

NOUS, kendi Supabase projeni gerektirir. Hazır bir DB ile gelmez. Aşağıdaki adımlar tamamen ücretsiz tier üzerinde tamamlanır.

2.1 Proje oluştur

  1. supabase.comStart your project → GitHub ile giriş
  2. New Project:
    • Name: nous (veya istediğin)
    • Database Password: güçlü bir şifre üret, kaydet (sonra .env'e koyacaksın)
    • Region: kullanıcılarına yakın olanı seç (TR için eu-central-1 / Frankfurt önerilir — Tokyo gibi uzak region'lar latency'yi 10×'lar)
    • Pricing Plan: Free (yeterli — 500 MB DB, 2 GB bandwidth)
  3. Proje oluşması ~2 dk sürer.

2.2 Connection bilgilerini topla

Supabase Dashboard'da projene gir, Project Settings (alt sol gear ikonu):

Settings → API:

  • Project URL → kopyala (örn. https://abcd1234.supabase.co) — bu NEXT_PUBLIC_SUPABASE_URL olacak
  • Project API keys → anon / public → kopyala (sb_publishable_... ile başlar) — bu NEXT_PUBLIC_SUPABASE_ANON_KEY olacak

Settings → Database → Connection string:

  • Tab "URI" → "Connection pooling" alt menüsünü göster
  • Mode: Transaction seçili olsun
  • String'i kopyala (örn. postgresql://postgres.abcd1234:[YOUR-PASSWORD]@aws-0-eu-central-1.pooler.supabase.com:6543/postgres)
  • [YOUR-PASSWORD] yerine 2.1'de kaydettiğin şifreyi koy
  • Sonuna ?pgbouncer=true&connection_limit=1&pool_timeout=30 ekle → bu DATABASE_URL olacak
  • Aynı string'i :6543/postgres yerine :5432/postgres olacak şekilde kopyala, hiçbir parametre eklemeden → bu DIRECT_URL olacak (Prisma migrate'in kullandığı non-pooled bağlantı)

2.3 .env.local oluştur

cp .env.example .env.local

.env.local'i aç, 2.2'de topladıklarınla doldur:

DATABASE_URL="postgresql://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:6543/postgres?pgbouncer=true&connection_limit=1&pool_timeout=30"
DIRECT_URL="postgresql://postgres.<ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres"

NEXT_PUBLIC_SITE_URL="http://localhost:3000"
NEXT_PUBLIC_SUPABASE_URL="https://<ref>.supabase.co"
NEXT_PUBLIC_SUPABASE_ANON_KEY="sb_publishable_..."

⚠️ .env.local asla git'e push edilmez.gitignore engelliyor. Yine de push'tan önce git status ile kontrol et.

connection_limit=1 ve pool_timeout=30 kritik: pgbouncer üzerinden Prisma havuzunu doğru ayarlar. Build sırasında 200+ paralel sayfa rendering connection limit'ini aşmasın.

SUPABASE_SERVICE_ROLE_KEY gerekmez — kod hiç kullanmıyor. RLS bypass etmek istemiyoruz; admin yazımları zaten DATABASE_URL üzerinden Prisma ile geçiyor.

2.4 Şema + Row-Level Security (RLS)

Prisma şemasını gönder:

npm run db:generate    # Prisma client üret
npm run db:push        # nodes + edges tablolarını oluştur

RLS'i mutlaka açanon key browser'a expose ediliyor; RLS yoksa herkes yazabilir:

Supabase Dashboard → Authentication → Policies → her iki tablo (nodes, edges) için:

  1. Enable RLS butonuna bas
  2. "New Policy" → "From scratch" seç
  3. Aşağıdaki politikayı uygula (sadece SELECT izni anon rolüne, INSERT/UPDATE/DELETE hiç kimseye):
-- nodes tablosu için
CREATE POLICY "Public read access" ON nodes
  FOR SELECT USING (true);

-- edges tablosu için
CREATE POLICY "Public read access" ON edges
  FOR SELECT USING (true);

Admin yazımları RLS'i bypass eden Postgres connection string üzerinden geçer — Supabase Auth'la değil. Bu yüzden anon rolüne yazma izni vermene gerek YOK.

2.5 Admin kullanıcısı oluştur

Supabase Dashboard → Authentication → UsersAdd userCreate new user:

  • Email: kendi email'in
  • Password: güçlü bir şifre
  • Auto Confirm User işaretle (email doğrulama gönderilmez)
  • Create user

Bu hesapla /admin/login üzerinden gireceksin. Self-registration yok; ekstra admin gerekiyorsa yine bu yoldan eklersin.

3) Seed verisi

npm run db:seed              # 286 node + 692 edge yükle (idempotent upsert)

Repo'da 50 filozof, 78 kavram, 22 akım, 81 eser, 50 soru, 5 dönem ve 692 ilişkilik bir başlangıç dataset'i var (seed/*.json). İdempotent — script'i tekrar çalıştırırsan veriyi düplike etmez, sadece günceller.

4) Cache'i ilk üretim

Public site DB'ye runtime'da gitmiyordata/ ve public/data/ altındaki statik JSON dosyalarından okuyor. İlk dev başlangıcından önce bir kez:

npm run cache-data           # DB'den çek, JSON dosyalarını üret (~5-20 sn)

Bu adım atlanırsa public sayfaları "missing data/graph.json — run npm run cache-data" hatasıyla açılmaz. Admin paneli her zaman DB'ye direkt yazıyor; cache yenileme admin'in /admin/cache sayfasından da tetiklenebilir.

5) Geliştirme

npm run dev

http://localhost:3000/tr (TR varsayılan) veya http://localhost:3000/en. Admin paneli: http://localhost:3000/admin.


Komutlar

Komut Yapar
npm run dev Dev server (Turbopack)
npm run build Production build (otomatik olarak cache-data çalıştırır)
npm run start Production server
npm run typecheck tsc --noEmit
npm run cache-data DB'den data/*.json ve public/data/*.json üret
npm run db:generate Prisma client üret
npm run db:push Şemayı DB'ye gönder
npm run db:pull Şemayı DB'den çek
npm run db:seed seed/seed.ts çalıştır
npm run db:studio Prisma Studio aç

Veri Modeli

İki tablo, polimorfik:

  • nodestype ile ayrımlı (philosopher / concept / school / work / question / period); ortak alanlar (name_tr/en, description_short_tr/en), tipe-özel alanlar metadata JSONB içinde
  • edges — yönlü, 21 tip (influenced_by, teacher_of, authored, developed, coined, opposes, central_to, active_during, addresses, explores, discusses, …)

Şema detayları: prisma/schema.prisma.


Mimari Notlar

  • HTML/body root layout'ta — public site ([locale]/) ve admin (admin/) tek bir HTML köküne paralel oturur
  • Admin gate iki katmanlı: proxy.ts middleware'inde getUser() + (protected)/layout.tsx içinde defence-in-depth redirect()
  • Graph layout cache: dagre tüm 286 node için useMemo([data]) ile bir kez hesaplanır, filter değişimlerinde sadece hidden flag'i toggle olur (~150 ms → ~10 ms iyileştirme)
  • Detail sayfa cache: getNodeBySlug React.cache() ile sarılı — page + generateMetadata aynı request'te tek sorgu
  • Search index — Fuse.js'i besleyen endpoint sadece name_tr/en + slug + type + importance döner (~26 KB)
  • JSON-LD — her detay sayfası tipine göre Person / Book / DefinedTerm schema.org çıkarır
  • Sitemap — dinamik, hreflang trenx-default üçlüsünü her URL için emit eder

Güvenlik Notları

Deploy etmeden veya repo'yu public yapmadan önce:

  • .env.local git'e kesinlikle gitmemeli.gitignore zaten engelliyor; yine de git ls-files | grep .env ile doğrula. .env.example placeholder'lar içerir, gerçek değer yok.
  • Supabase RLS açık olmalı: kod runtime'da anon key kullanıyor (browser'a expose ediliyor). nodes ve edges tablolarında RLS yoksa veya anon rolüne yazma izni varsa, herkes admin'i bypass edip içerik değiştirebilir.
    • Supabase Dashboard → Authentication → Policies → her iki tablo için "Enable RLS" + sadece SELECT izni ver. INSERT/UPDATE/DELETE'i authenticated role'e bile vermene gerek yok (admin yazımları zaten proxy.ts arkasından server-side Prisma ile geçiyor, ki o DATABASE_URL (Postgres connection string) üzerinden bağlanır — anon key'i kullanmaz).
  • SUPABASE_SERVICE_ROLE_KEY kullanılmıyor: Kodda service_role çağrısı yok. Env'inden kaldırabilirsin; ekleyeceksen yalnızca server-side (asla NEXT_PUBLIC_* prefix'i ile değil).
  • Admin gate iki katmanlı: proxy.ts /admin/*'ı kapı bekçisi, (protected)/layout.tsx defence-in-depth redirect(), her server action requireUser() çağırır.
  • Open-redirect koruması: Login formunda ?next= parametresi sadece /admin prefix'ini kabul eder (safeNext()).
  • XSS hardening: JsonLd komponenti <, >, & karakterlerini unicode escape eder — admin malicious içerik girerse bile script enjeksiyonu olmaz.
  • Güvenlik header'ları: next.config.ts her response'a X-Frame-Options: DENY, X-Content-Type-Options: nosniff, HSTS, Permissions-Policy ekler. CSP yok (Next.js'in inline script bootstrap'ı ile çakışıyor — sonraki iş).
  • robots.txt: /admin ve /api crawlerlardan gizli.
  • Service role exposure check: git grep -i service_role push öncesi boş çıkmalı.

Deploy (Vercel)

  1. Vercel'e bağla — repo otomatik algılanır
  2. Project Settings → Environment Variables — .env.local'daki tüm değişkenleri ekle
  3. NEXT_PUBLIC_SITE_URL'ı production domain'e çevir (ör. https://nous.app)
  4. (Opsiyonel ama önerilen) Project Settings → Git → Deploy Hooks → "Cache regen" adıyla bir hook oluştur, URL'i kopyala, Environment Variables'a VERCEL_DEPLOY_HOOK_URL olarak ekle. Bu, admin panelindeki "Cache'i yenile" butonunun production'da çalışmasını sağlar.
  5. Deploy

Build adımı npx prisma generate çalıştırmak için prisma zaten dependencies altında. prebuild hook'u cache-data'yı tetikler — her build DB'den taze veri çeker.


Cache Akışı

Public site runtime'da DB'ye gitmiyor. Tüm sayfalar build-time'da üretilen statik JSON dosyalarından okuyor. Bu Türkiye-Tokyo latency'sinin (5-17 sn) etkisini sıfırlıyor.

Cache dosyaları:

Dosya Boyut Kullanan
data/graph.json ~380 KB /explore, /timeline, detail sayfaları (server-only)
data/stats.json ~220 B /, /about (counts)
public/data/search-index.json ~44 KB SearchBar (CDN'den fetch)
public/data/questions.json ~7 KB DiveButton (CDN'den fetch)

Yenileme:

Senaryo Yapılacak
Yeni dev makinesi npm run cache-data (ilk başlangıçtan önce bir kez)
Local geliştirme sırasında DB değişti Admin → /admin/cache → "Cache'i yenile" (script local çalışır)
Production'da admin yazımı sonrası Admin → /admin/cache → "Cache'i yenile" (Vercel deploy hook tetikler, ~1-2 dk)
Otomatik tazeleme Vercel Cron'a /admin/cache action'ını günlük çağıran job ekle

Hata davranışı: cache-data DB'ye ulaşamadığında ve disk'te eski cache varsa → eski cache'i koruyup exit 0. Cache yoksa exit 1 (build kırılır). Bu, geçici Supabase outage'lerinde production'ı düşürmemeyi garantiler.


İçerik Genişletme

A) Seed dosyaları üzerinden

  1. seed/<type>.json dosyasına yeni node ekle
  2. seed/edges.json'a en az 1 ilişki ekle (orphan kalmasın)
  3. npm run db:seed çalıştır (upsert — idempotent)

B) Admin paneli

  1. /admin/login — Supabase Auth ile giriş
  2. Düğümler sekmesi → "+ Yeni düğüm" → form
  3. İlişkiler sekmesi → "+ Yeni ilişki" → source/target dropdown + tip

Orphan check admin dashboard'unda görünür.


Lisans

İçerik açık kaynak felsefe literatüründen derlenmiştir. Kod MIT lisansı altındadır.

About

Bilingual TR/EN philosophy knowledge graph — interactive Next.js + React Flow + Supabase exploration tool

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages