Bu kılavuzda, paylaştığınız JavaScript eğitim müfredatını en başından en sonuna kadar derinlemesine inceliyoruz. İstediğiniz gibi süre bilgilerini tamamen kaldırdım, "Önizleme" kısmını atladım, her konuyu bolca günlük hayat örneği ve kod uygulamalarıyla detaylandırdım.


"Öğrenecekleriniz" Paragrafının Açıklaması

Bu kurs, modern bir web uygulamasının hem görünen yüzünü (Front-End) hem de arkada dönen tüm veri ve motor işlerini (Back-End) sıfırdan inşa etmeyi hedefler. Müfredatta belirtilen maddelerin detaylı anlamları şunlardır:


1. Bölüm: The 10 Days of JavaScript: The Language Itself (JavaScript Dilinin Kendisi)

Bu bölümde web dünyasının ana dili olan JavaScript'in temel mekanizmalarını öğrenirsiniz.

Getting Started (Başlangıç)

Functions (Fonksiyonlar / İşlevler)

·        function selamlamaVer(isim) {

·            return "Merhaba " + isim + ", hoş geldin!";

·        }

·        console.log(selamlamaVer("Ahmet")); // Çıktı: Merhaba Ahmet, hoş geldin!

Objects (Objeler / Nesneler)

·        let kedi = {

·            isim: "Pamuk",

·            renk: "Beyaz",

·            miyavla: function() {

·                return "Miyavvv!";

·            }

·        };

·        console.log(kedi.isim); // Pamuk

·        console.log(kedi.miyavla()); // Miyavvv!

Arrays & Arrays Exercise (Diziler ve Dizi Egzersizi)

·        let meyveler = ["Elma", "Armut", "Muz"];

·        console.log(meyveler[0]); // Elma (Sayım sıfırdan başlar)

Making Decisions (Karar Verme Yapıları)

·        let yas = 20;

·        if (yas >= 18) {

·            console.log("Ehliyet alabilirsin.");

·        } else {

·            console.log("Ehliyet almak için büyümelisin.");

·        }

Higher-Order Functions (Yüksek Mertebeden Fonksiyonlar)

·        let sayilar = [1, 2, 3];

·        // map bir yüksek mertebeden fonksiyondur, içine başka bir fonksiyon alır

·        let ikiKati = sayilar.map(function(sayi) {

·            return sayi * 2;

·        });

·        console.log(ikiKati); // [2, 4, 6]

Returning vs Mutating (Geriye Değer Döndürmek vs Orijinali Değiştirmek)

·        let renkler = ["Kırmızı", "Mavi"];

·        renkler.push("Yeşil"); // Mutating: Orijinal diziyi değiştirdi.

Scope & Context (Kapsam ve Bağlam - Bölüm 1 ve 2)

·        function kapsamTesti() {

·            let gizliKelime = "Sır"; // Sadece bu fonksiyon içinden erişilebilir

·        }

·        // console.log(gizliKelime); // Hata verir! Dışarıdan okunamaz.

Miscellaneous Info (Çeşitli Bilgiler - Bölüm 1 ve 2)

Building To-Do App (Yapılacaklar Listesi Uygulaması - Bölüm 1 ve 2)


2. Bölüm: Server Basics (Sunucu Temelleri)

Web sitelerinin arkasındaki görünmeyen beyin sistemini kurmayı öğrenirsiniz.

Why Do We Need a Server? (Neden Bir Sunucuya İhtiyacımız Var?)

Node.js Intro (Node.js Giriş)

Text Editor Software (Metin Editörü Yazılımı)

Our First Basic Server (İlk Temel Sunucumuz)

Express Intro (Express Giriş - Bölüm 1 ve 2)

·        const express = require('express');

·        const app = express();

·         

·        app.get('/', function(istek, cevap) {

·            cevap.send("Ana Sayfaya Hoş Geldiniz!");

·        });

·        app.listen(3000); // 3000 numaralı kapıdan dünyayı dinle

What's Next? (Big Picture / Büyük Resim)


3. Bölüm: Database Basics (Veri Tabanı Temelleri)

Kullanıcıların yüklediği verileri ömür boyu saklayacağımız disk alanını yönetme bölümüdür.

First Taste of a Database & CRUD (Veri Tabanına İlk Bakış ve CRUD)

Host Your Database Locally On Your Computer (Veri Tabanını Bilgisayarınızda Yerel Olarak Barındırmak)

Initial Setup for App #1 (Uygulama #1 İçin İlk Kurulum)

Save Time: Automatic Node App Restarts (Otomatik Yeniden Başlatma ile Zaman Kazanma)

Connecting Node App to Database (Node Uygulamasını Veri Tabanına Bağlamak)

Solution to Common Database Problem & Installing NPM Packages (Sık Karşılaşılan Sorunlar ve NPM Paketleri Kurulumu)

Reading, Updating, Deleting Items & Create Without Page Reload (Veri Okuma, Güncelleme, Silme ve Sayfa Yenilenmeden Veri Oluşturma)

Client-Side Rendering (İstemci Taraflı Ekrana Çizme)

Security & Front-End Security (Güvenlik ve Ön Yüz Güvenliği)

Pushing Our App Onto The Internet (Uygulamayı İnternete Fırlatmak / Canlıya Almak)


4. Bölüm: Starting Our Complex App (Karmaşık Uygulamamıza Başlıyoruz - Uygulama #2)

Bu bölümde tam teşekküllü, çok kullanıcılı bir sosyal ağ platformu projesine başlanır.

Let's Begin App #2 & Dynamic Footer (Uygulama #2'ye Başlıyoruz ve Dinamik Alt Bilgi)

What Is A Router, Controller, Model? (Router, Controller ve Model Nedir? - MVC Mimarisi)

Adding Validation To Our Model (Modele Doğrulama Ekleme)

Best Practice: Environment Variables (En İyi Yöntem: Çevre Değişkenleri)

Letting Users Log In & Promises (Kullanıcı Girişi ve Sözler / Vaatler)

Hashing User Passwords (Kullanıcı Şifrelerini Kriptolamak/Öğütmek)

Session Security & Understanding Sessions & Logout (Oturum Güvenliği, Oturumları Anlamak ve Çıkış Yapma)

Adding Flash Messages (Anlık Uyarı Mesajları)

Adding User Profile Photos (Kullanıcı Profil Fotoğrafları Ekleme)


5. Bölüm: User Created Posts (Kullanıcı Tarafından Oluşturulan Gönderiler)

Sitede kullanıcıların kendi blog yazılarını veya tweet benzeri gönderilerini paylaşabilmesini sağlayan dinamik altyapıdır.

Letting Users Create Posts & Post Model (Kullanıcıların Gönderi Oluşturması ve Gönderi Modeli)

ObjectId Instead of ObjectID & New Keyword (ObjectId Kavramı)

Viewing a Post & Performing a Lookup in MongoDB (Bir Gönderiyi Görüntüleme ve Veri Birleştirme)

User Profile Screen & View Posts by Author (Kullanıcı Profil Ekranı ve Yazara Göre Gönderileri Görme)

Is the Current Visitor the Owner of the Post? (Mevcut Ziyaretçi Gönderinin Sahibi mi?)

The "Edit" Screen & Updating/Deleting Posts (Düzenleme Ekranı, Güncelleme ve Silme)

Markdown: Safe User Generated HTML (Markdown: Güvenli Kullanıcı HTML İçerikleri)


6. Bölüm: Live Search Feature (Canlı Arama Özelliği)

Kullanıcıların site içindeki gönderileri sayfa yenilenmeden, harf yazdıkça anlık aramasını sağlayan gelişmiş bölümdür.

Front-End JavaScript & Showing/Hiding Search Overlay (Ön Yüz JavaScript ve Arama Ekranını Açma/Kapama)

Responding to Key Press Events (Klavye Tuş Basma Olaylarına Tepki Verme)

Back-End Aspect of Search & DB Indexes (Aramanın Arka Yüzü ve Veri Tabanı İndeksleri)

Generating HTML for Search Results & Sanitizing (Arama Sonuçları İçin HTML Oluşturma ve Temizleme)


7. Bölüm: Letting Users Follow Each Other (Kullanıcıların Birbirini Takip Etmesi)

Sosyal ağların temel etkileşim mekanizması olan takip sisteminin kurulmasıdır.

Saving a "Follow" Action & Stop Following (Takip Etme Eylemini Kaydetme ve Takibi Bırakma)

Profile Followers & Following Screens (Profil Takipçiler ve Takip Edilenler Ekranları)

Homepage Feed (Ana Sayfa Akışı - Takip Edilenlerin Gönderileri)


8. Bölüm: Live Chat (Socket.IO) (Canlı Mesajlaşma Odası)

Kullanıcıların sayfayı yenilemeden birbiriyle anlık mesajlaşmasını sağlayan teknolojidir.

Socket.IO Introduction (Socket.IO Giriş)

Finishing Chat & Dynamic Title Tag (Sohbeti Bitirme ve Dinamik Sekme Başlığı)


9. Bölüm: Live Validation for Registration Form (Kayıt Formu İçin Canlı Doğrulama)

Kullanıcı deneyimini (UX) üst seviyeye çıkaran form kontrol mekanizmasıdır.

Live Form Validation (Canlı Form Doğrulama - Bölüm 1, 2, 3, 4)


10. Bölüm: What is CSRF? (Security) (CSRF Nedir? - Güvenlik Bölümü)

Uygulamanın hacklenmesini önlemek için kritik bir güvenlik duvarı konusudur.

Preventing Cross-Site Request Forgery (Siteler Arası İstek Sahtekarlığı Engelleme)


11. Bölüm: Let's Create an API (Bir API Oluşturalım)

Uygulamamızın verilerini dış dünyaya veya mobil uygulamalara açma yöntemidir.

Setting Up The Skeleton & JSON Web Tokens (JWT) (API İskeleti ve JSON Web Jetonları)

Understanding CORS (CORS Mekanizmasını Anlamak)


12. Bölüm: Deploying Complex App to Render (Karmaşık Uygulamayı Render Platformuna Canlıya Alma)

Yaptığımız devasa projeyi internete yükleme aşamasıdır.

Pushing Complex App & Sending Email (Uygulamayı Yükleme ve E-posta Gönderme)

Convert ".then()" Syntax To Async / Await (Async/Await Dönüşümü)


13. Bölüm: Turning Our App Into a SPA (Uygulamayı Tek Sayfalık Uygulamaya Dönüştürme)

Uygulamayı bir web sitesinden çok anlık tepki veren bir mobil uygulama hissiyatına kavuşturma cilasıdır.

Preventing Default Behavior of Links & History API (Linklerin Varsayılan Davranışını Engelleme ve Tarayıcı Geçmişi)

Fetching Data & Submitting Forms (JS ile Veri Çekme ve Form Gönderme)

 

Harika bir karar! Teorik bilgileri pratiğe dökerek pekiştirmek, yazılım öğrenmenin en etkili yoludur. Bu egzersizde, paylaştığınız müfredatta yer alan Express, Yönlendirme (Routing), Şifre Güvenliği (Hashing), Doğrulama (Validation) ve Oturum Yönetimi (Session) gibi kritik kavramları bir araya getiren harika bir "Üyelik ve Giriş Sistemi" arka yüz kodlaması yapacağız.

Kodları parça parça ele alacak, ardından müfredattaki konularla bağdaştırarak en derin detayına kadar inceleyeceğiz.


Bölüm 1: Projenin Kurulumu ve İskeleti (Server Basics)

Öncelikle projemizin ana dosyası olan server.js dosyasını oluşturuyoruz. Bu kısım müfredattaki "Server Basics" ve "Express Intro" konularını kapsar.

// server.js - Temel Sunucu ve Ayarlar

const express = require('express');

const bcrypt = require('bcrypt'); // Şifre kriptolamak için

const session = require('express-session'); // Oturum yönetimi için

 

const app = express();

 

// Middleware (Ara Yazılım) Ayarları

app.use(express.urlencoded({ extended: false })); // Formlardan gelen verileri okuyabilmek için

app.use(express.json()); // JSON formatındaki verileri okuyabilmek için

 

// Oturum (Session) Ayarları

app.use(session({

    secret: 'cok_gizli_bir_anahtar_kelime', // Güvenlik mührü

    resave: false,

    saveUninitialized: false,

    cookie: { maxAge: 1000 * 60 * 60 * 24 } // Oturum 24 saat geçerli olsun

}));

 

// Geçici Veri Tabanı (Müfredattaki MongoDB yerine şimdilik hafızada dizi tutuyoruz)

const kullaniciVeritabani = [];

🔍 Derin Değerlendirme ve Analiz:


Bölüm 2: Kullanıcı Kaydı ve Şifre Güvenliği (Hashing & Validation)

Şimdi kullanıcıların güvenli bir şekilde üye olmasını sağlayan kodu yazıyoruz. Müfredattaki "Hashing User Passwords" ve "Model Validation" konularını içerir.

// Kullanıcı Kayıt Rotası (Registration)

app.post('/kayit-ol', async (req, res) => {

    try {

        const { kullaniciAdi, eposta, sifre } = req.body;

 

        // 1. Doğrulama (Validation) Adımı

        if (!kullaniciAdi || !eposta || !sifre) {

            return res.status(400).send("Lütfen tüm alanları doldurun!");

        }

        if (sifre.length < 6) {

            return res.status(400).send("Şifre en az 6 karakter olmalıdır!");

        }

 

        // 2. Benzersizlik Kontrolü (Unique Check)

        const var Mi = kullaniciVeritabani.find(k => k.eposta === eposta);

        if (varMi) {

            return res.status(400).send("Bu e-posta adresi zaten kayıtlı!");

        }

 

        // 3. Şifreyi Kriptolama (Hashing)

        const tuzlamaKatsayisi = 10; // Salt rounds

        const kriptoluSifre = await bcrypt.hash(sifre, tuzlamaKatsayisi);

 

        // 4. Veriyi Kaydetme

        const yeniKullanici = {

            id: Date.now().toString(),

            kullaniciAdi,

            eposta,

            sifre: kriptoluSifre // Dümdüz şifreyi değil, kriptolu halini saklıyoruz!

        };

       

        kullaniciVeritabani.push(yeniKullanici);

        res.status(201).send("Tebrikler, kayıt başarıyla tamamlandı!");

 

    } catch (error) {

        res.status(500).send("Sunucu hatası oluştu.");

    }

});

🔍 Derin Değerlendirme ve Analiz:


Bölüm 3: Giriş Yapma ve Oturum Açma (Log-in & Promises)

Kullanıcının şifresini kontrol edip içeri aldığımız kısımdır. Müfredattaki "Letting Users Log In" ve "What is a Promise?" bölümlerini inceler.

// Kullanıcı Giriş Rotası (Log-in)

app.post('/giris-yap', async (req, res) => {

    try {

        const { eposta, sifre } = req.body;

 

        // 1. Kullanıcıyı Bulma

        const kullanici = kullaniciVeritabani.find(k => k.eposta === eposta);

        if (!kullanici) {

            return res.status(400).send("Hatalı e-posta veya şifre!");

        }

 

        // 2. Şifre Karşılaştırma (Bcrypt Promise yapısıyla çalışır)

        const sifreDogruMu = await bcrypt.compare(sifre, kullanici.sifre);

 

        if (sifreDogruMu) {

            // 3. Oturum Kartı Tanımlama (Session)

            req.session.kullanici = {

                id: kullanici.id,

                kullaniciAdi: kullanici.kullaniciAdi

            };

            return res.send(`Giriş başarılı! Hoş geldin, ${kullanici.kullaniciAdi}`);

        } else {

            return res.status(400).send("Hatalı e-posta veya şifre!");

        }

 

    } catch (error) {

        res.status(500).send("Giriş esnasında bir hata oluştu.");

    }

});

🔍 Derin Değerlendirme ve Analiz:


Bölüm 4: Yetkilendirme Koruması ve Çıkış Yapma (Authorization & Log-out)

Sadece giriş yapmış kişilerin görebileceği özel bir sayfa (Örn: Profil) ve çıkış yapma mekanizması kuruyoruz.

// Özel Koruma Ara Yazılımı (Auth Middleware)

function yetkiKontrolEt(req, res, next) {

    if (req.session.kullanici) {

        next(); // Oturum varsa geçişe izin ver, sonraki fonksiyona git

    } else {

        res.status(401).send("Bu sayfayı görmek için önce giriş yapmalısınız!");

    }

}

 

// Sadece Giriş Yapanların Görebileceği Özel Sayfa (Gizli Akış / Panel)

app.get('/panel', yetkiKontrolEt, (req, res) => {

    res.send(`Özel Yönetim Paneline Hoş Geldin, ${req.session.kullanici.kullaniciAdi}. Bu veriler gizlidir.`);

});

 

// Çıkış Yapma Rotası (Log-out)

app.post('/cikis-yap', (req, res) => {

    req.session.destroy((err) => {

        if (err) {

            return res.send("Çıkış yapılırken bir hata oluştu.");

        }

        res.send("Başarıyla çıkış yapıldı. Oturum sonlandırıldı.");

    });

});

 

// Sunucuyu Başlatma

app.listen(3000, () => console.log("Sunucu 3000 numaralı kapıda çalışıyor..."));

🔍 Derin Değerlendirme ve Analiz:


Büyük Resim Özeti ve İlerleme Durumu

Bu yaptığımız mini uygulamada müfredatınızın neredeyse tüm ana damarlarını birbirine bağladık:

  1. Girdi Kontrolü (Validation) ile hatalı verileri eledik.
  2. Kriptoloji (Bcrypt Hashing) ile şifreleri kırılamaz hale getirdik.
  3. Asenkron Mimari (Async/Await & Promises) kullanarak sunucumuzun performansını koruduk.
  4. Durum Yönetimi (Sessions & Cookies) ile kullanıcının sitemizde oturum halinde kalmasını sağladık.

 

 

Mükemmel bir adıma geçiyoruz! Bu aşamada projemizi tam anlamıyla modern, güvenli ve kurumsal bir SPA (Single Page Application / Tek Sayfalık Uygulama) mimarisine kavuşturuyoruz.

İstediğiniz güncellemeleri yaparken çok önemli bir sektörel güncellemeden bahsetmeliyim: Orijinal csurf paketi artık NPM üzerinde güvenlik zafiyetleri ve bakım eksikliği nedeniyle kullanımdan kaldırılmıştır (deprecated). Bu nedenle, modern Node.js ekosisteminde onun birebir alternatifi ve en güvenli standardı olan csrf-csrf kütüphanesini kullanacağız. Mantığı tamamen aynıdır (Double-Submit Cookie tekniği) ancak çok daha güvenlidir. [1, 2, 3, 4, 5]

Aşağıda hem sunucu (server.js) hem de tarayıcı (index.html) kodlarını, müfredatla bağdaştırılmış en derin analizleriyle hazırladım.


1. Bölüm: Arka Yüz (Back-End) Kodları: server.js

Bu kod veri tabanı bağlantısını sağlar, CSRF jetonlarını üretip denetler ve SPA için sadece ham JSON çıktıları verir.

// server.js

const express = require('express');

const mongoose = require('mongoose'); // Gerçek Veri Tabanı (MongoDB)

const bcrypt = require('bcrypt');

const session = require('express-session');

const cookieParser = require('cookie-parser'); // CSRF Çerezleri için şarttır

const { doubleCsrf } = require('csrf-csrf'); // Modern Güvenli CSRF Kütüphanesi

 

const app = express();

 

// 1. Veri Tabanı Bağlantısı (Database Basics)

// Yerelinizdeki MongoDB'ye bağlanır. 'uyelikSistemi' adında bir DB oluşturur.

mongoose.connect('mongodb://127.0.0.1:27017/uyelikSistemi')

  .then(() => console.log("MongoDB Veri Tabanına Başarıyla Bağlanıldı."))

  .catch(err => console.error("Veri Tabanı Bağlantı Hatası:", err));

 

// MongoDB Kullanıcı Şablonu (Model Yapısı)

const KullaniciSchema = new mongoose.Schema({

    kullaniciAdi: { type: String, required: true },

    eposta: { type: String, required: true, unique: true },

    sifre: { type: String, required: true }

});

const Kullanici = mongoose.model('Kullanici', KullaniciSchema);

 

// 2. Temel Ara Yazılımlar (Middleware)

app.use(express.static('public')); // HTML ve JS dosyalarımızı sunmak için

app.use(express.json()); // SPA için JSON gövdelerini okur

app.use(cookieParser("gizli_cerez_sifresi")); // Çerezleri şifreli okumak için

 

app.use(session({

    secret: 'cok_gizli_oturum_anahtari',

    resave: false,

    saveUninitialized: false,

    cookie: { maxAge: 1000 * 60 * 60 * 24 }

}));

 

// 3. CSRF Güvenlik Katmanı Ayarları (What is CSRF?)

const {

    invalidCsrfTokenErrorMiddleware,

    generateToken,

    doubleCsrfProtection

} = doubleCsrf({

    getSecret: () => "ust_duzey_gizli_csrf_sirri",

    cookieName: "x-csrf-token", // Tarayıcıda saklanacak çerez adı

    cookieOptions: { sameSite: "lax", secure: false }, // Yerel test için secure: false

    getTokenFromRequest: (req) => req.headers["x-csrf-token"] // İsteğin neresine bakılacak?

});

 

// Tarayıcının ilk açılışta gizli CSRF jetonunu talep edeceği kapı

app.get('/api/csrf-token', (req, res) => {

    const token = generateToken(req, res);

    res.json({ csrfToken: token }); // Jetonu JSON olarak SPA'ya fırlatır

});

 

// 4. SPA Üyelik ve Giriş Rotaları (Routes & Business Logic)

 

// Kayıt Olma Rotası

app.post('/api/kayit-ol', doubleCsrfProtection, async (req, res) => {

    try {

        const { kullaniciAdi, eposta, sifre } = req.body;

 

        if (!kullaniciAdi || !eposta || !sifre || sifre.length < 6) {

            return res.status(400).json({ hata: "Veriler geçersiz veya şifre çok kısa!" });

        }

 

        const varMi = await Kullanici.findOne({ eposta });

        if (varMi) return res.status(400).json({ hata: "Bu e-posta zaten kayıtlı!" });

 

        const kriptoluSifre = await bcrypt.hash(sifre, 10);

       

        // MongoDB'ye Kalıcı Kaydetme (CRUD - Create)

        const yeniKullanici = new Kullanici({ kullaniciAdi, eposta, sifre: kriptoluSifre });

        await yeniKullanici.save();

 

        res.status(201).json({ mesaj: "Kayıt başarıyla MongoDB'ye işlendi!" });

    } catch (e) {

        res.status(500).json({ hata: "Sistem hatası." });

    }

});

 

// Giriş Yapma Rotası

app.post('/api/giris-yap', doubleCsrfProtection, async (req, res) => {

    try {

        const { eposta, sifre } = req.body;

        const kullanici = await Kullanici.findOne({ eposta }); // MongoDB'den Okuma (CRUD - Read)

       

        if (!kullanici) return res.status(400).json({ hata: "Hatalı e-posta veya şifre!" });

 

        const sfreDogruMu = await bcrypt.compare(sifre, kullanici.sifre);

        if (!sfreDogruMu) return res.status(400).json({ hata: "Hatalı e-posta veya şifre!" });

 

        req.session.kullanici = { id: kullanici._id, ad: kullanici.kullaniciAdi };

        res.json({ mesaj: `Giriş başarılı! Hoş geldin ${kullanici.kullaniciAdi}` });

    } catch (e) {

        res.status(500).json({ hata: "Giriş hatası." });

    }

});

 

// CSRF Hata Yakalayıcı (Eğer jeton sahteyse bu devreye girer)

app.use(invalidCsrfTokenErrorMiddleware);

 

app.listen(3000, () => console.log("SPA Sunucusu port 3000'de yayında..."));

🔍 Arka Yüzün Derin Analiz Değerlendirmesi:


2. Bölüm: Ön Yüz (Front-End) Kodları: public/index.html

Bu dosya tarayıcıda çalışan tek sayfadır. Sayfa hiç yenilenmeden arkadan gizlice fetch kullanarak verileri çeker ve ekranı günceller (SPA).

<!-- public/index.html -->

<!DOCTYPE html>

<html lang="tr">

<head>

    <meta charset="UTF-8">

    <title>Güvenli SPA Üyelik Sistemi</title>

    <style>

        body { font-family: sans-serif; padding: 50px; background: #f4f4f9; }

        .form-kutusu { background: white; padding: 20px; border-radius: 8px; max-width: 400px; margin-bottom: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }

        .mesaj { padding: 10px; margin-top: 10px; font-weight: bold; border-radius: 4px; display: none; }

        .basari { background: #d4edda; color: #155724; }

        .hata { background: #f8d7da; color: #721c24; }

    </style>

</head>

<body>

 

    <h1>Güvenli Tek Sayfa Uygulaması (SPA)</h1>

   

    <!-- Kayıt Formu -->

    <div class="form-kutusu">

        <h3>Kayıt Ol (MongoDB & CSRF Korumalı)</h3>

        <input type="text" id="kayitAd" placeholder="Kullanıcı Adı"><br><br>

        <input type="email" id="kayitEposta" placeholder="E-posta"><br><br>

        <input type="password" id="kayitSifre" placeholder="Şifre (Min 6 Karakter)"><br><br>

        <button onclick="kayitOl()">Kayıt Ol</button>

        <div id="kayitMesaj" class="mesaj"></div>

    </div>

 

    <!-- Giriş Formu -->

    <div class="form-kutusu">

        <h3>Giriş Yap</h3>

        <input type="email" id="girisEposta" placeholder="E-posta"><br><br>

        <input type="password" id="girisSifre" placeholder="Şifre"><br><br>

        <button onclick="girisYap()">Giriş Yap</button>

        <div id="girisMesaj" class="mesaj"></div>

    </div>

 

    <script>

        // Sayfa yüklenir yüklenmez hafızada tutulacak CSRF Jetonu değişkeni

        let aktifCsrfToken = "";

 

        // 1. Sayfa Açıldığında Sunucudan Gizli CSRF Jetonunu İstiyoruz (SPA Başlangıcı)

        async function csrfTokenAl() {

            try {

                const yanit = await fetch('/api/csrf-token');

                const veri = await yanit.json();

                aktifCsrfToken = veri.csrfToken; // Jeton hafızaya alındı

                console.log("CSRF Jetonu Başarıyla Alındı ve Hafızaya Kilitlendi.");

            } catch (err) {

                console.error("CSRF Jetonu alınamadı!");

            }

        }

        csrfTokenAl();

 

        // 2. Fetch ile Sayfa Yenilenmeden Kayıt Olma İşlemi (Client-Side Rendering)

        async function kayitOl() {

            const ad = document.getElementById('kayitAd').value;

            const eposta = document.getElementById('kayitEposta').value;

            const sifre = document.getElementById('kayitSifre').value;

            const mesajKutusu = document.getElementById('kayitMesaj');

 

            // JavaScript ile Sunucuya İstek Paketi Hazırlama

            const yanit = await fetch('/api/kayit-ol', {

                method: 'POST',

                headers: {

                    'Content-Type': 'application/json',

                    'X-CSRF-Token': aktifCsrfToken // Güvenlik duvarını geçmek için zorunlu başlık!

                },

                body: JSON.stringify({ kullaniciAdi: ad, eposta: eposta, sifre: sifre })

            });

 

            const veri = await yanit.json();

            mesajKutusu.style.display = "block";

 

            if (yanit.ok) {

                mesajKutusu.className = "mesaj basari";

                mesajKutusu.innerText = veri.mesaj;

            } else {

                mesajKutusu.className = "mesaj hata";

                mesajKutusu.innerText = veri.hata;

            }

        }

 

        // 3. Fetch ile Giriş Yapma İşlemi

        async function girisYap() {

            const eposta = document.getElementById('girisEposta').value;

            const sifre = document.getElementById('girisSifre').value;

            const mesajKutusu = document.getElementById('girisMesaj');

 

            const yanit = await fetch('/api/giris-yap', {

                method: 'POST',

                headers: {

                    'Content-Type': 'application/json',

                    'X-CSRF-Token': aktifCsrfToken // Her güvenli POST işleminde bu gönderilmelidir

                },

                body: JSON.stringify({ eposta, sifre })

            });

 

            const veri = await yanit.json();

            mesajKutusu.style.display = "block";

 

            if (yanit.ok) {

                mesajKutusu.className = "mesaj basari";

                mesajKutusu.innerText = veri.mesaj;

            } else {

                mesajKutusu.className = "mesaj hata";

                mesajKutusu.innerText = veri.hata;

            }

        }

    </script>

</body>

</html>


🔍 Ön Yüz ve SPA Mantığının Derin Analiz Değerlendirmesi:

 

Harika bir noktaya geldik! Bu aşamada projemizi tam teşekküllü bir sosyal ağ platformuna dönüştürüyoruz. Müfredatta yer alan en heyecan verici üç ileri seviye özelliği uygulamamıza kazandıracağız: Socket.IO ile Canlı Sohbet, Anlık Form Doğrulama (Live Validation) ve Takipçi İlişkisi Veri Tabanı Mimarisi (Follower DB Design).

Aşağıda hem sunucu (server.js) hem de istemci (public/index.html) tarafındaki güncellemeleri tüm derinliğiyle hazırladım.


1. Bölüm: Geliştirilmiş Arka Yüz: server.js

Bu kodda Socket.IO entegrasyonu yapılmış, Takipçi şeması eklenmiş ve anlık e-posta kontrolü için özel bir API kapısı açılmıştır.

// server.js

const express = require('express');

const mongoose = require('mongoose');

const bcrypt = require('bcrypt');

const session = require('express-session');

const cookieParser = require('cookie-parser');

const { doubleCsrf } = require('csrf-csrf');

const http = require('http'); // Socket.IO için HTTP modülü şarttır

const { Server } = require('socket.io'); // Canlı Sohbet kütüphanesi

 

const app = express();

const server = http.createServer(app); // Express'i HTTP sunucusuna sarıyoruz

const io = new Server(server); // Socket.IO'yu bu sunucuya bağlıyoruz

 

// 1. MongoDB Bağlantısı ve Yeni Şemalar (Follower DB Design)

mongoose.connect('mongodb://127.0.0.1:27017/uyelikSistemi')

  .then(() => console.log("MongoDB Bağlantısı Başarılı."))

  .catch(err => console.error(err));

 

// Kullanıcı Şeması

const KullaniciSchema = new mongoose.Schema({

    kullaniciAdi: { type: String, required: true },

    eposta: { type: String, required: true, unique: true },

    sifre: { type: String, required: true }

});

const Kullanici = mongoose.model('Kullanici', KullaniciSchema);

 

// Takipçi İlişkisi Şeması (Follower Model)

// Kim, kimi takip ediyor? İki veriyi de Kullanıcı ID'si olarak tutuyoruz.

const TakipSchema = new mongoose.Schema({

    takipEdenId: { type: mongoose.Schema.Types.ObjectId, ref: 'Kullanici', required: true },

    takipEdilenId: { type: mongoose.Schema.Types.ObjectId, ref: 'Kullanici', required: true }

});

// Aynı kişinin aynı kişiyi iki kez takip etmesini engellemek için benzersiz indeks

TakipSchema.index({ takipEdenId: 1, takipEdilenId: 1 }, { unique: true });

const Takip = mongoose.model('Takip', TakipSchema);

 

// 2. Temel Ayarlar ve CSRF

app.use(express.static('public'));

app.use(express.json());

app.use(cookieParser("gizli_cerez_sifresi"));

 

const sessionMiddleware = session({

    secret: 'cok_gizli_oturum_anahtari',

    resave: false,

    saveUninitialized: false,

    cookie: { maxAge: 1000 * 60 * 60 * 24 }

});

app.use(sessionMiddleware);

 

// Socket.IO'nun session verilerine erişebilmesi için köprü kuruyoruz

io.use((socket, next) => {

    sessionMiddleware(socket.request, {}, next);

});

 

const { generateToken, doubleCsrfProtection, invalidCsrfTokenErrorMiddleware } = doubleCsrf({

    getSecret: () => "ust_duzey_gizli_csrf_sirri",

    cookieName: "x-csrf-token",

    getTokenFromRequest: (req) => req.headers["x-csrf-token"]

});

 

app.get('/api/csrf-token', (req, res) => {

    res.json({ csrfToken: generateToken(req, res) });

});

 

// 3. ANLIK DOĞRULAMA API KAPISI (Live Validation)

// Bu kapı güvenlik duvarının (CSRF) önünde olmalıdır çünkü kullanıcı daha formu göndermedi

app.post('/api/kontrol-eposta', async (req, res) => {

    try {

        const { eposta } = req.body;

        const varMi = await Kullanici.findOne({ eposta });

        if (varMi) {

            return res.json({ durum: "alinmis", mesaj: "Bu e-posta adresi zaten kullanımda!" });

        }

        res.json({ durum: "uygun", mesaj: "E-posta adresi kullanılabilir." });

    } catch (e) {

        res.status(500).json({ hata: "Kontrol edilemedi." });

    }

});

 

// 4. Standart Kayıt ve Giriş Rotaları

app.post('/api/kayit-ol', doubleCsrfProtection, async (req, res) => {

    try {

        const { kullaniciAdi, eposta, sifre } = req.body;

        if (!kullaniciAdi || !eposta || !sifre || sifre.length < 6) return res.status(400).json({ hata: "Eksik veri!" });

       

        const kriptoluSifre = await bcrypt.hash(sifre, 10);

        const yeniKullanici = new Kullanici({ kullaniciAdi, eposta, sifre: kriptoluSifre });

        await yeniKullanici.save();

        res.status(201).json({ mesaj: "Kayıt başarıyla tamamlandı!" });

    } catch (e) { res.status(500).json({ hata: "Kayıt hatası." }); }

});

 

app.post('/api/giris-yap', doubleCsrfProtection, async (req, res) => {

    try {

        const { eposta, sifre } = req.body;

        const kullanici = await Kullanici.findOne({ eposta });

        if (!kullanici || !(await bcrypt.compare(sifre, kullanici.sifre))) {

            return res.status(400).json({ hata: "Hatalı giriş!" });

        }

        req.session.kullanici = { id: kullanici._id, ad: kullanici.kullaniciAdi };

        res.json({ mesaj: "Giriş başarılı!", kullanici: req.session.kullanici });

    } catch (e) { res.status(500).json({ hata: "Giriş hatası." }); }

});

 

// 5. TAKİPÇİ SİSTEMİ ROTALARI (Follower System)

 

// Takip Etme Rotası

app.post('/api/takip-et', doubleCsrfProtection, async (req, res) => {

    if (!req.session.kullanici) return res.status(401).json({ hata: "Önce giriş yapmalısınız!" });

   

    try {

        const { takipEdilenEposta } = req.body;

        const hedefKullanici = await Kullanici.findOne({ eposta: takipEdilenEposta });

        if (!hedefKullanici) return res.status(404).json({ hata: "Takip edilecek kullanıcı bulunamadı!" });

       

        if (hedefKullanici._id.toString() === req.session.kullanici.id.toString()) {

            return res.status(400).json({ hata: "Kendinizi takip edemezsiniz!" });

        }

 

        const yeniTakip = new Takip({

            takipEdenId: req.session.kullanici.id,

            takipEdilenId: hedefKullanici._id

        });

        await yeniTakip.save();

        res.json({ mesaj: `${hedefKullanici.kullaniciAdi} başarıyla takip edildi!` });

    } catch (e) { res.status(400).json({ hata: "Zaten takip ediyorsunuz veya bir hata oluştu." }); }

});

 

// 6. CANLI SOHBET MOTORU (Live Chat - Socket.IO)

io.on('connection', (socket) => {

    // Sokete bağlanan kişinin oturum verilerine bakıyoruz

    const oturum = socket.request.session;

    const kullaniciAdi = oturum.kullanici ? oturum.kullanici.ad : "Anonim Misafir";

 

    console.log(`${kullaniciAdi} canlı sohbete bağlandı.`);

 

    // Bir kullanıcı mesaj gönderdiğinde tetiklenir

    socket.on('yeniMesaj', (mesajMetni) => {

        // Gelen mesajı, gönderenin adıyla birlikte odadaki HERKESE saliseler içinde dağıtıyoruz

        io.emit('odaMesaji', {

            gonderen: kullaniciAdi,

            metin: mesajMetni,

            zaman: new Date().toLocaleTimeString()

        });

    });

 

    socket.on('disconnect', () => {

        console.log(`${kullaniciAdi} odadan ayrıldı.`);

    });

});

 

app.use(invalidCsrfTokenErrorMiddleware);

 

// Sunucuyu app.listen yerine server.listen ile başlatıyoruz çünkü Socket.IO bunu gerektirir

server.listen(3000, () => console.log("Sosyal SPA Sunucusu port 3000'de çalışıyor..."));


2. Bölüm: Ön Yüz (Front-End) Kodları: public/index.html

Bu sayfada, harf yazıldığı an tetiklenen canlı doğrulama motoru, takip butonu ve Socket.IO canlı sohbet arayüzü tek sayfada buluşur.

<!-- public/index.html -->

<!DOCTYPE html>

<html lang="tr">

<head>

    <meta charset="UTF-8">

    <title>Gelişmiş Sosyal SPA Uygulaması</title>

    <!-- Socket.IO'nun tarayıcı kütüphanesini sunucumuzdan otomatik çekiyoruz -->

    <script src="/socket.io/socket.io.js"></script>

    <style>

        body { font-family: Arial, sans-serif; display: flex; gap: 20px; padding: 30px; background: #eef2f3; }

        .sol-kolon, .sag-kolon { flex: 1; }

        .kart { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 4px 6px rgba(0,0,0,0.05); }

        .uyari-yazisi { font-size: 12px; font-weight: bold; margin-top: 5px; }

        .uygun { color: green; }

        .alinmis { color: red; }

        #sohbetKutusu { height: 200px; border: 1px solid #ccc; overflow-y: scroll; padding: 10px; background: #fafafa; margin-bottom: 10px; }

    </style>

</head>

<body>

 

    <div class="sol-kolon">

        <!-- Kayıt Kartı (Live Validation İçerir) -->

        <div class="kart">

            <h3>Kayıt Ol</h3>

            <input type="text" id="kayitAd" placeholder="Kullanıcı Adı"><br><br>

            <!-- input olayı: Kullanıcı her harf yazdığında tetiklenir -->

            <input type="email" id="kayitEposta" placeholder="E-posta" oninput="epostaKontrolEt()"><br>

            <div id="epostaUyari" class="uyari-yazisi"></div><br>

            <input type="password" id="kayitSifre" placeholder="Şifre"><br><br>

            <button onclick="kayitOl()">Hesap Oluştur</button>

        </div>

 

        <!-- Giriş ve Takip Kartı -->

        <div class="kart">

            <h3>Giriş & Etkileşim</h3>

            <input type="email" id="girisEposta" placeholder="E-posta"><br><br>

            <input type="password" id="girisSifre" placeholder="Şifre"><br><br>

            <button onclick="girisYap()">Giriş Yap</button>

            <hr>

            <h4>Kullanıcı Takip Et</h4>

            <input type="email" id="takipHedef" placeholder="Takip edilecek e-posta"><br><br>

            <button onclick="takipEt()">Takip Et</button>

        </div>

    </div>

 

    <div class="sag-kolon">

        <!-- Canlı Sohbet Odası (Socket.IO) -->

        <div class="kart">

            <h3>Küresel Canlı Sohbet (Socket.IO)</h3>

            <div id="sohbetKutusu"></div>

            <input type="text" id="mesajGirdisi" placeholder="Mesajınızı yazın..." style="width: 70%;">

            <button onclick="mesajGonder()" style="width: 25%;">Gönder</button>

        </div>

    </div>

 

    <script>

        let aktifCsrfToken = "";

        let socket; // Socket bağlantı nesnesi

 

        // CSRF Jetonu Alımı

        async function tokenYenile() {

            const y = await fetch('/api/csrf-token');

            const v = await y.json();

            aktifCsrfToken = v.csrfToken;

        }

        tokenYenile();

 

        // 1. CANLI DOĞRULAMA MOTORU (Live Validation)

        async function epostaKontrolEt() {

            const eposta = document.getElementById('kayitEposta').value;

            const uyariKutusu = document.getElementById('epostaUyari');

 

            if (eposta.length < 5) {

                uyariKutusu.innerText = "";

                return;

            }

 

            // Sayfa yenilenmeden arka planda e-posta sorgusu fırlatılıyor

            const yanit = await fetch('/api/kontrol-eposta', {

                method: 'POST',

                headers: { 'Content-Type': 'application/json' },

                body: JSON.stringify({ eposta })

            });

            const veri = await yanit.json();

 

            // Gelen cevaba göre anında görsel sınıf ataması yapılıyor

            if (veri.durum === "alinmis") {

                uyariKutusu.className = "uyari-yazisi alinmis";

            } else {

                uyariKutusu.className = "uyari-yazisi uygun";

            }

            uyariKutusu.innerText = veri.mesaj;

        }

 

        // 2. Standart Kayıt İşlemi

        async function kayitOl() {

            const yanit = await fetch('/api/kayit-ol', {

                method: 'POST',

                headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': aktifCsrfToken },

                body: JSON.stringify({

                    kullaniciAdi: document.getElementById('kayitAd').value,

                    eposta: document.getElementById('kayitEposta').value,

                    sifre: document.getElementById('kayitSifre').value

                })

            });

            const v = await yanit.json();

            alert(v.mesaj || v.hata);

        }

 

        // 3. Giriş İşlemi ve Sohbet Hattını Ateşleme

        async function girisYap() {

            const yanit = await fetch('/api/giris-yap', {

                method: 'POST',

                headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': aktifCsrfToken },

                body: JSON.stringify({

                    eposta: document.getElementById('girisEposta').value,

                    sifre: document.getElementById('girisSifre').value

                })

            });

            const v = await yanit.json();

            alert(v.mesaj || v.hata);

 

            if (yanit.ok) {

                // Giriş başarılıysa Socket hattını açıyoruz!

                sohbetiBaslat();

            }

        }

 

        // 4. TAKİP ETME İŞLEMİ (Follow Action)

        async function takipEt() {

            const yanit = await fetch('/api/takip-et', {

                method: 'POST',

                headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': aktifCsrfToken },

                body: JSON.stringify({ takipEdilenEposta: document.getElementById('takipHedef').value })

            });

            const v = await yanit.json();

            alert(v.mesaj || v.hata);

        }

 

        // 5. SOCKET.IO CANLI SOHBET YÖNETİMİ (Live Chat)

        function sohbetiBaslat() {

            // Sunucu ile sürekli açık kalacak olan WebSocket köprüsünü kuruyoruz

            socket = io();

 

            // Sunucudan yeni bir mesaj odasına düştüğünde yakala

            socket.on('odaMesaji', (veri) => {

                const kutu = document.getElementById('sohbetKutusu');

                kutu.innerHTML += `<p><strong>[${veri.zaman}] ${veri.gonderen}:</strong> ${veri.metin}</p>`;

                kutu.scrollTop = kutu.scrollHeight; // Sohbeti otomatik aşağı kaydır

            });

        }

 

        function mesajGonder() {

            const girdi = document.getElementById('mesajGirdisi');

            if (!socket) {

                alert("Önce giriş yapmalısınız!");

                return;

            }

            if (girdi.value.trim() !== "") {

                // Mesajı sunucuya fırlatıyoruz

                socket.emit('yeniMesaj', girdi.value);

                girdi.value = "";

            }

        }

    </script>

</body>

</html>


🔍 Gelişmiş Özelliklerin En Derin Değerlendirilmesi: