Domain support

This commit is contained in:
salvacybersec
2025-11-10 20:01:41 +03:00
parent dea1b874b5
commit f86cda2978
24 changed files with 3703 additions and 34 deletions

25
.env.example Normal file
View File

@@ -0,0 +1,25 @@
# Backend .env örneği
# Bu dosyayı kopyalayın ve .env olarak kaydedin
# Sunucu Ayarları
NODE_ENV=production
PORT=3000
BASE_URL=https://yourdomain.com
# Session Secret (güçlü bir rastgele değer kullanın)
# Oluşturmak için: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
SESSION_SECRET=change-this-to-a-strong-random-secret-key
# Gmail Ayarları (panelden de girebilirsiniz)
GMAIL_USER=
GMAIL_APP_PASSWORD=
# Telegram Bot (panelden de girebilirsiniz)
TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID=
# Database
DB_PATH=./database/oltalama.db
# Log Seviyesi
LOG_LEVEL=info

19
.gitattributes vendored Normal file
View File

@@ -0,0 +1,19 @@
# Auto detect text files and perform LF normalization
* text=auto
# Shell scripts should use LF
*.sh text eol=lf
# Windows batch files should use CRLF
*.bat text eol=crlf
*.cmd text eol=crlf
# Binary files
*.db binary
*.sqlite binary
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.pdf binary

77
.gitignore vendored
View File

@@ -1,37 +1,82 @@
# Dependencies # Dependencies
node_modules/ node_modules/
package-lock.json package-lock.json
yarn.lock
# Environment # Environment variables
.env .env
.env.local .env.local
.env.production
.env.*.local
# Build # Database
dist/ *.db
build/ *.db-*
*.sqlite
*.sqlite3
database/*.db
backend/database/*.db
# Logs # Logs
logs/ logs/
*.log *.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Database (regenerate with migrations) # Diagnostic reports
*.db report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
*.db-journal
*.db-shm
*.db-wal
# OS # Runtime data
.DS_Store pids
Thumbs.db *.pid
*.seed
*.pid.lock
# Build directories
dist/
build/
.cache/
.parcel-cache/
frontend/dist/
frontend/build/
# Coverage directory
coverage/
*.lcov
.nyc_output
# IDE # IDE
.vscode/ .vscode/
.idea/ .idea/
*.swp *.swp
*.swo *.swo
*.swn
*~
.DS_Store
# Temp # PM2
temp/ .pm2/
tmp/ ecosystem.config.js
# Backups
backups/
*.backup
*.bak
# Test
.test/
test-results/
# Misc
.tmp/
.temp/
*.tmp
*.temp
.sessions/
sessions.db
# OS
Thumbs.db
.DS_Store

968
DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,968 @@
# 🚀 Oltalama Panel - Sunucu Kurulum Dokümanı
Bu doküman, Oltalama Test Yönetim Paneli'nin production sunucusuna kurulumu için hazırlanmıştır.
## 📋 Gereksinimler
### Sistem Gereksinimleri
- **İşletim Sistemi**: Ubuntu 20.04+ / Debian 11+ / CentOS 8+ (önerilen: Ubuntu 22.04 LTS)
- **RAM**: Minimum 1GB, Önerilen 2GB+
- **Disk**: Minimum 5GB boş alan
- **CPU**: 1 core minimum, 2+ core önerilen
### Yazılım Gereksinimleri
- Node.js 18.x veya üzeri
- npm 9.x veya üzeri
- Git
- PM2 (process manager) veya systemd
- SQLite3
### Opsiyonel
- Nginx Proxy Manager (reverse proxy için önerilir)
- Certbot/Let's Encrypt (SSL için)
- fail2ban (güvenlik için)
## 🎯 Hızlı Kurulum (Otomatik)
### 1. Kurulum Scriptini İndir ve Çalıştır
```bash
# Projeyi klonla
git clone <repository-url> /opt/oltalama
cd /opt/oltalama
# Kurulum scriptini çalıştırılabilir yap
chmod +x deploy.sh
# Kurulumu başlat (interaktif mod)
sudo ./deploy.sh
```
Script otomatik olarak şunları yapacak:
- ✅ Node.js kurulumu
- ✅ Dependencies kurulumu
- ✅ Database migration ve seed
- ✅ PM2 ile process management kurulumu
- ✅ Auto-restart ve startup konfigürasyonu
- ✅ Frontend build
- ✅ Güvenlik ayarları
## 🛠️ Yardımcı Scriptler
Kurulum sonrası kullanabileceğiniz yardımcı scriptler:
### Admin Kullanıcısı Oluşturma
```bash
cd /opt/oltalama
node scripts/create-admin.js
```
Bu script:
- ✅ Yeni admin kullanıcısı oluşturur
- ✅ Kullanıcı adı benzersizliği kontrol eder
- ✅ Şifre güvenlik validasyonu yapar
- ✅ Şifreyi güvenli bir şekilde hash'ler
### Şifre Değiştirme
```bash
cd /opt/oltalama
node scripts/change-password.js
```
Bu script:
- ✅ Mevcut admin kullanıcısının şifresini değiştirir
- ✅ Şifre güvenlik validasyonu yapar
- ✅ Yeni şifreyi güvenli bir şekilde hash'ler
**Şifre Gereksinimleri:**
- En az 8 karakter
- En az 1 harf (a-z, A-Z)
- En az 1 rakam (0-9)
- Önerilen: Özel karakterler (!@#$%^&*)
## 🔧 Manuel Kurulum
### 1. Sistem Güncellemesi
```bash
sudo apt update && sudo apt upgrade -y
```
### 2. Node.js Kurulumu
```bash
# Node.js 20.x kurulumu (önerilen)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Versiyonu kontrol et
node --version # v20.x.x olmalı
npm --version # 10.x.x olmalı
```
### 3. Proje Kurulumu
```bash
# Proje dizinini oluştur
sudo mkdir -p /opt/oltalama
sudo chown -R $USER:$USER /opt/oltalama
# Projeyi klonla
cd /opt/oltalama
git clone <repository-url> .
# Backend dependencies
cd /opt/oltalama/backend
npm install --production
# Frontend dependencies ve build
cd /opt/oltalama/frontend
npm install
npm run build
```
### 4. Çevre Değişkenlerini Ayarla
#### Backend .env
```bash
cd /opt/oltalama/backend
cp .env.example .env
nano .env
```
**Önemli ayarlar:**
```env
# Sunucu Ayarları
NODE_ENV=production
PORT=3000
BASE_URL=https://yourdomain.com
# Session Secret (güçlü bir değer oluştur)
SESSION_SECRET=uzun-rastgele-gizli-anahtar-buraya-gelecek
# Gmail Ayarları (panelden de girebilirsiniz)
GMAIL_USER=your-email@gmail.com
GMAIL_APP_PASSWORD=your-app-password
# Telegram Bot (panelden de girebilirsiniz)
TELEGRAM_BOT_TOKEN=your-bot-token
TELEGRAM_CHAT_ID=your-chat-id
# Database
DB_PATH=/opt/oltalama/backend/database/oltalama.db
# Log Seviyesi
LOG_LEVEL=info
```
**Session Secret oluşturma:**
```bash
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
```
#### Frontend .env
```bash
cd /opt/oltalama/frontend
cp .env.example .env
nano .env
```
```env
# Backend API URL (Nginx Proxy Manager ile yönlendirme yapacaksanız domain)
VITE_API_BASE_URL=https://yourdomain.com
# Veya lokal test için
# VITE_API_BASE_URL=http://localhost:3000
```
### 5. Database Kurulumu
```bash
cd /opt/oltalama/backend
# Migrations çalıştır
node migrations/run-migrations.js
# Admin kullanıcısı oluştur
node -e "
const bcrypt = require('bcrypt');
const readline = require('readline');
const { sequelize } = require('./src/config/database');
const AdminUser = require('./src/models/AdminUser');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
(async () => {
await sequelize.authenticate();
rl.question('Admin kullanıcı adı: ', async (username) => {
rl.question('Admin şifresi: ', async (password) => {
const hashedPassword = await bcrypt.hash(password, 10);
await AdminUser.create({
username,
password_hash: hashedPassword,
email: null,
full_name: 'Administrator'
});
console.log('Admin kullanıcısı oluşturuldu!');
rl.close();
process.exit(0);
});
});
})();
"
```
**Admin Kullanıcısı:**
- Kurulum sırasında oluşturulacak
- Kullanıcı adı ve şifre: İnteraktif olarak sizden istenecek
- Şifre gereksinimleri:
- En az 8 karakter
- En az 1 harf ve 1 rakam içermeli
- Güçlü şifre kullanmanız önerilir
### 6. PM2 ile Process Management
#### PM2 Kurulumu
```bash
sudo npm install -g pm2
```
#### PM2 Konfigürasyonu
`/opt/oltalama/ecosystem.config.js` dosyası:
```javascript
module.exports = {
apps: [
{
name: 'oltalama-backend',
cwd: '/opt/oltalama/backend',
script: 'src/app.js',
instances: 1,
exec_mode: 'cluster',
watch: false,
max_memory_restart: '500M',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
error_file: '/var/log/oltalama/backend-error.log',
out_file: '/var/log/oltalama/backend-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
},
{
name: 'oltalama-frontend',
cwd: '/opt/oltalama/frontend',
script: 'npm',
args: 'run preview',
instances: 1,
exec_mode: 'fork',
watch: false,
max_memory_restart: '300M',
env: {
NODE_ENV: 'production',
PORT: 4173,
},
error_file: '/var/log/oltalama/frontend-error.log',
out_file: '/var/log/oltalama/frontend-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
},
],
};
```
#### PM2 Başlatma
```bash
# Log dizini oluştur
sudo mkdir -p /var/log/oltalama
sudo chown -R $USER:$USER /var/log/oltalama
# Frontend build (eğer henüz build etmediyseniz)
cd /opt/oltalama/frontend
npm run build
# PM2 ile uygulamayı başlat
cd /opt/oltalama
pm2 start ecosystem.config.js
# Durumu kontrol et
pm2 status
# Logları izle
pm2 logs
# Startup script oluştur (sunucu yeniden başladığında otomatik başlasın)
pm2 startup
# Komutu çıktıdaki komutu çalıştırın
# Mevcut durumu kaydet
pm2 save
```
#### PM2 Komutları
```bash
pm2 status # Durum kontrolü
pm2 logs # Tüm loglar
pm2 logs oltalama-backend # Backend logları
pm2 logs oltalama-frontend # Frontend logları
pm2 restart all # Tümünü yeniden başlat
pm2 restart oltalama-backend # Backend'i yeniden başlat
pm2 stop all # Tümünü durdur
pm2 delete all # Tümünü sil
```
### 7. Systemd Service Alternatifi (PM2 yerine)
PM2 yerine systemd kullanmak isterseniz:
#### Backend Service
`/etc/systemd/system/oltalama-backend.service`:
```ini
[Unit]
Description=Oltalama Backend Service
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/oltalama/backend
Environment=NODE_ENV=production
ExecStart=/usr/bin/node /opt/oltalama/backend/src/app.js
Restart=always
RestartSec=10
StandardOutput=append:/var/log/oltalama/backend.log
StandardError=append:/var/log/oltalama/backend-error.log
[Install]
WantedBy=multi-user.target
```
#### Frontend Service
`/etc/systemd/system/oltalama-frontend.service`:
```ini
[Unit]
Description=Oltalama Frontend Service
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/oltalama/frontend
Environment=NODE_ENV=production
ExecStart=/usr/bin/npm run preview
Restart=always
RestartSec=10
StandardOutput=append:/var/log/oltalama/frontend.log
StandardError=append:/var/log/oltalama/frontend-error.log
[Install]
WantedBy=multi-user.target
```
#### Servisleri Başlat
```bash
# Servisleri yükle
sudo systemctl daemon-reload
# Servisleri başlat
sudo systemctl start oltalama-backend
sudo systemctl start oltalama-frontend
# Başlangıçta otomatik başlasın
sudo systemctl enable oltalama-backend
sudo systemctl enable oltalama-frontend
# Durumu kontrol et
sudo systemctl status oltalama-backend
sudo systemctl status oltalama-frontend
```
## 🌐 Nginx Proxy Manager Kurulumu
### Portlar
- **Backend**: `http://localhost:3000`
- **Frontend**: `http://localhost:4173`
### Nginx Proxy Manager Ayarları
1. **Nginx Proxy Manager'a giriş yapın**
2. **Proxy Hosts****Add Proxy Host**
#### Frontend Proxy Host
```
Domain Names: yourdomain.com
Scheme: http
Forward Hostname/IP: localhost
Forward Port: 4173
Cache Assets: ✓
Block Common Exploits: ✓
Websockets Support: ✓
SSL:
- Force SSL: ✓
- HTTP/2 Support: ✓
- HSTS Enabled: ✓
```
#### Backend API Proxy (Eğer ayrı subdomain kullanacaksanız)
```
Domain Names: api.yourdomain.com
Scheme: http
Forward Hostname/IP: localhost
Forward Port: 3000
Block Common Exploits: ✓
Websockets Support: ✓
SSL:
- Force SSL: ✓
- HTTP/2 Support: ✓
- HSTS Enabled: ✓
```
**Custom Nginx Configuration (Advanced sekmesi):**
```nginx
location /api {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
```
## 🔒 Güvenlik Önerileri
### 1. Firewall Ayarları
```bash
# UFW kurulumu ve ayarları
sudo apt install ufw
# SSH izin ver (bağlantınızı koparmayın!)
sudo ufw allow 22/tcp
# Backend ve Frontend portları (sadece localhost'tan erişilebilir olmalı)
# Nginx Proxy Manager kullanıyorsanız bu portları kapatın
sudo ufw deny 3000/tcp
sudo ufw deny 4173/tcp
# HTTP/HTTPS (Nginx Proxy Manager için)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Firewall'ı aktifleştir
sudo ufw enable
```
### 2. Admin Şifresini Değiştir (Gerekirse)
Şifrenizi değiştirmek isterseniz:
```bash
# Backend dizinine git
cd /opt/oltalama/backend
# Yeni şifre oluşturma scripti
node -e "
const bcrypt = require('bcrypt');
const readline = require('readline');
const { sequelize } = require('./src/config/database');
const { AdminUser } = require('./src/models');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
(async () => {
await sequelize.authenticate();
rl.question('Admin kullanıcı adı: ', async (username) => {
rl.question('Yeni şifre: ', async (password) => {
const hashedPassword = await bcrypt.hash(password, 10);
await AdminUser.update(
{ password_hash: hashedPassword },
{ where: { username } }
);
console.log('Şifre değiştirildi!');
rl.close();
process.exit(0);
});
});
})();
"
```
### 3. Dosya İzinleri
```bash
# Proje dizini izinleri
sudo chown -R www-data:www-data /opt/oltalama
sudo chmod -R 755 /opt/oltalama
# .env dosyalarını koru
sudo chmod 600 /opt/oltalama/backend/.env
sudo chmod 600 /opt/oltalama/frontend/.env
# Database izinleri
sudo chmod 600 /opt/oltalama/backend/database/oltalama.db
```
### 4. Fail2Ban Kurulumu (Opsiyonel)
```bash
sudo apt install fail2ban
# /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
```
```ini
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
```
```bash
sudo systemctl restart fail2ban
```
### 5. Düzenli Güncellemeler
```bash
# Sistem güncellemeleri
sudo apt update && sudo apt upgrade -y
# Node paketleri
cd /opt/oltalama/backend && npm update
cd /opt/oltalama/frontend && npm update
# PM2 güncelleme
pm2 update
```
## 💾 Yedekleme
### 1. Database Yedekleme
```bash
# Manuel yedekleme
cp /opt/oltalama/backend/database/oltalama.db \
/opt/oltalama/backups/oltalama-$(date +%Y%m%d-%H%M%S).db
```
### 2. Otomatik Yedekleme Script
`/opt/oltalama/backup.sh`:
```bash
#!/bin/bash
BACKUP_DIR="/opt/oltalama/backups"
DB_PATH="/opt/oltalama/backend/database/oltalama.db"
DATE=$(date +%Y%m%d-%H%M%S)
# Backup dizini oluştur
mkdir -p $BACKUP_DIR
# Database yedekle
cp $DB_PATH "$BACKUP_DIR/oltalama-$DATE.db"
# 30 günden eski yedekleri sil
find $BACKUP_DIR -name "oltalama-*.db" -mtime +30 -delete
echo "Backup completed: oltalama-$DATE.db"
```
```bash
chmod +x /opt/oltalama/backup.sh
# Crontab ekle (her gün saat 03:00'te)
crontab -e
# Ekle:
0 3 * * * /opt/oltalama/backup.sh >> /var/log/oltalama/backup.log 2>&1
```
### 3. Tam Sistem Yedeği
```bash
# Tüm projeyi yedekle
tar -czf /backup/oltalama-full-$(date +%Y%m%d).tar.gz \
/opt/oltalama \
/etc/systemd/system/oltalama-*.service \
/var/log/oltalama
```
## 🔄 Güncelleme
### Git ile Güncelleme
```bash
cd /opt/oltalama
# Değişiklikleri al
git pull origin main
# Backend güncelleme
cd backend
npm install --production
node migrations/run-migrations.js
# Frontend güncelleme
cd ../frontend
npm install
npm run build
# Servisleri yeniden başlat
pm2 restart all
# veya
sudo systemctl restart oltalama-backend
sudo systemctl restart oltalama-frontend
```
## 📊 Monitoring ve Loglar
### PM2 Monitoring
```bash
# Dashboard
pm2 monit
# Memory/CPU kullanımı
pm2 list
```
### Log Dosyaları
```bash
# Backend logs
tail -f /var/log/oltalama/backend.log
tail -f /var/log/oltalama/backend-error.log
# Frontend logs
tail -f /var/log/oltalama/frontend.log
# PM2 logs
pm2 logs --lines 100
```
### Log Rotation
`/etc/logrotate.d/oltalama`:
```
/var/log/oltalama/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
pm2 reloadLogs
endscript
}
```
## 🐛 Sorun Giderme
### Port Zaten Kullanımda
```bash
# Port 3000'i kullanan process'i bul
sudo lsof -i :3000
# Process'i öldür
sudo kill -9 <PID>
```
### Database Locked
```bash
# SQLite lock'ları temizle
fuser -k /opt/oltalama/backend/database/oltalama.db
```
### PM2 Çalışmıyor
```bash
# PM2'yi sıfırla
pm2 kill
pm2 start ecosystem.config.js
pm2 save
```
### High Memory Usage
```bash
# Memory kullanımını kontrol et
pm2 list
# Uygulamayı yeniden başlat
pm2 restart oltalama-backend --update-env
```
## 👥 Kullanıcı Yönetimi
### Yeni Admin Kullanıcısı Ekleme
```bash
cd /opt/oltalama
node scripts/create-admin.js
```
### Admin Şifresi Değiştirme
```bash
cd /opt/oltalama
node scripts/change-password.js
```
### Admin Kullanıcısını Manuel Oluşturma (SQL)
```bash
# Şifre hash'i oluştur
node -p "require('bcrypt').hashSync('YourPassword123', 10)"
# SQLite ile kullanıcı ekle
cd /opt/oltalama/backend
sqlite3 database/oltalama.db
INSERT INTO admin_user (username, password_hash, full_name, created_at, updated_at)
VALUES ('newadmin', '$2b$10$...', 'New Admin', datetime('now'), datetime('now'));
.quit
```
### Tüm Admin Kullanıcılarını Listele
```bash
cd /opt/oltalama/backend
sqlite3 database/oltalama.db "SELECT id, username, full_name, email, created_at FROM admin_user;"
```
### Admin Kullanıcısı Sil
```bash
cd /opt/oltalama/backend
sqlite3 database/oltalama.db "DELETE FROM admin_user WHERE username='oldadmin';"
```
## 📝 Opsiyonel: Manuel Nginx Kurulumu
Nginx Proxy Manager yerine klasik Nginx kullanmak isterseniz:
### Nginx Kurulumu
```bash
sudo apt install nginx
```
### Nginx Konfigürasyonu
`/etc/nginx/sites-available/oltalama`:
```nginx
# Upstream tanımları
upstream backend {
server localhost:3000;
keepalive 64;
}
upstream frontend {
server localhost:4173;
keepalive 64;
}
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name yourdomain.com;
return 301 https://$server_name$request_uri;
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name yourdomain.com;
# SSL sertifikaları (Let's Encrypt ile oluşturun)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
# SSL ayarları
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Client body size
client_max_body_size 10M;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
# Backend API
location /api {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Tracking endpoint
location /t/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Frontend
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Logs
access_log /var/log/nginx/oltalama-access.log;
error_log /var/log/nginx/oltalama-error.log;
}
```
### Nginx'i Aktifleştir
```bash
# Symlink oluştur
sudo ln -s /etc/nginx/sites-available/oltalama /etc/nginx/sites-enabled/
# Konfigürasyonu test et
sudo nginx -t
# Nginx'i yeniden başlat
sudo systemctl restart nginx
sudo systemctl enable nginx
```
### SSL Sertifikası (Let's Encrypt)
```bash
# Certbot kurulumu
sudo apt install certbot python3-certbot-nginx
# Sertifika oluştur
sudo certbot --nginx -d yourdomain.com
# Otomatik yenileme
sudo certbot renew --dry-run
```
## ✅ Kurulum Kontrolü
### Sistemin Çalıştığını Kontrol Et
```bash
# Servis durumları
pm2 status
# veya
sudo systemctl status oltalama-backend
sudo systemctl status oltalama-frontend
# Port dinleme kontrolü
sudo netstat -tulpn | grep -E ':(3000|4173)'
# HTTP istekleri
curl http://localhost:3000/api/health
curl http://localhost:4173
# Domain kontrolü (Nginx Proxy Manager kurulduysa)
curl https://yourdomain.com
```
### Performans Testi
```bash
# Backend response time
time curl http://localhost:3000/api/health
# Memory kullanımı
free -h
# Disk kullanımı
df -h
```
## 📞 Destek
Sorun yaşarsanız:
1. Logları kontrol edin: `pm2 logs` veya `/var/log/oltalama/`
2. GitHub Issues'da sorun bildirin
3. Dokümanı tekrar gözden geçirin
## 📚 Ek Kaynaklar
- [PM2 Documentation](https://pm2.keymetrics.io/)
- [Nginx Documentation](https://nginx.org/en/docs/)
- [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices)
- [SQLite Documentation](https://www.sqlite.org/docs.html)
---
**Son Güncelleme**: 2025-11-10
**Versiyon**: 1.0.0

View File

@@ -25,7 +25,7 @@ npm run dev # ✅ Çalışıyor (background)
``` ```
**API:** http://localhost:3000 **API:** http://localhost:3000
**Default Admin:** admin / admin123 **Default Admin:** Kurulum sırasında oluşturulur
### Frontend ✅ TAMAMLANDI ### Frontend ✅ TAMAMLANDI
@@ -36,7 +36,7 @@ npm run dev # ✅ Çalışıyor (background)
``` ```
**UI:** http://localhost:5173 **UI:** http://localhost:5173
**Default Admin:** admin / admin123 **Default Admin:** Kurulum sırasında oluşturulur
## 📂 Proje Yapısı ## 📂 Proje Yapısı
@@ -178,9 +178,70 @@ curl http://localhost:3000/api/stats/dashboard
Sistem kullanıma hazır. Gmail ve Telegram ayarlarını yaparak phishing testlerinizi başlatabilirsiniz. Sistem kullanıma hazır. Gmail ve Telegram ayarlarını yaparak phishing testlerinizi başlatabilirsiniz.
## 🚀 Sunucu Kurulumu (Production)
### Otomatik Kurulum
```bash
cd /opt/oltalama
sudo ./deploy.sh
```
### Manuel Kurulum
Detaylı sunucu kurulum talimatları için:
```bash
cat DEPLOYMENT.md
```
**Önemli dosyalar:**
- `DEPLOYMENT.md` - Detaylı sunucu kurulum kılavuzu
- `deploy.sh` - Otomatik kurulum scripti
- `systemd/` - Systemd servis dosyaları
- `nginx/` - Nginx konfigürasyon örneği
**Portlar:**
- Backend: `3000` (değiştirilebilir)
- Frontend: `4173` (değiştirilebilir)
**Domain Seçenekleri:**
-**Tek Domain** (önerilen): `yourdomain.com` - Path-based routing
-**İki Domain**: `api.yourdomain.com` + `panel.yourdomain.com` - CORS aktif
**Process Manager:**
- ✅ PM2 (önerilen - otomatik restart, monitoring)
- ✅ Systemd (native Linux service)
**Reverse Proxy:**
- ✅ Nginx Proxy Manager (önerilen - GUI)
- ✅ Nginx (manuel konfig: `nginx/oltalama.conf`)
## 🛠️ Yardımcı Scriptler
### Admin Kullanıcı Yönetimi
```bash
# Yeni admin kullanıcısı oluştur
node scripts/create-admin.js
# Admin şifresini değiştir
node scripts/change-password.js
```
**Özellikler:**
- ✅ İnteraktif kullanıcı oluşturma
- ✅ Şifre güvenlik validasyonu (min 8 karakter, harf+rakam)
- ✅ Benzersiz kullanıcı adı kontrolü
- ✅ Güvenli bcrypt hash'leme
## 📚 Dokümantasyon ## 📚 Dokümantasyon
- **Ana Doküman:** `README.md` (bu dosya) - **Ana Doküman:** `README.md` (bu dosya)
- **Sunucu Kurulumu:** `DEPLOYMENT.md` 🚀 (Production kurulum)
- **Domain Yapılandırma:** `docs/DOMAIN_SETUP.md` 🌐 (Tek/İki domain)
- **Nginx Proxy Manager:** `docs/NGINX_PROXY_MANAGER.md` 🔄 (Reverse proxy)
- **Güvenlik Rehberi:** `SECURITY.md` 🔒 (Güvenlik en iyi uygulamaları)
- **Hızlı Başlangıç:** `QUICKSTART.md` ⚡ (5 dakika) - **Hızlı Başlangıç:** `QUICKSTART.md` ⚡ (5 dakika)
- **Kullanım Kılavuzu:** `KULLANIM.md` 📖 (Detaylı) - **Kullanım Kılavuzu:** `KULLANIM.md` 📖 (Detaylı)
- **Development Plan:** `devpan.md` 🏗️ (Teknik detay) - **Development Plan:** `devpan.md` 🏗️ (Teknik detay)

454
SECURITY.md Normal file
View File

@@ -0,0 +1,454 @@
# 🔒 Güvenlik Rehberi
Oltalama Test Yönetim Paneli için güvenlik en iyi uygulamaları ve önerileri.
## 🎯 Temel Güvenlik Prensipleri
### 1. Admin Kullanıcı Güvenliği
#### Güçlü Şifre Kullanımı
**Minimum Gereksinimler:**
- En az 8 karakter
- En az 1 büyük harf
- En az 1 küçük harf
- En az 1 rakam
- Önerilen: En az 1 özel karakter (!@#$%^&*)
**Önerilen Şifre Uzunluğu:** 12+ karakter
**Güçlü Şifre Örnekleri:**
-`S3cur3P@ssw0rd!`
-`MyT3st#2025!Pass`
-`Phish1ng$Test@2025`
**Zayıf Şifreler (KULLANMAYIN):**
-`admin123`
-`password`
-`12345678`
-`qwerty123`
#### Şifre Yönetimi
```bash
# Şifre değiştirme (90 günde bir önerilir)
cd /opt/oltalama
node scripts/change-password.js
# Yeni admin kullanıcısı ekleme
node scripts/create-admin.js
```
#### Varsayılan Kullanıcı
- ⚠️ **ÖNEMLİ:** Sistem varsayılan kullanıcı ile gelmiyor
- ✅ Kurulum sırasında güvenli bir kullanıcı oluşturmanız gerekir
- ✅ Şifreniz bcrypt ile hash'lenmiş olarak saklanır
### 2. Sunucu Güvenliği
#### Firewall Konfigürasyonu
```bash
# UFW kurulumu
sudo apt install ufw
# Gerekli portları
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
# Uygulama portlarını kapat (reverse proxy kullanın)
sudo ufw deny 3000/tcp # Backend
sudo ufw deny 4173/tcp # Frontend
# Firewall'ı aktifleştir
sudo ufw enable
# Durumu kontrol et
sudo ufw status
```
#### SSH Güvenliği
```bash
# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no # SSH key kullanın
PubkeyAuthentication yes
Port 22 # Veya özel bir port
# SSH servisini yeniden başlat
sudo systemctl restart sshd
```
#### Fail2Ban Kurulumu
```bash
# Fail2Ban kurulumu
sudo apt install fail2ban
# Konfigürasyon
sudo nano /etc/fail2ban/jail.local
```
```ini
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = 22
logpath = /var/log/auth.log
[nginx-http-auth]
enabled = true
filter = nginx-http-auth
port = http,https
logpath = /var/log/nginx/error.log
```
```bash
# Fail2Ban'ı başlat
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
# Durumu kontrol et
sudo fail2ban-client status
```
### 3. SSL/TLS Sertifikası
#### Let's Encrypt ile Ücretsiz SSL
```bash
# Certbot kurulumu
sudo apt install certbot python3-certbot-nginx
# Sertifika oluşturma
sudo certbot --nginx -d yourdomain.com
# Otomatik yenileme testi
sudo certbot renew --dry-run
```
#### SSL Konfigürasyonu
**Minimum TLS Versiyonu:** TLSv1.2 ve TLSv1.3
```nginx
# Nginx SSL ayarları
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;
# HSTS header
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
```
### 4. Database Güvenliği
#### Dosya İzinleri
```bash
# Database dosya izinleri
sudo chmod 600 /opt/oltalama/backend/database/oltalama.db
sudo chown www-data:www-data /opt/oltalama/backend/database/oltalama.db
# Backup dizini izinleri
sudo chmod 700 /opt/oltalama/backups
sudo chown www-data:www-data /opt/oltalama/backups
```
#### Düzenli Yedekleme
```bash
# Otomatik yedekleme scripti
# /opt/oltalama/backup.sh
#!/bin/bash
BACKUP_DIR="/opt/oltalama/backups"
DB_PATH="/opt/oltalama/backend/database/oltalama.db"
DATE=$(date +%Y%m%d-%H%M%S)
# Yedek al
cp $DB_PATH "$BACKUP_DIR/oltalama-$DATE.db"
# Eski yedekleri sil (30 günden eski)
find $BACKUP_DIR -name "oltalama-*.db" -mtime +30 -delete
# Backup'ı şifrele (opsiyonel)
gpg --symmetric --cipher-algo AES256 "$BACKUP_DIR/oltalama-$DATE.db"
rm "$BACKUP_DIR/oltalama-$DATE.db"
```
```bash
# Crontab ekle
crontab -e
# Ekle: Her gün saat 03:00'te yedek al
0 3 * * * /opt/oltalama/backup.sh >> /var/log/oltalama/backup.log 2>&1
```
### 5. Uygulama Güvenliği
#### Çevre Değişkenleri
```bash
# .env dosya izinleri
sudo chmod 600 /opt/oltalama/backend/.env
sudo chmod 600 /opt/oltalama/frontend/.env
# Sahibi ayarla
sudo chown www-data:www-data /opt/oltalama/backend/.env
sudo chown www-data:www-data /opt/oltalama/frontend/.env
```
#### Session Secret
```bash
# Güçlü session secret oluştur
node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
# .env dosyasına ekle
SESSION_SECRET=uzun-rastgele-gizli-anahtar-buraya
```
#### Rate Limiting
Uygulama zaten rate limiting kullanıyor:
- Login endpoint: 5 istek / dakika
- API endpoints: 100 istek / 15 dakika
#### Security Headers
Uygulama otomatik olarak şu güvenlik başlıklarını ekliyor:
- `X-Content-Type-Options: nosniff`
- `X-Frame-Options: SAMEORIGIN`
- `X-XSS-Protection: 1; mode=block`
- `Strict-Transport-Security` (HTTPS ile)
### 6. Gmail Güvenliği
#### App Password Kullanımı
**Doğru:** Gmail App Password kullanın
**Yanlış:** Normal Gmail şifresi kullanmayın
**App Password Oluşturma:**
1. Google Hesabı → Güvenlik
2. 2 Adımlı Doğrulama'yı aktifleştir
3. Uygulama Şifreleri → Mail
4. Oluşturulan şifreyi panele girin
**Güvenlik İpuçları:**
- App Password'ü asla paylaşmayın
- Düzenli olarak değiştirin (6 ayda bir)
- Kullanılmayan app password'leri silin
### 7. Telegram Bot Güvenliği
#### Bot Token Güvenliği
```bash
# .env dosyasında saklayın
TELEGRAM_BOT_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
# Asla git'e commit etmeyin
echo ".env" >> .gitignore
```
#### Chat ID Doğrulama
```bash
# Sadece belirli chat ID'lere bildirim gönderin
# Bot'u sadece bilinen kişilerle paylaşın
```
### 8. Log Yönetimi
#### Log Dosyalarını Koruma
```bash
# Log dizini izinleri
sudo chmod 750 /var/log/oltalama
sudo chown www-data:www-data /var/log/oltalama
# Log dosyaları izinleri
sudo chmod 640 /var/log/oltalama/*.log
```
#### Log Rotation
```bash
# /etc/logrotate.d/oltalama
/var/log/oltalama/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
pm2 reloadLogs
endscript
}
```
#### Hassas Bilgileri Loglamayın
- ❌ Şifreler
- ❌ Session token'lar
- ❌ API keys
- ❌ Kredi kartı bilgileri
### 9. Düzenli Güncellemeler
#### Sistem Güncellemeleri
```bash
# Sistem paketlerini güncelle
sudo apt update && sudo apt upgrade -y
# Güvenlik güncellemelerini otomatik yap
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
```
#### Uygulama Güncellemeleri
```bash
# Backend dependencies
cd /opt/oltalama/backend
npm audit
npm audit fix
# Frontend dependencies
cd /opt/oltalama/frontend
npm audit
npm audit fix
# PM2 güncelleme
sudo npm update -g pm2
pm2 update
```
### 10. Monitoring ve Alerting
#### PM2 Monitoring
```bash
# PM2 status
pm2 status
# Memory ve CPU kullanımı
pm2 monit
# Error logları izle
pm2 logs --err
```
#### Sistem Monitoring
```bash
# Disk kullanımı
df -h
# Memory kullanımı
free -h
# CPU kullanımı
top
# Network bağlantıları
sudo netstat -tulpn
```
## 🚨 Güvenlik Kontrol Listesi
### Kurulum Sonrası
- [ ] Güçlü admin şifresi oluşturuldu
- [ ] .env dosya izinleri ayarlandı (600)
- [ ] Database izinleri ayarlandı (600)
- [ ] Firewall yapılandırıldı
- [ ] SSL sertifikası kuruldu
- [ ] Fail2Ban kuruldu
- [ ] SSH key-based auth aktif
- [ ] Otomatik yedekleme ayarlandı
- [ ] Log rotation yapılandırıldı
### Aylık Kontroller
- [ ] Sistem güncellemeleri yapıldı
- [ ] npm audit çalıştırıldı
- [ ] Loglar gözden geçirildi
- [ ] Yedekler test edildi
- [ ] Disk kullanımı kontrol edildi
### Üç Aylık Kontroller
- [ ] Admin şifresi değiştirildi
- [ ] Gmail App Password yenilendi
- [ ] Kullanılmayan kullanıcılar silindi
- [ ] SSL sertifikası kontrol edildi
### Yıllık Kontroller
- [ ] Tam sistem denetimi yapıldı
- [ ] Güvenlik politikaları gözden geçirildi
- [ ] Yedekleme stratejisi gözden geçirildi
## 🔍 Güvenlik Olaylarına Müdahale
### Şüpheli Aktivite Tespiti
```bash
# Son giriş denemeleri
sudo tail -100 /var/log/auth.log
# Fail2Ban ban listesi
sudo fail2ban-client status sshd
# Nginx access logları
sudo tail -100 /var/log/nginx/oltalama-access.log
# Uygulama error logları
pm2 logs --err --lines 100
```
### Güvenlik İhlali Durumunda
1. **Hemen:**
- Tüm admin şifrelerini değiştir
- Gmail App Password'ü yenile
- Telegram Bot Token'ı yenile
- Session secret'ı değiştir ve tüm servisleri yeniden başlat
2. **Kısa Vadede:**
- Tüm logları yedekle
- Şüpheli IP adreslerini banla
- Sistem taraması yap
3. **Uzun Vadede:**
- Güvenlik politikalarını gözden geçir
- İhlal raporunu hazırla
- Ek güvenlik önlemleri al
## 📚 Ek Kaynaklar
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [Node.js Security Best Practices](https://nodejs.org/en/docs/guides/security/)
- [Express.js Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html)
- [Nginx Security Tips](https://nginx.org/en/docs/http/ngx_http_ssl_module.html)
---
**Son Güncelleme:** 2025-11-10
**Versiyon:** 1.0.0
⚠️ **UYARI:** Bu sistem sadece yasal ve etik phishing testleri için kullanılmalıdır. Kötü niyetli kullanım yasaktır ve suçtur.

View File

@@ -14,10 +14,41 @@ const PORT = process.env.PORT || 3000;
// Security middleware // Security middleware
app.use(helmet()); app.use(helmet());
app.use(cors({
origin: process.env.FRONTEND_URL || 'http://localhost:3001', // Dynamic CORS configuration (will be updated from settings)
let corsOptions = {
origin: process.env.FRONTEND_URL || 'http://localhost:5173',
credentials: true, credentials: true,
})); };
app.use((req, res, next) => {
cors(corsOptions)(req, res, next);
});
// Function to update CORS from database
const updateCorsFromSettings = async () => {
try {
const { Settings } = require('./models');
const corsEnabledSetting = await Settings.findOne({ where: { key: 'cors_enabled' } });
const frontendUrlSetting = await Settings.findOne({ where: { key: 'frontend_url' } });
if (corsEnabledSetting && corsEnabledSetting.value === 'true' && frontendUrlSetting) {
corsOptions.origin = frontendUrlSetting.value;
logger.info(`CORS enabled for: ${frontendUrlSetting.value}`);
} else {
// Default: allow both frontend and backend on same origin
corsOptions.origin = process.env.FRONTEND_URL || 'http://localhost:5173';
}
} catch (error) {
logger.warn('Could not load CORS settings from database, using defaults');
}
};
// Update CORS on startup (with delay to ensure DB is ready)
setTimeout(updateCorsFromSettings, 2000);
// Export for use in settings controller
app.updateCorsSettings = updateCorsFromSettings;
// Body parsing middleware // Body parsing middleware
app.use(express.json()); app.use(express.json());

View File

@@ -97,6 +97,89 @@ exports.updateTelegramSettings = async (req, res, next) => {
} }
}; };
// Update System settings (domain, etc.)
exports.updateSystemSettings = async (req, res, next) => {
try {
const { base_url, frontend_url, cors_enabled } = req.body;
if (base_url !== undefined) {
if (base_url) {
// Remove trailing slash if exists
const cleanUrl = base_url.trim().replace(/\/$/, '');
// Basic URL validation
try {
new URL(cleanUrl);
} catch (e) {
return res.status(400).json({
success: false,
error: 'Geçersiz Base URL formatı. Örnek: https://yourdomain.com',
});
}
await Settings.upsert({
key: 'base_url',
value: cleanUrl,
is_encrypted: false,
description: 'Base URL for tracking links (backend)',
});
// Update process.env for immediate use
process.env.BASE_URL = cleanUrl;
} else {
await Settings.destroy({ where: { key: 'base_url' } });
}
}
if (frontend_url !== undefined) {
if (frontend_url) {
// Remove trailing slash if exists
const cleanUrl = frontend_url.trim().replace(/\/$/, '');
// Basic URL validation
try {
new URL(cleanUrl);
} catch (e) {
return res.status(400).json({
success: false,
error: 'Geçersiz Frontend URL formatı. Örnek: https://panel.yourdomain.com',
});
}
await Settings.upsert({
key: 'frontend_url',
value: cleanUrl,
is_encrypted: false,
description: 'Frontend URL (for CORS)',
});
} else {
await Settings.destroy({ where: { key: 'frontend_url' } });
}
}
if (cors_enabled !== undefined) {
await Settings.upsert({
key: 'cors_enabled',
value: cors_enabled ? 'true' : 'false',
is_encrypted: false,
description: 'Enable CORS for separate domains',
});
}
// Update CORS configuration if available
if (req.app && req.app.updateCorsSettings) {
await req.app.updateCorsSettings();
}
res.json({
success: true,
message: 'Sistem ayarları güncellendi. CORS ayarları uygulandı.',
});
} catch (error) {
next(error);
}
};
// Test Gmail connection // Test Gmail connection
exports.testGmail = async (req, res, next) => { exports.testGmail = async (req, res, next) => {
try { try {

View File

@@ -30,6 +30,11 @@ const TrackingToken = sequelize.define('TrackingToken', {
type: DataTypes.STRING(50), type: DataTypes.STRING(50),
defaultValue: 'bank', defaultValue: 'bank',
}, },
from_name: {
type: DataTypes.STRING(255),
allowNull: true,
comment: 'Custom sender name for email (e.g., "HR Department", "Management")',
},
mail_subject: { mail_subject: {
type: DataTypes.STRING(500), type: DataTypes.STRING(500),
allowNull: true, allowNull: true,

View File

@@ -9,6 +9,7 @@ router.use(requireAuth);
router.get('/', settingsController.getAllSettings); router.get('/', settingsController.getAllSettings);
router.put('/gmail', settingsController.updateGmailSettings); router.put('/gmail', settingsController.updateGmailSettings);
router.put('/telegram', settingsController.updateTelegramSettings); router.put('/telegram', settingsController.updateTelegramSettings);
router.put('/system', settingsController.updateSystemSettings);
router.post('/test-gmail', settingsController.testGmail); router.post('/test-gmail', settingsController.testGmail);
router.post('/test-telegram', settingsController.testTelegram); router.post('/test-telegram', settingsController.testTelegram);

View File

@@ -45,14 +45,19 @@ class MailService {
} }
} }
async sendMail(to, subject, htmlBody) { async sendMail(to, subject, htmlBody, fromName = null) {
try { try {
if (!this.transporter) { if (!this.transporter) {
await this.initializeTransporter(); await this.initializeTransporter();
} }
// Use custom from_name if provided, otherwise use default
const from = fromName
? `${fromName} <${this.fromAddress.match(/<(.+)>/)?.[1] || this.fromAddress}>`
: this.fromAddress;
const mailOptions = { const mailOptions = {
from: this.fromAddress, from,
to, to,
subject, subject,
html: htmlBody, html: htmlBody,

View File

@@ -5,7 +5,7 @@ const logger = require('../config/logger');
class TokenService { class TokenService {
async createToken(data) { async createToken(data) {
const { company_id, target_email, employee_name, template_type } = data; const { company_id, target_email, employee_name, template_type, from_name } = data;
// Generate unique token // Generate unique token
let token = generateTrackingToken(); let token = generateTrackingToken();
@@ -45,6 +45,7 @@ class TokenService {
target_email, target_email,
employee_name, employee_name,
template_type, template_type,
from_name: from_name || null,
mail_subject: template.subject_template.replace('{{company_name}}', company.name), mail_subject: template.subject_template.replace('{{company_name}}', company.name),
}); });
@@ -78,8 +79,13 @@ class TokenService {
throw new Error('Mail template not found'); throw new Error('Mail template not found');
} }
// Get base URL from settings or env
const { Settings } = require('../models');
const baseUrlSetting = await Settings.findOne({ where: { key: 'base_url' } });
const baseUrl = baseUrlSetting?.value || process.env.BASE_URL || 'http://localhost:3000';
// Prepare template data // Prepare template data
const trackingUrl = `${process.env.BASE_URL}/t/${token.token}`; const trackingUrl = `${baseUrl}/t/${token.token}`;
const currentDate = new Date().toLocaleDateString('tr-TR', { const currentDate = new Date().toLocaleDateString('tr-TR', {
year: 'numeric', year: 'numeric',
month: 'long', month: 'long',
@@ -99,8 +105,8 @@ class TokenService {
const htmlBody = mailService.renderTemplate(template.body_html, templateData); const htmlBody = mailService.renderTemplate(template.body_html, templateData);
const subject = mailService.renderTemplate(template.subject_template, templateData); const subject = mailService.renderTemplate(template.subject_template, templateData);
// Send mail // Send mail with custom from_name if provided
await mailService.sendMail(token.target_email, subject, htmlBody); await mailService.sendMail(token.target_email, subject, htmlBody, token.from_name);
// Update token // Update token
await token.update({ await token.update({

View File

@@ -20,6 +20,13 @@ const createTokenSchema = Joi.object({
.max(255) .max(255)
.allow(null, '') .allow(null, '')
.optional(), .optional(),
from_name: Joi.string()
.max(255)
.allow(null, '')
.optional()
.messages({
'string.max': 'From name must be less than 255 characters',
}),
template_type: Joi.string() template_type: Joi.string()
.max(50) .max(50)
.default('bank') .default('bank')

562
deploy.sh Executable file
View File

@@ -0,0 +1,562 @@
#!/bin/bash
###############################################################################
# Oltalama Test Yönetim Paneli - Otomatik Kurulum Scripti
# Version: 1.0.0
# Date: 2025-11-10
###############################################################################
set -e # Exit on error
# Renkli çıktılar
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Değişkenler
INSTALL_DIR="/opt/oltalama"
LOG_DIR="/var/log/oltalama"
BACKUP_DIR="${INSTALL_DIR}/backups"
USER="www-data"
NODE_VERSION="20"
# Fonksiyonlar
print_header() {
echo -e "\n${BLUE}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ ║${NC}"
echo -e "${BLUE}║ Oltalama Test Yönetim Paneli - Kurulum Scripti ║${NC}"
echo -e "${BLUE}║ Version 1.0.0 ║${NC}"
echo -e "${BLUE}║ ║${NC}"
echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${NC}\n"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
check_root() {
if [[ $EUID -ne 0 ]]; then
print_error "Bu script root yetkisi ile çalıştırılmalıdır."
print_info "Lütfen 'sudo ./deploy.sh' komutunu kullanın."
exit 1
fi
}
check_os() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS=$ID
VERSION=$VERSION_ID
print_success "İşletim sistemi tespit edildi: $PRETTY_NAME"
else
print_error "İşletim sistemi tespit edilemedi!"
exit 1
fi
}
install_nodejs() {
print_info "Node.js ${NODE_VERSION}.x kuruluyor..."
if command -v node &> /dev/null; then
CURRENT_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
if [[ $CURRENT_VERSION -ge $NODE_VERSION ]]; then
print_success "Node.js $(node -v) zaten yüklü."
return
fi
fi
# NodeSource repository ekle
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
apt-get install -y nodejs
print_success "Node.js $(node -v) kuruldu."
print_success "npm $(npm -v) kuruldu."
}
install_dependencies() {
print_info "Sistem bağımlılıkları kuruluyor..."
apt-get update
apt-get install -y git curl wget build-essential sqlite3
print_success "Sistem bağımlılıkları kuruldu."
}
install_pm2() {
print_info "PM2 process manager kuruluyor..."
npm install -g pm2
print_success "PM2 $(pm2 -v) kuruldu."
}
setup_directories() {
print_info "Dizinler oluşturuluyor..."
mkdir -p $LOG_DIR
mkdir -p $BACKUP_DIR
mkdir -p "${INSTALL_DIR}/backend/database"
print_success "Dizinler oluşturuldu."
}
install_project() {
print_info "Proje dosyaları kontrol ediliyor..."
# Eğer script zaten proje dizininde çalıştırılıyorsa
if [[ -f "$(pwd)/backend/package.json" ]]; then
CURRENT_DIR=$(pwd)
print_info "Mevcut dizinden kurulum yapılıyor: $CURRENT_DIR"
if [[ "$CURRENT_DIR" != "$INSTALL_DIR" ]]; then
print_info "Dosyalar $INSTALL_DIR dizinine kopyalanıyor..."
rsync -av --exclude 'node_modules' --exclude '.git' \
"$CURRENT_DIR/" "$INSTALL_DIR/"
fi
else
print_error "Proje dosyaları bulunamadı!"
print_info "Script'i proje dizininde çalıştırın veya projeyi klonlayın."
exit 1
fi
print_success "Proje dosyaları hazır."
}
install_backend() {
print_info "Backend dependencies kuruluyor..."
cd "${INSTALL_DIR}/backend"
npm install --production
print_success "Backend dependencies kuruldu."
}
install_frontend() {
print_info "Frontend dependencies kuruluyor..."
cd "${INSTALL_DIR}/frontend"
npm install
print_success "Frontend dependencies kuruldu."
}
setup_env_files() {
print_info "Çevre değişkenleri ayarlanıyor..."
# Backend .env
if [[ ! -f "${INSTALL_DIR}/backend/.env" ]]; then
cp "${INSTALL_DIR}/backend/.env.example" "${INSTALL_DIR}/backend/.env"
# Session secret oluştur
SESSION_SECRET=$(node -e "console.log(require('crypto').randomBytes(64).toString('hex'))")
# .env dosyasını güncelle
sed -i "s|NODE_ENV=development|NODE_ENV=production|g" "${INSTALL_DIR}/backend/.env"
sed -i "s|SESSION_SECRET=.*|SESSION_SECRET=${SESSION_SECRET}|g" "${INSTALL_DIR}/backend/.env"
sed -i "s|DB_PATH=.*|DB_PATH=${INSTALL_DIR}/backend/database/oltalama.db|g" "${INSTALL_DIR}/backend/.env"
print_success "Backend .env dosyası oluşturuldu."
print_warning "Gmail ve Telegram ayarlarını panelden yapabilirsiniz."
else
print_info "Backend .env dosyası zaten mevcut."
fi
# Frontend .env
if [[ ! -f "${INSTALL_DIR}/frontend/.env" ]]; then
cp "${INSTALL_DIR}/frontend/.env.example" "${INSTALL_DIR}/frontend/.env"
print_success "Frontend .env dosyası oluşturuldu."
else
print_info "Frontend .env dosyası zaten mevcut."
fi
}
setup_admin_user() {
print_info "Admin kullanıcısı oluşturuluyor..."
# Check if admin already exists
ADMIN_COUNT=$(sqlite3 "${INSTALL_DIR}/backend/database/oltalama.db" "SELECT COUNT(*) FROM admin_user;" 2>/dev/null || echo "0")
if [[ "$ADMIN_COUNT" -gt 0 ]]; then
print_info "Admin kullanıcısı zaten mevcut, atlanıyor."
return
fi
echo ""
print_warning "╔═══════════════════════════════════════════════════════════════╗"
print_warning "║ Admin Kullanıcı Bilgilerini Oluşturun ║"
print_warning "╚═══════════════════════════════════════════════════════════════╝"
echo ""
# Username
while true; do
read -p "Admin kullanıcı adı (en az 3 karakter): " ADMIN_USERNAME
if [[ ${#ADMIN_USERNAME} -ge 3 ]]; then
break
else
print_error "Kullanıcı adı en az 3 karakter olmalıdır!"
fi
done
# Password with validation
while true; do
read -sp "Admin şifresi (en az 8 karakter, harf ve rakam içermeli): " ADMIN_PASSWORD
echo ""
# Password length check
if [[ ${#ADMIN_PASSWORD} -lt 8 ]]; then
print_error "Şifre en az 8 karakter olmalıdır!"
continue
fi
# Check for letters and numbers
if ! [[ "$ADMIN_PASSWORD" =~ [a-zA-Z] ]] || ! [[ "$ADMIN_PASSWORD" =~ [0-9] ]]; then
print_error "Şifre hem harf hem de rakam içermelidir!"
continue
fi
# Confirm password
read -sp "Şifreyi tekrar girin: " ADMIN_PASSWORD_CONFIRM
echo ""
if [[ "$ADMIN_PASSWORD" == "$ADMIN_PASSWORD_CONFIRM" ]]; then
break
else
print_error "Şifreler eşleşmiyor! Tekrar deneyin."
fi
done
# Create admin user using Node.js
cd "${INSTALL_DIR}/backend"
cat > /tmp/create_admin.js << EOF
const bcrypt = require('bcrypt');
const { sequelize } = require('./src/config/database');
const AdminUser = require('./src/models/AdminUser');
(async () => {
try {
await sequelize.authenticate();
const hashedPassword = await bcrypt.hash('${ADMIN_PASSWORD}', 10);
await AdminUser.create({
username: '${ADMIN_USERNAME}',
password_hash: hashedPassword,
email: null,
full_name: 'Administrator',
});
console.log('✓ Admin kullanıcısı oluşturuldu');
process.exit(0);
} catch (error) {
console.error('✗ Hata:', error.message);
process.exit(1);
}
})();
EOF
node /tmp/create_admin.js
rm -f /tmp/create_admin.js
print_success "Admin kullanıcısı oluşturuldu: ${ADMIN_USERNAME}"
echo ""
}
setup_database() {
print_info "Database oluşturuluyor..."
cd "${INSTALL_DIR}/backend"
# Migrations
node migrations/run-migrations.js
print_success "Database migrations tamamlandı."
# Seed sample data (companies and templates only, not admin user)
ADMIN_COUNT=$(sqlite3 "${INSTALL_DIR}/backend/database/oltalama.db" "SELECT COUNT(*) FROM admin_user;" 2>/dev/null || echo "0")
if [[ "$ADMIN_COUNT" -eq 0 ]]; then
# Seed only non-admin data
cat > /tmp/seed_data.js << 'EOF'
const { sequelize } = require('./src/config/database');
const { Company, MailTemplate } = require('./src/models');
(async () => {
try {
await sequelize.authenticate();
// Seed companies
const companies = [
{ name: 'Türk Telekom', domain: 'turktelekom.com.tr', description: 'Türkiye\'nin lider telekomünikasyon şirketi' },
{ name: 'İş Bankası', domain: 'isbank.com.tr', description: 'Türkiye\'nin en büyük özel sermayeli bankası' },
{ name: 'PTT', domain: 'ptt.gov.tr', description: 'Posta ve Telgraf Teşkilatı' },
];
for (const company of companies) {
await Company.findOrCreate({ where: { name: company.name }, defaults: company });
}
// Seed templates
const templates = [
{
name: 'Banka Güvenlik Uyarısı',
template_type: 'bank',
subject_template: '🔒 {{company_name}} - Hesap Güvenlik Uyarısı',
body_html: '<html><body style="font-family: Arial, sans-serif; line-height: 1.6;"><div style="max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd;"><h2 style="color: #d32f2f;">🔒 Güvenlik Uyarısı</h2><p>Sayın {{employee_name}},</p><p>{{company_name}} hesabınızda şüpheli bir aktivite tespit edildi. Hesabınızın güvenliğini sağlamak için lütfen aşağıdaki bağlantıya tıklayarak kimlik doğrulaması yapın.</p><p style="text-align: center; margin: 30px 0;"><a href="{{tracking_url}}" style="background-color: #d32f2f; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px; display: inline-block;">Hesabımı Doğrula</a></p><p style="color: #666; font-size: 12px;">Bu işlemi 24 saat içinde yapmazsanız hesabınız geçici olarak askıya alınabilir.</p><p style="color: #666; font-size: 12px;">Tarih: {{current_date}}</p><hr style="border: none; border-top: 1px solid #eee; margin: 20px 0;"><p style="color: #999; font-size: 11px;">Bu bir phishing testidir. Gerçek bir güvenlik tehdidi değildir.</p></div></body></html>',
description: 'Banka hesap güvenliği temalı phishing test maili',
active: true,
},
{
name: 'E-Devlet Sistem Güncellemesi',
template_type: 'edevlet',
subject_template: '⚠️ E-Devlet - Sistem Güncellemesi Gerekli',
body_html: '<html><body style="font-family: Arial, sans-serif; line-height: 1.6;"><div style="max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd;"><div style="background-color: #c62828; color: white; padding: 15px; text-align: center;"><h2 style="margin: 0;">E-DEVLET KAPISI</h2></div><div style="padding: 20px;"><p>Sayın {{employee_name}},</p><p>E-Devlet sistemimizde önemli bir güvenlik güncellemesi yapılmaktadır. Hesabınıza erişiminizi sürdürebilmek için lütfen kimlik bilgilerinizi güncelleyin.</p><p style="text-align: center; margin: 30px 0;"><a href="{{tracking_url}}" style="background-color: #c62828; color: white; padding: 12px 30px; text-decoration: none; border-radius: 5px; display: inline-block;">Bilgilerimi Güncelle</a></p><p style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 10px; color: #856404;">⚠️ Bu işlemi 48 saat içinde tamamlamazsanız E-Devlet hesabınız askıya alınacaktır.</p><p style="color: #666; font-size: 12px;">İşlem Tarihi: {{current_date}}</p></div><div style="background-color: #f5f5f5; padding: 15px; text-align: center; color: #666; font-size: 11px;"><p>Bu bir phishing farkındalık testidir.</p><p>© {{current_year}} E-Devlet Kapısı</p></div></div></body></html>',
description: 'E-Devlet sistem güncellemesi temalı phishing test maili',
active: true,
},
];
for (const template of templates) {
await MailTemplate.findOrCreate({ where: { template_type: template.template_type }, defaults: template });
}
console.log('✓ Örnek veriler oluşturuldu');
process.exit(0);
} catch (error) {
console.error('✗ Hata:', error.message);
process.exit(1);
}
})();
EOF
node /tmp/seed_data.js
rm -f /tmp/seed_data.js
print_success "Örnek veriler oluşturuldu (şirketler ve mail şablonları)."
else
print_info "Database zaten veri içeriyor, seed atlanıyor."
fi
}
build_frontend() {
print_info "Frontend build ediliyor..."
cd "${INSTALL_DIR}/frontend"
npm run build
print_success "Frontend build tamamlandı."
}
setup_pm2() {
print_info "PM2 konfigürasyonu yapılıyor..."
# ecosystem.config.js oluştur
cat > "${INSTALL_DIR}/ecosystem.config.js" << 'EOF'
module.exports = {
apps: [
{
name: 'oltalama-backend',
cwd: '/opt/oltalama/backend',
script: 'src/app.js',
instances: 1,
exec_mode: 'cluster',
watch: false,
max_memory_restart: '500M',
env: {
NODE_ENV: 'production',
PORT: 3000,
},
error_file: '/var/log/oltalama/backend-error.log',
out_file: '/var/log/oltalama/backend-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
},
{
name: 'oltalama-frontend',
cwd: '/opt/oltalama/frontend',
script: 'npm',
args: 'run preview',
instances: 1,
exec_mode: 'fork',
watch: false,
max_memory_restart: '300M',
env: {
NODE_ENV: 'production',
PORT: 4173,
},
error_file: '/var/log/oltalama/frontend-error.log',
out_file: '/var/log/oltalama/frontend-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
},
],
};
EOF
print_success "PM2 konfigürasyon dosyası oluşturuldu."
}
start_services() {
print_info "Servisler başlatılıyor..."
cd "${INSTALL_DIR}"
# PM2 ile başlat
pm2 start ecosystem.config.js
# Startup script oluştur
pm2 startup systemd -u root --hp /root
pm2 save
print_success "Servisler başlatıldı."
# Durum göster
sleep 2
pm2 status
}
setup_permissions() {
print_info "Dosya izinleri ayarlanıyor..."
# Dizin sahipliği
chown -R ${USER}:${USER} ${INSTALL_DIR}
chown -R ${USER}:${USER} ${LOG_DIR}
# .env dosyaları
chmod 600 "${INSTALL_DIR}/backend/.env"
chmod 600 "${INSTALL_DIR}/frontend/.env"
# Database
chmod 600 "${INSTALL_DIR}/backend/database/oltalama.db"
print_success "Dosya izinleri ayarlandı."
}
setup_firewall() {
print_info "Firewall ayarlanıyor..."
if command -v ufw &> /dev/null; then
# SSH izin ver
ufw allow 22/tcp comment 'SSH'
# HTTP/HTTPS izin ver
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
# Backend ve Frontend portlarını engelle (sadece localhost'tan erişilebilir)
ufw deny 3000/tcp comment 'Oltalama Backend (use reverse proxy)'
ufw deny 4173/tcp comment 'Oltalama Frontend (use reverse proxy)'
# UFW'yi aktifleştir (sadece henüz aktif değilse)
if ! ufw status | grep -q "Status: active"; then
print_warning "UFW firewall aktifleştiriliyor..."
print_warning "SSH bağlantınız kopmayacak (port 22 açık)."
read -p "Devam etmek için ENTER'a basın..."
echo "y" | ufw enable
fi
print_success "Firewall ayarlandı."
else
print_warning "UFW bulunamadı, firewall ayarları atlanıyor."
fi
}
setup_backup_cron() {
print_info "Otomatik yedekleme ayarlanıyor..."
# Backup script oluştur
cat > "${INSTALL_DIR}/backup.sh" << 'EOF'
#!/bin/bash
BACKUP_DIR="/opt/oltalama/backups"
DB_PATH="/opt/oltalama/backend/database/oltalama.db"
DATE=$(date +%Y%m%d-%H%M%S)
mkdir -p $BACKUP_DIR
cp $DB_PATH "$BACKUP_DIR/oltalama-$DATE.db"
find $BACKUP_DIR -name "oltalama-*.db" -mtime +30 -delete
echo "Backup completed: oltalama-$DATE.db"
EOF
chmod +x "${INSTALL_DIR}/backup.sh"
# Crontab ekle
CRON_JOB="0 3 * * * ${INSTALL_DIR}/backup.sh >> ${LOG_DIR}/backup.log 2>&1"
(crontab -l 2>/dev/null | grep -v "backup.sh"; echo "$CRON_JOB") | crontab -
print_success "Otomatik yedekleme ayarlandı (her gün saat 03:00)."
}
print_completion() {
echo -e "\n${GREEN}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}║ 🎉 Kurulum Tamamlandı! 🎉 ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════╝${NC}\n"
print_info "Servis Bilgileri:"
echo -e " Backend: ${BLUE}http://localhost:3000${NC}"
echo -e " Frontend: ${BLUE}http://localhost:4173${NC}"
print_info "\nAdmin Hesabı:"
echo -e " ${GREEN}${NC} Kurulum sırasında oluşturuldu"
echo -e " ${GREEN}${NC} Güvenli şifre kullanıldı"
print_info "\nYapılması Gerekenler:"
echo " 1. Nginx Proxy Manager'da reverse proxy ayarları yapın"
echo " 2. Frontend için: yourdomain.com → localhost:4173"
echo " 3. Backend API için path: /api → localhost:3000"
echo " 4. SSL sertifikası ekleyin"
echo " 5. Panele giriş yapın ve Gmail/Telegram ayarlarını yapın"
print_info "\nYararlı Komutlar:"
echo " Servis durumu: pm2 status"
echo " Logları izle: pm2 logs"
echo " Servis yeniden başlat: pm2 restart all"
echo " Servis durdur: pm2 stop all"
print_info "\nDokümantasyon:"
echo " Detaylı kurulum kılavuzu: ${INSTALL_DIR}/DEPLOYMENT.md"
echo " Kullanım kılavuzu: ${INSTALL_DIR}/KULLANIM.md"
echo " Hızlı başlangıç: ${INSTALL_DIR}/QUICKSTART.md"
print_success "\nKurulum başarıyla tamamlandı!"
}
# Ana kurulum fonksiyonu
main() {
print_header
check_root
check_os
print_info "Kurulum başlıyor...\n"
install_dependencies
install_nodejs
install_pm2
setup_directories
install_project
install_backend
install_frontend
setup_env_files
setup_database
setup_admin_user
build_frontend
setup_pm2
setup_permissions
start_services
setup_firewall
setup_backup_cron
print_completion
}
# Script'i çalıştır
main "$@"

396
docs/DOMAIN_SETUP.md Normal file
View File

@@ -0,0 +1,396 @@
# 🌐 Domain Yapılandırma Rehberi
Oltalama Panel için domain yapılandırma seçenekleri ve kurulum rehberi.
## 📋 İki Seçenek
### Seçenek 1: Tek Domain (Önerilen) ⭐
**Yapı:**
```
yourdomain.com/ → Frontend
yourdomain.com/api/ → Backend API
yourdomain.com/t/ → Tracking Links
```
**Avantajları:**
- ✅ CORS sorunu yok
- ✅ Tek SSL sertifikası
- ✅ Kolay kurulum
- ✅ Basit yönetim
**Dezavantajları:**
- ⚠️ Reverse proxy gerekli (Nginx Proxy Manager)
### Seçenek 2: İki Ayrı Domain
**Yapı:**
```
panel.yourdomain.com → Frontend
api.yourdomain.com → Backend API & Tracking
```
**Avantajları:**
- ✅ Servisler birbirinden izole
- ✅ Bağımsız ölçeklendirme
- ✅ Farklı sunucularda çalışabilir
**Dezavantajları:**
- ⚠️ CORS yapılandırması gerekli
- ⚠️ İki SSL sertifikası
- ⚠️ Daha karmaşık kurulum
## 🚀 Seçenek 1: Tek Domain Kurulumu
### 1. DNS Ayarları
```
yourdomain.com A 123.456.789.10
www.yourdomain.com CNAME yourdomain.com
```
### 2. Nginx Proxy Manager Kurulumu
**Proxy Host Ekle:**
```
Domain: yourdomain.com, www.yourdomain.com
Scheme: http
Forward Hostname: localhost
Forward Port: 4173
SSL: ✓ Force SSL, HTTP/2, HSTS
```
**Advanced Config:**
```nginx
location /api {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /t/ {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
```
### 3. Oltalama Panel Ayarları
Panele giriş yapın → **Ayarlar****Genel Ayarlar**
```
☐ İki Ayrı Domain Kullan (kapalı bırakın)
Domain (Base URL): https://yourdomain.com
```
### 4. Test
```bash
# Frontend
curl https://yourdomain.com
# Backend API
curl https://yourdomain.com/api/health
# Tracking
curl -I https://yourdomain.com/t/test-token
```
## 🔀 Seçenek 2: İki Domain Kurulumu
### 1. DNS Ayarları
```
api.yourdomain.com A 123.456.789.10
panel.yourdomain.com A 123.456.789.10
```
### 2. Nginx Proxy Manager - Backend
**Proxy Host Ekle (Backend):**
```
Domain: api.yourdomain.com
Scheme: http
Forward Hostname: localhost
Forward Port: 3000
SSL: ✓ Force SSL, HTTP/2, HSTS
```
### 3. Nginx Proxy Manager - Frontend
**Proxy Host Ekle (Frontend):**
```
Domain: panel.yourdomain.com
Scheme: http
Forward Hostname: localhost
Forward Port: 4173
SSL: ✓ Force SSL, HTTP/2, HSTS
```
### 4. Oltalama Panel Ayarları
Panele giriş yapın → **Ayarlar****Genel Ayarlar**
```
☑ İki Ayrı Domain Kullan (aktif et)
Backend Domain: https://api.yourdomain.com
Frontend Domain: https://panel.yourdomain.com
```
**Kaydet** butonuna tıklayın. CORS otomatik olarak yapılandırılacaktır.
### 5. Frontend Ortam Değişkenleri
`/opt/oltalama/frontend/.env`:
```env
VITE_API_URL=https://api.yourdomain.com
```
Frontend'i yeniden build edin:
```bash
cd /opt/oltalama/frontend
npm run build
pm2 restart oltalama-frontend
```
### 6. Test
```bash
# Frontend
curl https://panel.yourdomain.com
# Backend API
curl https://api.yourdomain.com/api/health
# Tracking
curl -I https://api.yourdomain.com/t/test-token
# CORS test
curl -I -X OPTIONS https://api.yourdomain.com/api/health \
-H "Origin: https://panel.yourdomain.com" \
-H "Access-Control-Request-Method: GET"
```
## 🔧 Port Ayarları
### Varsayılan Portlar
- **Backend**: `3000`
- **Frontend**: `4173`
- **Nginx Proxy Manager**: `80` (HTTP), `443` (HTTPS)
### Farklı Portlar Kullanma
Eğer backend veya frontend'i farklı portlarda çalıştırmak isterseniz:
#### Backend Port Değiştirme
**1. Backend .env:**
```env
PORT=8080
```
**2. PM2 Ecosystem Config:**
```javascript
{
name: 'oltalama-backend',
env: {
PORT: 8080,
},
}
```
**3. Nginx Proxy Manager:**
```
Forward Port: 8080
```
**4. Restart:**
```bash
pm2 restart oltalama-backend
```
#### Frontend Port Değiştirme
**1. Frontend package.json:**
```json
{
"scripts": {
"preview": "vite preview --port 5000"
}
}
```
**2. PM2 Ecosystem Config:**
```javascript
{
name: 'oltalama-frontend',
env: {
PORT: 5000,
},
}
```
**3. Nginx Proxy Manager:**
```
Forward Port: 5000
```
**4. Restart:**
```bash
pm2 restart oltalama-frontend
```
## 🔒 SSL/TLS Sertifikaları
### Let's Encrypt (Otomatik)
Nginx Proxy Manager üzerinden otomatik:
1. Proxy Host ayarlarına girin
2. **SSL** sekmesine gidin
3. **Request a new SSL Certificate** seçin
4. **Force SSL**, **HTTP/2**, **HSTS** aktif edin
5. Email girin ve **I Agree** işaretleyin
6. **Save**
Sertifika otomatik olarak yenilenecektir.
### Manuel SSL (Opsiyonel)
```bash
# Certbot kurulumu
sudo apt install certbot
# Sertifika oluşturma
sudo certbot certonly --standalone -d yourdomain.com
# Sertifikalar
/etc/letsencrypt/live/yourdomain.com/fullchain.pem
/etc/letsencrypt/live/yourdomain.com/privkey.pem
```
## 🐛 Sorun Giderme
### Problem: "Failed to load resource: net::ERR_BLOCKED_BY_CLIENT"
**Sebep:** AdBlock/uBlock Origin tracking URL'lerini engelliyor
**Çözüm:**
- Test için AdBlock'u devre dışı bırakın
- Veya tracking URL pattern'ini değiştirin
### Problem: CORS hatası
**Semptomlar:**
```
Access to XMLHttpRequest at 'https://api.yourdomain.com/api/...'
from origin 'https://panel.yourdomain.com' has been blocked by CORS policy
```
**Çözüm:**
1. Panelden CORS ayarlarını kontrol edin
2. **İki Ayrı Domain Kullan** aktif mi?
3. Frontend ve Backend URL'leri doğru mu?
4. Backend'i restart edin: `pm2 restart oltalama-backend`
### Problem: 502 Bad Gateway
**Çözüm:**
```bash
# Backend çalışıyor mu?
pm2 status
# Port dinleniyor mu?
sudo netstat -tulpn | grep 3000
# Firewall açık mı?
sudo ufw status
```
### Problem: Tracking linkleri çalışmıyor
**Çözüm:**
1. Panelde Base URL doğru ayarlanmış mı?
2. `/t/` route Nginx'de tanımlı mı?
3. Backend loglarını kontrol edin:
```bash
pm2 logs oltalama-backend --lines 50
```
## 📊 Karşılaştırma Tablosu
| Özellik | Tek Domain | İki Domain |
|---------|------------|------------|
| **Kurulum Süresi** | 15 dakika | 30 dakika |
| **CORS** | Gerekli değil | Gerekli |
| **SSL Sertifikası** | 1 tane | 2 tane |
| **Reverse Proxy** | Gerekli | Opsiyonel |
| **DNS Kaydı** | 1 domain | 2 subdomain |
| **Güvenlik** | Yüksek | Yüksek |
| **Ölçeklenebilirlik** | Orta | Yüksek |
| **Bakım** | Kolay | Orta |
| **Maliyet** | Düşük | Düşük |
| **Önerilen** | ✅ **Evet** | Özel durumlar |
## 🎯 Hangi Seçeneği Seçmeliyim?
### Tek Domain Seçin Eğer:
- ✅ Basit kurulum istiyorsanız
- ✅ CORS ile uğraşmak istemiyorsanız
- ✅ Tek sunucuda çalışacaksanız
- ✅ Yeni başlıyorsanız
### İki Domain Seçin Eğer:
- ✅ Frontend ve backend'i farklı sunucularda çalıştıracaksanız
- ✅ Servislerinizi izole etmek istiyorsanız
- ✅ Farklı takımlar frontend/backend'i yönetecekse
- ✅ Bağımsız ölçeklendirme gerekiyorsa
## 📝 Özet: Hızlı Kurulum
### Tek Domain (5 Adım)
```bash
# 1. Sunucu başlatın
pm2 start ecosystem.config.js
# 2. Nginx Proxy Manager'a domain ekleyin
# 3. SSL sertifikası alın (Let's Encrypt)
# 4. Panelde domain ayarlayın
# 5. Test edin
```
### İki Domain (8 Adım)
```bash
# 1. Sunucu başlatın
pm2 start ecosystem.config.js
# 2. Nginx Proxy Manager'a backend domain ekleyin
# 3. Nginx Proxy Manager'a frontend domain ekleyin
# 4. SSL sertifikaları alın (Let's Encrypt)
# 5. Frontend .env dosyasını güncelleyin
# 6. Frontend'i yeniden build edin
# 7. Panelde CORS ve domain ayarlarını yapın
# 8. Test edin
```
---
**Son Güncelleme:** 2025-11-10
**Versiyon:** 1.0.0
**İpucu:** İlk kurulumda **Tek Domain** kullanmanızı öneririz. İhtiyaç duyduğunuzda daha sonra **İki Domain**'e geçebilirsiniz.

438
docs/NGINX_PROXY_MANAGER.md Normal file
View File

@@ -0,0 +1,438 @@
# 🌐 Nginx Proxy Manager Kurulum Rehberi
Oltalama Panel için Nginx Proxy Manager (NPM) ile reverse proxy kurulumu.
## 📋 Neden Tek Domain?
**Önerilen Yapı:** Tek domain, path-based routing
### Avantajları:
-**CORS Sorunu Yok**: Frontend ve backend aynı origin'de
-**Tek SSL Sertifikası**: Sadece bir domain için sertifika
-**Basit Yönetim**: Tek entry point
-**Kolay Kurulum**: Daha az konfigürasyon
### Alternatif: İki Subdomain
Eğer iki ayrı subdomain kullanmak isterseniz:
- `panel.yourdomain.com` → Frontend
- `api.yourdomain.com` → Backend
**Not:** Bu durumda CORS ayarları gerekir ve iki SSL sertifikası gerekir.
## 🚀 Nginx Proxy Manager Kurulumu
### 1. Docker ile NPM Kurulumu
```bash
# Docker ve Docker Compose kurulumu (eğer yoksa)
sudo apt update
sudo apt install docker.io docker-compose -y
# NPM dizini oluştur
mkdir -p ~/nginx-proxy-manager
cd ~/nginx-proxy-manager
```
**docker-compose.yml:**
```yaml
version: '3'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80' # HTTP
- '443:443' # HTTPS
- '81:81' # Admin Panel
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
```
```bash
# NPM başlat
docker-compose up -d
# Logları izle
docker-compose logs -f
```
### 2. NPM Admin Paneline Giriş
1. Tarayıcıdan `http://sunucu-ip:81` adresine gidin
2. İlk giriş bilgileri:
- Email: `admin@example.com`
- Şifre: `changeme`
3. Giriş yaptıktan sonra **mutlaka** email ve şifreyi değiştirin
## 🔧 Tek Domain Konfigürasyonu (Önerilen)
### Adım 1: Proxy Host Ekle
NPM Admin Panel → **Hosts****Proxy Hosts****Add Proxy Host**
#### Details Sekmesi:
```
Domain Names: yourdomain.com
www.yourdomain.com
Scheme: http
Forward Hostname: localhost (veya Oltalama sunucusunun IP'si)
Forward Port: 4173
☑ Cache Assets
☑ Block Common Exploits
☑ Websockets Support
```
#### SSL Sekmesi:
```
SSL Certificate: Request a new SSL Certificate
☑ Force SSL
☑ HTTP/2 Support
☑ HSTS Enabled
☑ HSTS Subdomains
Email Address: youremail@example.com
☑ I Agree to the Let's Encrypt Terms of Service
```
#### Advanced Sekmesi:
**Custom Nginx Configuration:**
```nginx
# Backend API routing
location /api {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Tracking endpoint
location /t/ {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# No cache for tracking
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
# Health check endpoint
location /health {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
# Frontend static files
location / {
proxy_pass http://localhost:4173;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
```
### Adım 2: Oltalama Panel Ayarları
Panele giriş yapın → **Ayarlar****Genel Ayarlar**
```
Domain (Base URL): https://yourdomain.com
```
**Önemli:**
- Protokolü ekleyin: `https://`
- Sondaki `/` karakterini eklemeyin
- Subdomain kullanıyorsanız: `https://panel.yourdomain.com`
## 🔀 İki Domain Konfigürasyonu (Alternatif)
### Oltalama Panel Ayarları
Panele giriş yapın → **Ayarlar****Genel Ayarlar**
```
☑ İki Ayrı Domain Kullan (CORS Aktif Et)
Backend Domain: https://api.yourdomain.com
Frontend Domain: https://panel.yourdomain.com
```
**Önemli:**
- CORS checkbox'ını işaretleyin
- Her iki domain'i de `https://` ile girin
- Sondaki `/` karakterini eklemeyin
## 🔀 Nginx Proxy Manager - İki Domain Kurulumu
Eğer backend ve frontend'i ayırmak isterseniz:
### Backend Proxy Host
```
Domain Names: api.yourdomain.com
Scheme: http
Forward Hostname: localhost
Forward Port: 3000
SSL: ✓ Force SSL, HTTP/2, HSTS
```
### Frontend Proxy Host
```
Domain Names: panel.yourdomain.com
Scheme: http
Forward Hostname: localhost
Forward Port: 4173
SSL: ✓ Force SSL, HTTP/2, HSTS
```
**Advanced (Frontend):**
```nginx
# Proxy backend API calls
location /api {
proxy_pass https://api.yourdomain.com;
proxy_ssl_server_name on;
proxy_set_header Host api.yourdomain.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
```
### CORS Ayarları
İki domain kullanıyorsanız CORS otomatik olarak ayarlanır. Sadece panelden ayarları yapın:
**Panelden Ayarlama (Önerilen):**
1. Giriş yapın → **Ayarlar****Genel Ayarlar**
2. **☑ İki Ayrı Domain Kullan** checkbox'ını işaretleyin
3. **Backend Domain**: `https://api.yourdomain.com`
4. **Frontend Domain**: `https://panel.yourdomain.com`
5. **Kaydet** butonuna tıklayın
CORS ayarları otomatik olarak uygulanacaktır.
**Manuel .env Ayarları (Opsiyonel):**
Backend `.env` (varsayılan):
```env
FRONTEND_URL=http://localhost:5173
```
Production'da panel ayarları bu değeri override edecektir.
## ✅ Test ve Doğrulama
### 1. DNS Kontrolü
```bash
# Domain çözümleniyor mu?
nslookup yourdomain.com
# Ping testi
ping yourdomain.com
```
### 2. SSL Sertifikası Kontrolü
```bash
# SSL sertifikası geçerli mi?
curl -I https://yourdomain.com
# Detaylı SSL testi
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
```
### 3. Endpoint Testleri
```bash
# Frontend erişilebilir mi?
curl https://yourdomain.com
# Backend API erişilebilir mi?
curl https://yourdomain.com/api/health
# Tracking endpoint çalışıyor mu?
curl -I https://yourdomain.com/t/test-token
```
### 4. Tarayıcı Testleri
1. `https://yourdomain.com` adresine gidin
2. Login sayfasıılmalı
3. Developer Console'da hata olmamalı
4. Network sekmesinde:
- Frontend dosyaları (JS, CSS) yüklenmeli
- API istekleri `/api/*` başarılı olmalı
- CORS hatası olmamalı
## 🐛 Sorun Giderme
### Problem: 502 Bad Gateway
**Çözüm:**
```bash
# Backend çalışıyor mu?
pm2 status
# Port dinleniyor mu?
sudo netstat -tulpn | grep -E ':(3000|4173)'
# Firewall açık mı?
sudo ufw status
```
### Problem: SSL Sertifikası Alınamıyor
**Çözüm:**
1. DNS kayıtlarının doğru olduğundan emin olun
2. 80 ve 443 portlarının açık olduğunu kontrol edin
3. Domain'in sunucuyu gösterdiğinden emin olun
```bash
# Port 80 ve 443 dinleniyor mu?
sudo netstat -tulpn | grep -E ':(80|443)'
# Firewall kuralları
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
```
### Problem: CORS Hatası
**İki subdomain kullanıyorsanız:**
1. Backend `.env` dosyasında `CORS_ORIGIN` ayarlayın
2. Frontend'den API URL'i doğru mu kontrol edin
3. Backend'i yeniden başlatın
```bash
cd /opt/oltalama/backend
pm2 restart oltalama-backend
```
### Problem: Tracking Linkleri Çalışmıyor
**Çözüm:**
1. Panelde doğru domain ayarlandığından emin olun
2. `/t/` route'unun proxy'de tanımlı olduğunu kontrol edin
3. Backend loglarını kontrol edin:
```bash
pm2 logs oltalama-backend --lines 50
```
## 📊 Performans Optimizasyonu
### NPM Cache Ayarları
**Advanced Nginx Config:**
```nginx
# Static dosyalar için cache
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
proxy_pass http://localhost:4173;
expires 1y;
add_header Cache-Control "public, immutable";
}
# API istekleri için cache yok
location /api {
proxy_pass http://localhost:3000;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
```
### Rate Limiting
```nginx
# Rate limit tanımı
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
location /api {
limit_req zone=api_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://localhost:3000;
# ... diğer ayarlar
}
```
## 🔒 Güvenlik İpuçları
1. **NPM Admin Paneli Güvenliği:**
- Port 81'i firewall'dan kapatın (sadece lokal erişim)
- Güçlü şifre kullanın
- 2FA aktif edin (eğer varsa)
```bash
# NPM admin paneline sadece lokal erişim
sudo ufw deny 81/tcp
```
2. **SSL/TLS:**
- Her zaman Let's Encrypt kullanın
- Force SSL aktif edin
- HSTS etkinleştirin
3. **Headers:**
- NPM otomatik güvenlik başlıkları ekler
- Ek başlıklar için Advanced Nginx Config kullanın
## 📝 Özet Karşılaştırma
| Özellik | Tek Domain | İki Subdomain |
|---------|------------|---------------|
| CORS | ✅ Yok | ⚠️ Gerekli |
| SSL | ✅ 1 Sertifika | ⚠️ 2 Sertifika |
| Kurulum | ✅ Kolay | ⚠️ Orta |
| Yönetim | ✅ Basit | ⚠️ Karmaşık |
| DNS | ✅ 1 Kayıt | ⚠️ 2 Kayıt |
| Önerilen | ✅ **Evet** | ⚠️ İhtiyaç varsa |
## 🎯 Sonuç
**Önerimiz:** Tek domain kullanın (`yourdomain.com`)
- Frontend: `yourdomain.com/`
- Backend: `yourdomain.com/api/`
- Tracking: `yourdomain.com/t/`
Bu yapı en kolay ve en sorunsuz çözümdür.
---
**Son Güncelleme:** 2025-11-10
**Versiyon:** 1.0.0

View File

@@ -11,14 +11,19 @@ import {
Divider, Divider,
} from '@mui/material'; } from '@mui/material';
import { Save, Send } from '@mui/icons-material'; import { Save, Send } from '@mui/icons-material';
import { FormControlLabel, Checkbox } from '@mui/material';
import axios from 'axios'; import axios from 'axios';
const API_URL = import.meta.env.VITE_API_URL; const API_URL = import.meta.env.VITE_API_URL;
function Settings() { function Settings() {
const [settings, setSettings] = useState({ const [settings, setSettings] = useState({
base_url: '',
frontend_url: '',
cors_enabled: false,
gmail_user: '', gmail_user: '',
gmail_app_password: '', gmail_app_password: '',
gmail_from_name: '',
telegram_bot_token: '', telegram_bot_token: '',
telegram_chat_id: '', telegram_chat_id: '',
}); });
@@ -36,11 +41,24 @@ function Settings() {
withCredentials: true, withCredentials: true,
}); });
const data = response.data.data || {}; const data = response.data.data || {};
// Convert array to object
const settingsObj = {};
if (Array.isArray(data)) {
data.forEach(item => {
settingsObj[item.key] = item.value === '********' ? '' : item.value;
});
}
setSettings({ setSettings({
gmail_user: data.gmail_user || '', base_url: settingsObj.base_url || '',
gmail_app_password: data.gmail_app_password || '', frontend_url: settingsObj.frontend_url || '',
telegram_bot_token: data.telegram_bot_token || '', cors_enabled: settingsObj.cors_enabled === 'true',
telegram_chat_id: data.telegram_chat_id || '', gmail_user: settingsObj.gmail_user || '',
gmail_app_password: settingsObj.gmail_password || '',
gmail_from_name: settingsObj.gmail_from_name || '',
telegram_bot_token: settingsObj.telegram_bot_token || '',
telegram_chat_id: settingsObj.telegram_chat_id || '',
}); });
} catch (error) { } catch (error) {
console.error('Failed to load settings:', error); console.error('Failed to load settings:', error);
@@ -52,9 +70,15 @@ function Settings() {
const handleSave = async () => { const handleSave = async () => {
try { try {
await Promise.all([ await Promise.all([
axios.put(`${API_URL}/api/settings/system`, {
base_url: settings.base_url,
frontend_url: settings.frontend_url,
cors_enabled: settings.cors_enabled,
}, { withCredentials: true }),
axios.put(`${API_URL}/api/settings/gmail`, { axios.put(`${API_URL}/api/settings/gmail`, {
gmail_user: settings.gmail_user, gmail_user: settings.gmail_user,
gmail_app_password: settings.gmail_app_password, gmail_app_password: settings.gmail_app_password,
gmail_from_name: settings.gmail_from_name,
}, { withCredentials: true }), }, { withCredentials: true }),
axios.put(`${API_URL}/api/settings/telegram`, { axios.put(`${API_URL}/api/settings/telegram`, {
telegram_bot_token: settings.telegram_bot_token, telegram_bot_token: settings.telegram_bot_token,
@@ -64,7 +88,7 @@ function Settings() {
alert('Ayarlar kaydedildi!'); alert('Ayarlar kaydedildi!');
} catch (error) { } catch (error) {
console.error('Failed to save settings:', error); console.error('Failed to save settings:', error);
alert('Ayarlar kaydedilemedi'); alert('Ayarlar kaydedilemedi: ' + (error.response?.data?.error || error.message));
} }
}; };
@@ -124,10 +148,72 @@ function Settings() {
</Typography> </Typography>
<Grid container spacing={3}> <Grid container spacing={3}>
{/* System Settings */}
<Grid item xs={12}>
<Paper sx={{ p: 3 }}>
<Typography variant="h6" gutterBottom>
🌐 Genel Ayarlar
</Typography>
<Typography variant="body2" color="text.secondary" gutterBottom>
Domain ve genel sistem ayarları
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
<strong>Tek Domain (Önerilen):</strong> Frontend ve backend aynı domainde, path ile ayrılır<br/>
<strong>İki Domain:</strong> Frontend ve backend farklı domainlerde (CORS gerekir)
</Typography>
<FormControlLabel
control={
<Checkbox
checked={settings.cors_enabled}
onChange={(e) =>
setSettings({ ...settings, cors_enabled: e.target.checked })
}
/>
}
label="İki Ayrı Domain Kullan (CORS Aktif Et)"
/>
<TextField
fullWidth
margin="normal"
label={settings.cors_enabled ? "Backend Domain (Base URL)" : "Domain (Base URL)"}
type="url"
placeholder="https://yourdomain.com"
value={settings.base_url}
onChange={(e) =>
setSettings({ ...settings, base_url: e.target.value })
}
helperText={
settings.cors_enabled
? "Backend API domain'i. Örnek: https://api.yourdomain.com"
: "Hem frontend hem backend için kullanılacak domain. Örnek: https://yourdomain.com"
}
/>
{settings.cors_enabled && (
<TextField
fullWidth
margin="normal"
label="Frontend Domain"
type="url"
placeholder="https://panel.yourdomain.com"
value={settings.frontend_url}
onChange={(e) =>
setSettings({ ...settings, frontend_url: e.target.value })
}
helperText="Frontend panel domain'i. CORS için gerekli."
/>
)}
</Paper>
</Grid>
{/* Gmail Settings */}
<Grid item xs={12} md={6}> <Grid item xs={12} md={6}>
<Paper sx={{ p: 3 }}> <Paper sx={{ p: 3 }}>
<Typography variant="h6" gutterBottom> <Typography variant="h6" gutterBottom>
Gmail Ayarları 📧 Gmail Ayarları
</Typography> </Typography>
<Typography variant="body2" color="text.secondary" gutterBottom> <Typography variant="body2" color="text.secondary" gutterBottom>
Gmail App Password kullanın (2FA aktif olmalı) Gmail App Password kullanın (2FA aktif olmalı)
@@ -152,6 +238,18 @@ function Settings() {
onChange={(e) => onChange={(e) =>
setSettings({ ...settings, gmail_app_password: e.target.value }) setSettings({ ...settings, gmail_app_password: e.target.value })
} }
helperText="Google Hesap → Güvenlik → 2FA → Uygulama Şifreleri"
/>
<TextField
fullWidth
margin="normal"
label="Gönderen Adı (Varsayılan)"
value={settings.gmail_from_name}
onChange={(e) =>
setSettings({ ...settings, gmail_from_name: e.target.value })
}
placeholder="Güvenlik Ekibi"
helperText="Mail gönderirken görünecek varsayılan isim"
/> />
{alerts.mail && ( {alerts.mail && (

View File

@@ -247,6 +247,14 @@ function TokenDetail() {
</Typography> </Typography>
<Typography variant="body1">{token.company?.name}</Typography> <Typography variant="body1">{token.company?.name}</Typography>
</Grid> </Grid>
<Grid item xs={12} sm={6}>
<Typography variant="body2" color="textSecondary">
Gönderen Adı
</Typography>
<Typography variant="body1">
{token.from_name || 'Varsayılan'}
</Typography>
</Grid>
</Grid> </Grid>
</Paper> </Paper>

View File

@@ -37,6 +37,7 @@ function Tokens() {
company_id: '', company_id: '',
target_email: '', target_email: '',
employee_name: '', employee_name: '',
from_name: '',
template_type: 'bank', template_type: 'bank',
}); });
@@ -65,7 +66,7 @@ function Tokens() {
try { try {
await tokenService.createAndSend(formData); await tokenService.createAndSend(formData);
setOpenDialog(false); setOpenDialog(false);
setFormData({ company_id: '', target_email: '', employee_name: '', template_type: 'bank' }); setFormData({ company_id: '', target_email: '', employee_name: '', from_name: '', template_type: 'bank' });
loadData(); loadData();
alert('Token oluşturuldu ve mail gönderildi!'); alert('Token oluşturuldu ve mail gönderildi!');
} catch (error) { } catch (error) {
@@ -170,6 +171,15 @@ function Tokens() {
value={formData.employee_name} value={formData.employee_name}
onChange={(e) => setFormData({ ...formData, employee_name: e.target.value })} onChange={(e) => setFormData({ ...formData, employee_name: e.target.value })}
/> />
<TextField
margin="dense"
label="Gönderen Adı (Opsiyonel)"
fullWidth
value={formData.from_name}
onChange={(e) => setFormData({ ...formData, from_name: e.target.value })}
placeholder="Ör: X Şirketi İnsan Kaynakları, MGT Yönetici"
helperText="Belirtilmezse varsayılan gönderen adı kullanılır"
/>
<TextField <TextField
select select
margin="dense" margin="dense"

132
nginx/oltalama.conf Normal file
View File

@@ -0,0 +1,132 @@
# Oltalama Nginx Configuration
# /etc/nginx/sites-available/oltalama
# Upstream definitions
upstream backend {
server localhost:3000;
keepalive 64;
}
upstream frontend {
server localhost:4173;
keepalive 64;
}
# HTTP to HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name yourdomain.com;
# Let's Encrypt verification
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name yourdomain.com;
# SSL certificates (update with your actual paths)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/yourdomain.com/chain.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
# Client body size
client_max_body_size 10M;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml;
# Rate limiting zone
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_req_status 429;
# Backend API endpoints
location /api {
# Rate limiting
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Tracking endpoint (no rate limiting for legitimate tracking)
location /t/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Cache control
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
add_header Expires "0";
}
# Frontend static files
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
proxy_pass http://frontend;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# Logs
access_log /var/log/nginx/oltalama-access.log combined;
error_log /var/log/nginx/oltalama-error.log warn;
}

117
scripts/change-password.js Executable file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env node
/**
* Admin Password Change Script
* Usage: node scripts/change-password.js
*/
const bcrypt = require('bcrypt');
const readline = require('readline');
const path = require('path');
// Set correct path for database config
process.chdir(path.join(__dirname, '../backend'));
const { sequelize } = require('../backend/src/config/database');
const AdminUser = require('../backend/src/models/AdminUser');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (query) => new Promise((resolve) => rl.question(query, resolve));
const validatePassword = (password) => {
if (password.length < 8) {
return 'Şifre en az 8 karakter olmalıdır!';
}
if (!/[a-zA-Z]/.test(password)) {
return 'Şifre en az bir harf içermelidir!';
}
if (!/[0-9]/.test(password)) {
return 'Şifre en az bir rakam içermelidir!';
}
return null;
};
(async () => {
try {
console.log('\n╔═══════════════════════════════════════════════════════════════╗');
console.log('║ Oltalama Panel - Admin Şifre Değiştir ║');
console.log('╚═══════════════════════════════════════════════════════════════╝\n');
// Connect to database
await sequelize.authenticate();
console.log('✓ Database bağlantısı başarılı\n');
// Get username
const username = await question('Admin kullanıcı adı: ');
// Check if user exists
const user = await AdminUser.findOne({ where: { username } });
if (!user) {
console.log('✗ Kullanıcı bulunamadı!');
rl.close();
process.exit(1);
}
// Get new password
let password;
while (true) {
// Disable echo for password input
const originalWrite = rl._writeToOutput;
rl._writeToOutput = function (stringToWrite) {
if (stringToWrite.charCodeAt(0) === 13) {
rl.output.write('\n');
} else {
rl.output.write('*');
}
};
password = await question('Yeni şifre (en az 8 karakter, harf ve rakam): ');
console.log(); // New line after hidden input
const validationError = validatePassword(password);
if (validationError) {
console.log(`${validationError}\n`);
continue;
}
const passwordConfirm = await question('Şifreyi tekrar girin: ');
console.log(); // New line after hidden input
// Restore echo
rl._writeToOutput = originalWrite;
if (password !== passwordConfirm) {
console.log('✗ Şifreler eşleşmiyor!\n');
continue;
}
break;
}
// Update password
console.log('\n⏳ Şifre güncelleniyor...');
const hashedPassword = await bcrypt.hash(password, 10);
await user.update({
password_hash: hashedPassword,
});
console.log('\n✓ Şifre başarıyla değiştirildi!');
console.log(`\nKullanıcı: ${username}`);
console.log('Yeni şifre: ********** (güvenli bir şekilde saklandı)');
console.log('\n✓ Artık yeni şifrenizle giriş yapabilirsiniz.\n');
rl.close();
process.exit(0);
} catch (error) {
console.error('\n✗ Hata:', error.message);
rl.close();
process.exit(1);
}
})();

142
scripts/create-admin.js Executable file
View File

@@ -0,0 +1,142 @@
#!/usr/bin/env node
/**
* Admin User Creation Script
* Usage: node scripts/create-admin.js
*/
const bcrypt = require('bcrypt');
const readline = require('readline');
const path = require('path');
// Set correct path for database config
process.chdir(path.join(__dirname, '../backend'));
const { sequelize } = require('../backend/src/config/database');
const AdminUser = require('../backend/src/models/AdminUser');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (query) => new Promise((resolve) => rl.question(query, resolve));
const questionHidden = (query) => {
return new Promise((resolve) => {
rl.question(query, (answer) => {
resolve(answer);
});
rl._writeToOutput = () => {}; // Hide input
});
};
const validatePassword = (password) => {
if (password.length < 8) {
return 'Şifre en az 8 karakter olmalıdır!';
}
if (!/[a-zA-Z]/.test(password)) {
return 'Şifre en az bir harf içermelidir!';
}
if (!/[0-9]/.test(password)) {
return 'Şifre en az bir rakam içermelidir!';
}
return null;
};
(async () => {
try {
console.log('\n╔═══════════════════════════════════════════════════════════════╗');
console.log('║ Oltalama Panel - Admin Kullanıcı Oluştur ║');
console.log('╚═══════════════════════════════════════════════════════════════╝\n');
// Connect to database
await sequelize.authenticate();
console.log('✓ Database bağlantısı başarılı\n');
// Get username
let username;
while (true) {
username = await question('Admin kullanıcı adı (en az 3 karakter): ');
if (username.length >= 3) {
// Check if username exists
const existing = await AdminUser.findOne({ where: { username } });
if (existing) {
console.log('✗ Bu kullanıcı adı zaten kullanılıyor!\n');
continue;
}
break;
} else {
console.log('✗ Kullanıcı adı en az 3 karakter olmalıdır!\n');
}
}
// Get password
let password;
while (true) {
// Disable echo for password input
const originalWrite = rl._writeToOutput;
rl._writeToOutput = function (stringToWrite) {
if (stringToWrite.charCodeAt(0) === 13) {
rl.output.write('\n');
} else {
rl.output.write('*');
}
};
password = await question('Admin şifresi (en az 8 karakter, harf ve rakam): ');
console.log(); // New line after hidden input
const validationError = validatePassword(password);
if (validationError) {
console.log(`${validationError}\n`);
continue;
}
const passwordConfirm = await question('Şifreyi tekrar girin: ');
console.log(); // New line after hidden input
// Restore echo
rl._writeToOutput = originalWrite;
if (password !== passwordConfirm) {
console.log('✗ Şifreler eşleşmiyor!\n');
continue;
}
break;
}
// Get optional details
const fullName = await question('Tam ad (opsiyonel, ENTER ile geç): ');
const email = await question('E-posta (opsiyonel, ENTER ile geç): ');
// Create admin user
console.log('\n⏳ Admin kullanıcısı oluşturuluyor...');
const hashedPassword = await bcrypt.hash(password, 10);
await AdminUser.create({
username,
password_hash: hashedPassword,
email: email || null,
full_name: fullName || 'Administrator',
});
console.log('\n✓ Admin kullanıcısı başarıyla oluşturuldu!');
console.log(`\nKullanıcı adı: ${username}`);
console.log('Şifre: ********** (güvenli bir şekilde saklandı)');
if (fullName) console.log(`Tam ad: ${fullName}`);
if (email) console.log(`E-posta: ${email}`);
console.log('\n✓ Artık panele giriş yapabilirsiniz.\n');
rl.close();
process.exit(0);
} catch (error) {
console.error('\n✗ Hata:', error.message);
rl.close();
process.exit(1);
}
})();

View File

@@ -0,0 +1,29 @@
[Unit]
Description=Oltalama Backend Service
Documentation=https://github.com/yourusername/oltalama
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/oltalama/backend
Environment=NODE_ENV=production
Environment=PORT=3000
ExecStart=/usr/bin/node /opt/oltalama/backend/src/app.js
Restart=always
RestartSec=10
StandardOutput=append:/var/log/oltalama/backend.log
StandardError=append:/var/log/oltalama/backend-error.log
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/oltalama/backend/database
ReadWritePaths=/var/log/oltalama
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,27 @@
[Unit]
Description=Oltalama Frontend Service
Documentation=https://github.com/yourusername/oltalama
After=network.target oltalama-backend.service
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/oltalama/frontend
Environment=NODE_ENV=production
Environment=PORT=4173
ExecStart=/usr/bin/npm run preview
Restart=always
RestartSec=10
StandardOutput=append:/var/log/oltalama/frontend.log
StandardError=append:/var/log/oltalama/frontend-error.log
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
[Install]
WantedBy=multi-user.target