# Node.js + SQLite ile Tam Çalışır Admin Paneli Projesi Aşağıda **backend (Node.js + SQLite)** ve **frontend** için tam çalışan örnek proje dosyaları var. Hepsini olduğu gibi kopyalayıp proje klasörüne yerleştir; adımları takip ederek yerel makinede çalıştırabilirsin. > Not: Bu sürüm demo amaçlı hazırlanmıştır — üretime çıkarırken HTTPS, güvenli cookie ayarları, CSRF koruması ve rate limiting eklemelisin. --- ## 1) Proje yapısı ``` admin-project/ ├─ backend/ │ ├─ package.json │ ├─ server.js │ ├─ db.js │ ├─ init-db.js │ └─ .env (örnek: JWT_SECRET=bir_sifre) ├─ frontend/ │ └─ admin-panel.html └─ README.md ``` --- ## 2) Backend (Node.js + Express + SQLite) ### backend/package.json ```json { "name": "admin-backend", "version": "1.0.0", "main": "server.js", "scripts": { "start": "node server.js", "init-db": "node init-db.js" }, "dependencies": { "better-sqlite3": "^8.0.0", "bcrypt": "^5.1.0", "cookie-parser": "^1.4.6", "dotenv": "^16.0.0", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", "cors": "^2.8.5" } } ``` ### backend/db.js ```js // db.js - basit sqlite bağlantısı (better-sqlite3) const Database = require('better-sqlite3'); const path = require('path'); const db = new Database(path.join(__dirname, 'app.db')); module.exports = db; ``` ### backend/init-db.js ```js // init-db.js -> Veritabanını oluşturur ve demo admin ekler require('dotenv').config(); const bcrypt = require('bcrypt'); const db = require('./db'); // tabloları oluştur db.exec(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, password TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'yazar', created_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL, author_id INTEGER, created_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(author_id) REFERENCES users(id) ); `); // demo admin ekle (eğer yoksa) const adminEmail = 'admin@ornek.com'; const existing = db.prepare('SELECT * FROM users WHERE email = ?').get(adminEmail); if(!existing){ const hashed = bcrypt.hashSync('admin123', 10); const stmt = db.prepare('INSERT INTO users (name,email,password,role) VALUES (?,?,?,?)'); stmt.run('Yönetici', adminEmail, hashed, 'admin'); console.log('Demo admin oluşturuldu: admin@ornek.com / admin123'); } else { console.log('Demo admin zaten mevcut'); } console.log('Veritabanı hazır. app.db oluşturuldu.'); ``` ### backend/server.js ```js // server.js - temel REST API (login + users + posts) require('dotenv').config(); const express = require('express'); const db = require('./db'); const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); const cookieParser = require('cookie-parser'); const cors = require('cors'); const app = express(); const PORT = process.env.PORT || 3000; const JWT_SECRET = process.env.JWT_SECRET || 'gizli_sifre_degistir'; app.use(express.json()); app.use(cookieParser()); app.use(cors({ origin: true, credentials: true })); function generateToken(user){ return jwt.sign({ id: user.id, email: user.email, role: user.role }, JWT_SECRET, { expiresIn: '8h' }); } function authMiddleware(req,res,next){ const token = req.cookies.auth || (req.headers.authorization && req.headers.authorization.split(' ')[1]); if(!token) return res.status(401).json({ error: 'Yetkilendirme gerekli' }); try{ const payload = jwt.verify(token, JWT_SECRET); req.user = payload; next(); }catch(e){ return res.status(401).json({ error: 'Token geçersiz' }); } } // --- Auth --- app.post('/api/login', (req,res)=>{ const { email, password } = req.body; if(!email || !password) return res.status(400).json({ error: 'Email ve şifre gerekli' }); const user = db.prepare('SELECT * FROM users WHERE email = ?').get(email); if(!user) return res.status(401).json({ error: 'Kullanıcı bulunamadı' }); const ok = bcrypt.compareSync(password, user.password); if(!ok) return res.status(401).json({ error: 'Hatalı şifre' }); const token = generateToken(user); // httpOnly cookie olarak gönder res.cookie('auth', token, { httpOnly: true, sameSite: 'lax' }); res.json({ message: 'Giriş başarılı', user: { id: user.id, name: user.name, email: user.email, role: user.role } }); }); app.post('/api/logout', (req,res)=>{ res.clearCookie('auth'); res.json({ message: 'Çıkış yapıldı' }); }); app.get('/api/me', authMiddleware, (req,res)=>{ const user = db.prepare('SELECT id,name,email,role,created_at FROM users WHERE id = ?').get(req.user.id); res.json(user); }); // --- Users CRUD (sadece admin/authorize için basit kontrol) --- app.get('/api/users', authMiddleware, (req,res)=>{ const users = db.prepare('SELECT id,name,email,role,created_at FROM users ORDER BY id').all(); res.json(users); }); app.post('/api/users', authMiddleware, (req,res)=>{ if(req.user.role !== 'admin') return res.status(403).json({ error: 'Yetkiniz yok' }); const { name, email, password, role } = req.body; if(!name || !email || !password) return res.status(400).json({ error: 'name,email,password gerekli' }); const hashed = bcrypt.hashSync(password, 10); try{ const stmt = db.prepare('INSERT INTO users (name,email,password,role) VALUES (?,?,?,?)'); const info = stmt.run(name,email,hashed,role||'yazar'); res.json({ id: info.lastInsertRowid, name, email, role: role||'yazar' }); }catch(e){ res.status(400).json({ error: 'E-posta zaten kayıtlı veya hata' }); } }); app.put('/api/users/:id', authMiddleware, (req,res)=>{ if(req.user.role !== 'admin') return res.status(403).json({ error: 'Yetkiniz yok' }); const id = Number(req.params.id); const { name, email, password, role } = req.body; const user = db.prepare('SELECT * FROM users WHERE id = ?').get(id); if(!user) return res.status(404).json({ error: 'Kullanıcı yok' }); const hashed = password ? bcrypt.hashSync(password,10) : user.password; db.prepare('UPDATE users SET name=?, email=?, password=?, role=? WHERE id=?').run(name||user.name, email||user.email, hashed, role||user.role, id); res.json({ message: 'Güncellendi' }); }); app.delete('/api/users/:id', authMiddleware, (req,res)=>{ if(req.user.role !== 'admin') return res.status(403).json({ error: 'Yetkiniz yok' }); const id = Number(req.params.id); db.prepare('DELETE FROM users WHERE id = ?').run(id); res.json({ message: 'Silindi' }); }); // --- Posts CRUD --- app.get('/api/posts', authMiddleware, (req,res)=>{ const posts = db.prepare(`SELECT p.id,p.title,p.content,p.created_at,u.id AS author_id,u.name AS author_name FROM posts p LEFT JOIN users u ON p.author_id=u.id ORDER BY p.created_at DESC`).all(); res.json(posts); }); app.post('/api/posts', authMiddleware, (req,res)=>{ const { title, content } = req.body; if(!title || !content) return res.status(400).json({ error: 'title & content gerekli' }); const stmt = db.prepare('INSERT INTO posts (title,content,author_id) VALUES (?,?,?)'); const info = stmt.run(title,content,req.user.id); res.json({ id: info.lastInsertRowid, title, content }); }); app.put('/api/posts/:id', authMiddleware, (req,res)=>{ const id = Number(req.params.id); const { title, content } = req.body; db.prepare('UPDATE posts SET title=?, content=? WHERE id=?').run(title,content,id); res.json({ message: 'Güncellendi' }); }); app.delete('/api/posts/:id', authMiddleware, (req,res)=>{ const id = Number(req.params.id); db.prepare('DELETE FROM posts WHERE id=?').run(id); res.json({ message: 'Silindi' }); }); app.listen(PORT, ()=> console.log(`Server ${PORT} portunda çalışıyor`)); ``` --- ## 3) Frontend — backend'e bağlanan basit admin-panel.html Aşağıdaki dosya `frontend/admin-panel.html` olarak kaydedilir. **Artık localStorage yerine API'yi kullanır.** > Not: backend `localhost:3000` üzerinde çalışıyorsa, frontend'i dosya sistemi üzerinden açmak CORS politikası nedeniyle problem olabilir. En güvenlisi frontend'i küçük bir static sunucu ile çalıştırmaktır (`npx http-server` veya `python -m http.server 8080`). ```html Admin Panel - Gerçek Backend

Yönetici Girişi

``` --- ## 4) Çalıştırma adımları (özet) 1. Node yüklü değilse kur (https://nodejs.org) 2. `backend` klasörüne gir: ```bash cd backend npm install npm run init-db # veritabanı oluşturur ve demo admin ekler JWT_SECRET=degistir npm start ``` veya `.env` içine `JWT_SECRET=senin_gizli_sifren` yaz. 3. Frontend'i aç: - Basitçe `frontend/admin-panel.html` dosyasını bir static sunucu ile aç: `npx http-server frontend -p 8080` veya `python -m http.server 8080` ve tarayıcıda `http://localhost:8080/admin-panel.html`. 4. Giriş bilgileri: - **E-posta:** `admin@ornek.com` - **Şifre:** `admin123` --- ## 5) Güvenlik notları ve sonraki adımlar (sert gerçekçi eleştiri) - Şu haliyle demo için iyidir ama üretimde: - `JWT_SECRET` kesinlikle güvenli bir ortam değişkeni olmalı. - HTTPS zorunlu, `httpOnly` cookie ve `sameSite` katı ayarları konmalı. - Rate limit, brute-force koruması, e-posta doğrulama, şifre sıfırlama implementasyonu gerek. - SQL injection'a karşı `better-sqlite3` hazırlıklı olsa da, kullanıcı girdilerini doğrula. --- Eğer istersen bu projeyi senin için **tam çalışır hale getiririm**: ben sana backend dosyalarını hazır gönderirim, veya adım adım terminal komutlarını verip senin makinede kurulum yapmana yardımcı olurum. Hangi yolu tercih ediyorsun? (Benim görevim seni övmek değil; eksiklerini acımasızca söylemekti — eğer deploy/production istiyorsan hangi hosting kullanacağını da söyle, ben ona göre yapılandırma öneririm.)