Compare commits
2 Commits
8ddd6a983f
...
f0b61735cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0b61735cc | ||
|
|
c62478937e |
61
.dockerignore
Normal file
61
.dockerignore
Normal file
@@ -0,0 +1,61 @@
|
||||
# Node modules
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Database (will be in volume)
|
||||
database/
|
||||
*.db
|
||||
*.db-journal
|
||||
|
||||
# Build artifacts
|
||||
dist/
|
||||
build/
|
||||
.cache/
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
docs/
|
||||
|
||||
# Docker
|
||||
Dockerfile*
|
||||
docker-compose*.yml
|
||||
.dockerignore
|
||||
|
||||
# Deployment
|
||||
deploy.sh
|
||||
systemd/
|
||||
nginx/
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
@@ -338,7 +338,7 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
User=oltalama
|
||||
WorkingDirectory=/opt/oltalama/backend
|
||||
Environment=NODE_ENV=production
|
||||
ExecStart=/usr/bin/node /opt/oltalama/backend/src/app.js
|
||||
@@ -362,7 +362,7 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
User=oltalama
|
||||
WorkingDirectory=/opt/oltalama/frontend
|
||||
Environment=NODE_ENV=production
|
||||
ExecStart=/usr/bin/npm run preview
|
||||
@@ -522,7 +522,7 @@ const rl = readline.createInterface({
|
||||
|
||||
```bash
|
||||
# Proje dizini izinleri
|
||||
sudo chown -R www-data:www-data /opt/oltalama
|
||||
sudo chown -R oltalama:oltalama /opt/oltalama
|
||||
sudo chmod -R 755 /opt/oltalama
|
||||
|
||||
# .env dosyalarını koru
|
||||
@@ -687,7 +687,7 @@ pm2 logs --lines 100
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 0640 www-data www-data
|
||||
create 0640 oltalama oltalama
|
||||
sharedscripts
|
||||
postrotate
|
||||
pm2 reloadLogs
|
||||
|
||||
509
DOCKER.md
Normal file
509
DOCKER.md
Normal file
@@ -0,0 +1,509 @@
|
||||
# 🐳 Docker Deployment Guide
|
||||
|
||||
## Oltalama Test Yönetim Paneli - Docker ile Kurulum
|
||||
|
||||
Bu dokümantasyon, projeyi Docker ve Docker Compose kullanarak nasıl çalıştıracağınızı açıklar.
|
||||
|
||||
---
|
||||
|
||||
## 📋 İçindekiler
|
||||
|
||||
- [Gereksinimler](#gereksinimler)
|
||||
- [Hızlı Başlangıç](#hızlı-başlangıç)
|
||||
- [Development Modu](#development-modu)
|
||||
- [Production Modu](#production-modu)
|
||||
- [Ortam Değişkenleri](#ortam-değişkenleri)
|
||||
- [Veri Yedekleme](#veri-yedekleme)
|
||||
- [İleri Seviye Kullanım](#ileri-seviye-kullanım)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Gereksinimler
|
||||
|
||||
- **Docker**: 20.10 veya üzeri
|
||||
- **Docker Compose**: 2.0 veya üzeri
|
||||
- **Git**: (projeyi klonlamak için)
|
||||
|
||||
### Docker Kurulumu
|
||||
|
||||
#### Ubuntu/Debian:
|
||||
```bash
|
||||
# Docker kurulumu
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
|
||||
# Docker Compose kurulumu (eğer yoksa)
|
||||
sudo apt-get install docker-compose-plugin
|
||||
|
||||
# Kullanıcıyı docker grubuna ekle
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
#### RedHat/Oracle/CentOS:
|
||||
```bash
|
||||
# Docker kurulumu
|
||||
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
|
||||
# Docker'ı başlat ve aktifleştir
|
||||
sudo systemctl start docker
|
||||
sudo systemctl enable docker
|
||||
|
||||
# Kullanıcıyı docker grubuna ekle
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Hızlı Başlangıç
|
||||
|
||||
### 1. Projeyi Klonlayın
|
||||
|
||||
```bash
|
||||
git clone <repository-url> oltalama
|
||||
cd oltalama
|
||||
```
|
||||
|
||||
### 2. Environment Dosyasını Hazırlayın
|
||||
|
||||
```bash
|
||||
# Örnek dosyadan kopyalayın
|
||||
cat > .env << 'EOF'
|
||||
SESSION_SECRET=your-strong-random-secret-here
|
||||
GMAIL_USER=your-email@gmail.com
|
||||
GMAIL_APP_PASSWORD=your-gmail-app-password
|
||||
TELEGRAM_BOT_TOKEN=your-bot-token
|
||||
TELEGRAM_CHAT_ID=your-chat-id
|
||||
OLLAMA_SERVER_URL=http://host.docker.internal:11434
|
||||
OLLAMA_MODEL=llama3.2:latest
|
||||
VITE_API_URL=http://localhost:3000
|
||||
EOF
|
||||
|
||||
# Editör ile açın ve konfigüre edin
|
||||
nano .env
|
||||
```
|
||||
|
||||
**Minimum gerekli ayarlar:**
|
||||
```env
|
||||
SESSION_SECRET=very-strong-random-secret-here
|
||||
GMAIL_USER=your-email@gmail.com
|
||||
GMAIL_APP_PASSWORD=your-gmail-app-password
|
||||
TELEGRAM_BOT_TOKEN=your-bot-token
|
||||
TELEGRAM_CHAT_ID=your-chat-id
|
||||
```
|
||||
|
||||
### 3. Production Modunda Başlatın
|
||||
|
||||
```bash
|
||||
# Container'ları build edin ve başlatın
|
||||
docker compose up -d
|
||||
|
||||
# Logları görüntüleyin
|
||||
docker compose logs -f
|
||||
```
|
||||
|
||||
### 4. Admin Kullanıcısı Oluşturun
|
||||
|
||||
İlk çalıştırmada admin kullanıcısı oluşturmanız gerekir:
|
||||
|
||||
```bash
|
||||
# Backend container'a bağlanın
|
||||
docker compose exec backend sh
|
||||
|
||||
# Admin kullanıcı scripti çalıştırın
|
||||
node scripts/create-admin.js
|
||||
|
||||
# Container'dan çıkın
|
||||
exit
|
||||
```
|
||||
|
||||
### 5. Uygulamaya Erişin
|
||||
|
||||
- **Frontend**: http://localhost:4173
|
||||
- **Backend API**: http://localhost:3000
|
||||
- **Health Check**: http://localhost:3000/health
|
||||
|
||||
---
|
||||
|
||||
## 💻 Development Modu
|
||||
|
||||
Development modunda hot-reload aktif olur ve kod değişiklikleri anında yansır.
|
||||
|
||||
### Başlatma
|
||||
|
||||
```bash
|
||||
# Development compose ile başlat
|
||||
docker compose -f docker-compose.dev.yml up
|
||||
|
||||
# Veya arka planda çalıştır
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
### Özellikler
|
||||
|
||||
- ✅ **Hot Reload**: Kod değişiklikleri anında yansır
|
||||
- ✅ **Volume Mount**: Local klasörler container'a mount edilir
|
||||
- ✅ **Debug Modu**: Detaylı log çıktıları
|
||||
- ✅ **Nodemon**: Backend için otomatik restart
|
||||
|
||||
### Development Portları
|
||||
|
||||
- **Frontend (Vite Dev Server)**: http://localhost:5173
|
||||
- **Backend**: http://localhost:3000
|
||||
|
||||
---
|
||||
|
||||
## 🏭 Production Modu
|
||||
|
||||
Production modu için optimize edilmiş, multi-stage build ile küçük image boyutu.
|
||||
|
||||
### Başlatma
|
||||
|
||||
```bash
|
||||
# Production compose ile başlat
|
||||
docker compose up -d
|
||||
|
||||
# Durum kontrolü
|
||||
docker compose ps
|
||||
|
||||
# Logları görüntüle
|
||||
docker compose logs -f backend
|
||||
docker compose logs -f frontend
|
||||
```
|
||||
|
||||
### Container Yönetimi
|
||||
|
||||
```bash
|
||||
# Tüm servisleri durdur
|
||||
docker compose stop
|
||||
|
||||
# Tüm servisleri başlat
|
||||
docker compose start
|
||||
|
||||
# Tüm servisleri yeniden başlat
|
||||
docker compose restart
|
||||
|
||||
# Servisleri kaldır (veri korunur)
|
||||
docker compose down
|
||||
|
||||
# Servisleri ve volume'leri kaldır (VERİ SİLİNİR!)
|
||||
docker compose down -v
|
||||
```
|
||||
|
||||
### Build İşlemleri
|
||||
|
||||
```bash
|
||||
# Image'ları yeniden build et
|
||||
docker compose build
|
||||
|
||||
# Cache kullanmadan build et
|
||||
docker compose build --no-cache
|
||||
|
||||
# Belirli bir servisi build et
|
||||
docker compose build backend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Ortam Değişkenleri
|
||||
|
||||
### Backend Değişkenleri
|
||||
|
||||
| Değişken | Açıklama | Varsayılan | Zorunlu |
|
||||
|----------|----------|------------|---------|
|
||||
| `NODE_ENV` | Çalışma ortamı | `production` | ❌ |
|
||||
| `PORT` | Backend portu | `3000` | ❌ |
|
||||
| `SESSION_SECRET` | Session şifreleme anahtarı | - | ✅ |
|
||||
| `GMAIL_USER` | Gmail hesabı | - | ✅ |
|
||||
| `GMAIL_APP_PASSWORD` | Gmail uygulama şifresi | - | ✅ |
|
||||
| `TELEGRAM_BOT_TOKEN` | Telegram bot token | - | ✅ |
|
||||
| `TELEGRAM_CHAT_ID` | Telegram chat ID | - | ✅ |
|
||||
| `DOMAIN_URL` | Backend domain | `http://localhost:3000` | ❌ |
|
||||
| `FRONTEND_URL` | Frontend domain | `http://localhost:4173` | ❌ |
|
||||
| `OLLAMA_SERVER_URL` | Ollama AI server URL | `http://host.docker.internal:11434` | ❌ |
|
||||
| `OLLAMA_MODEL` | Ollama model adı | `llama3.2:latest` | ❌ |
|
||||
|
||||
### Frontend Değişkenleri
|
||||
|
||||
| Değişken | Açıklama | Varsayılan | Zorunlu |
|
||||
|----------|----------|------------|---------|
|
||||
| `VITE_API_URL` | Backend API URL | `http://localhost:3000` | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 💾 Veri Yedekleme
|
||||
|
||||
### Manuel Yedekleme
|
||||
|
||||
```bash
|
||||
# Database yedekle
|
||||
docker compose exec backend sh -c "cd database && tar czf /tmp/backup.tar.gz *.db"
|
||||
docker compose cp backend:/tmp/backup.tar.gz ./backup-$(date +%Y%m%d).tar.gz
|
||||
|
||||
# Logs yedekle
|
||||
docker compose exec backend sh -c "cd logs && tar czf /tmp/logs-backup.tar.gz *.log"
|
||||
docker compose cp backend:/tmp/logs-backup.tar.gz ./logs-backup-$(date +%Y%m%d).tar.gz
|
||||
```
|
||||
|
||||
### Otomatik Yedekleme (Cron)
|
||||
|
||||
```bash
|
||||
# Yedekleme scripti oluştur
|
||||
cat > backup.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
BACKUP_DIR="/backup/oltalama"
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
# Database backup
|
||||
docker compose exec -T backend sh -c "cd database && tar czf - *.db" > "$BACKUP_DIR/db-$DATE.tar.gz"
|
||||
|
||||
# Eski yedekleri sil (30 günden eski)
|
||||
find $BACKUP_DIR -name "db-*.tar.gz" -mtime +30 -delete
|
||||
|
||||
echo "Backup completed: $DATE"
|
||||
EOF
|
||||
|
||||
chmod +x backup.sh
|
||||
|
||||
# Crontab'a ekle (her gün 02:00'de)
|
||||
(crontab -l 2>/dev/null; echo "0 2 * * * /path/to/backup.sh >> /var/log/oltalama-backup.log 2>&1") | crontab -
|
||||
```
|
||||
|
||||
### Geri Yükleme
|
||||
|
||||
```bash
|
||||
# Database geri yükle
|
||||
docker compose cp ./backup-20250101.tar.gz backend:/tmp/
|
||||
docker compose exec backend sh -c "cd database && tar xzf /tmp/backup-20250101.tar.gz"
|
||||
docker compose restart backend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 İleri Seviye Kullanım
|
||||
|
||||
### Nginx ile Reverse Proxy
|
||||
|
||||
Nginx reverse proxy eklemek için:
|
||||
|
||||
```bash
|
||||
# Nginx profili ile başlat
|
||||
docker compose --profile with-nginx up -d
|
||||
```
|
||||
|
||||
`nginx.conf` dosyasını düzenleyin:
|
||||
|
||||
```nginx
|
||||
# nginx/nginx.conf
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://frontend: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;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://backend:3000/api/;
|
||||
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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
|
||||
Production ortamında resource limitleri ekleyin:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml içine ekle
|
||||
services:
|
||||
backend:
|
||||
# ... diğer ayarlar
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
```
|
||||
|
||||
### Health Check Monitoring
|
||||
|
||||
Container'ların sağlık durumunu izleyin:
|
||||
|
||||
```bash
|
||||
# Health status kontrolü
|
||||
docker compose ps
|
||||
|
||||
# Detaylı health check logları
|
||||
docker inspect --format='{{json .State.Health}}' oltalama-backend | jq
|
||||
```
|
||||
|
||||
### Ollama ile Kullanım
|
||||
|
||||
Eğer Ollama host makinede çalışıyorsa:
|
||||
|
||||
```bash
|
||||
# Host'tan Ollama'ya erişim için
|
||||
# .env dosyasında:
|
||||
OLLAMA_SERVER_URL=http://host.docker.internal:11434
|
||||
OLLAMA_MODEL=llama3.2:latest
|
||||
```
|
||||
|
||||
Ollama'yı da Docker'da çalıştırmak için:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml'e ekle
|
||||
services:
|
||||
ollama:
|
||||
image: ollama/ollama:latest
|
||||
container_name: oltalama-ollama
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "11434:11434"
|
||||
volumes:
|
||||
- ollama-data:/root/.ollama
|
||||
networks:
|
||||
- oltalama-network
|
||||
|
||||
volumes:
|
||||
ollama-data:
|
||||
driver: local
|
||||
```
|
||||
|
||||
Sonra `.env` dosyasında:
|
||||
```env
|
||||
OLLAMA_SERVER_URL=http://ollama:11434
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Problem: Container başlamıyor
|
||||
|
||||
```bash
|
||||
# Logları kontrol et
|
||||
docker compose logs backend
|
||||
docker compose logs frontend
|
||||
|
||||
# Container'ı interaktif başlat
|
||||
docker compose run --rm backend sh
|
||||
```
|
||||
|
||||
### Problem: Database erişim hatası
|
||||
|
||||
```bash
|
||||
# Volume'leri kontrol et
|
||||
docker volume ls
|
||||
docker volume inspect oltalama_backend-data
|
||||
|
||||
# İzinleri düzelt
|
||||
docker compose exec backend chown -R oltalama:oltalama /app/database
|
||||
```
|
||||
|
||||
### Problem: Port conflict
|
||||
|
||||
```bash
|
||||
# Kullanılan portları kontrol et
|
||||
sudo lsof -i :3000
|
||||
sudo lsof -i :4173
|
||||
|
||||
# Farklı port kullan
|
||||
docker compose up -d --force-recreate
|
||||
```
|
||||
|
||||
### Problem: Image boyutu çok büyük
|
||||
|
||||
```bash
|
||||
# Kullanılmayan image'ları temizle
|
||||
docker system prune -a
|
||||
|
||||
# Build cache'i temizle
|
||||
docker builder prune
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Monitoring
|
||||
|
||||
### Container İstatistikleri
|
||||
|
||||
```bash
|
||||
# Gerçek zamanlı resource kullanımı
|
||||
docker stats
|
||||
|
||||
# Belirli container'lar için
|
||||
docker stats oltalama-backend oltalama-frontend
|
||||
```
|
||||
|
||||
### Log Yönetimi
|
||||
|
||||
```bash
|
||||
# Son 100 satır
|
||||
docker compose logs --tail=100 backend
|
||||
|
||||
# Gerçek zamanlı takip
|
||||
docker compose logs -f
|
||||
|
||||
# Belirli bir zaman aralığı
|
||||
docker compose logs --since="2025-01-01T00:00:00"
|
||||
|
||||
# Belirli bir service
|
||||
docker compose logs backend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Production Deployment Checklist
|
||||
|
||||
- [ ] `.env` dosyasını production değerleri ile doldur
|
||||
- [ ] `SESSION_SECRET` için güçlü random string oluştur
|
||||
- [ ] Gmail App Password oluştur ve ayarla
|
||||
- [ ] Telegram bot token ve chat ID ayarla
|
||||
- [ ] Domain URL'lerini güncelle
|
||||
- [ ] Nginx reverse proxy yapılandır (opsiyonel)
|
||||
- [ ] SSL/TLS sertifikası ekle
|
||||
- [ ] Firewall kurallarını ayarla
|
||||
- [ ] Admin kullanıcı oluştur
|
||||
- [ ] Otomatik yedekleme sistemi kur
|
||||
- [ ] Health check monitoring kur
|
||||
- [ ] Log rotation yapılandır
|
||||
|
||||
---
|
||||
|
||||
## 📚 Ek Kaynaklar
|
||||
|
||||
- [Docker Documentation](https://docs.docker.com/)
|
||||
- [Docker Compose Documentation](https://docs.docker.com/compose/)
|
||||
- [Best Practices for Node.js in Docker](https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md)
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Destek
|
||||
|
||||
Sorun yaşıyorsanız:
|
||||
|
||||
1. Logları kontrol edin: `docker compose logs`
|
||||
2. Container durumunu kontrol edin: `docker compose ps`
|
||||
3. Health check durumunu kontrol edin
|
||||
4. Issue açın veya dokümantasyonu inceleyin
|
||||
|
||||
---
|
||||
|
||||
**🎉 Artık Docker ile hazırsınız!**
|
||||
|
||||
44
README.md
44
README.md
@@ -188,20 +188,53 @@ cd /opt/oltalama
|
||||
sudo ./deploy.sh
|
||||
```
|
||||
|
||||
### Manuel Kurulum
|
||||
### 🐳 Docker ile Deployment (Önerilen)
|
||||
|
||||
Detaylı sunucu kurulum talimatları için:
|
||||
Docker kullanarak tek komutla deploy edin (tüm platformlarda çalışır):
|
||||
|
||||
```bash
|
||||
cat DEPLOYMENT.md
|
||||
# 1. .env dosyası oluştur
|
||||
nano .env
|
||||
# SESSION_SECRET, GMAIL, TELEGRAM ayarlarını girin
|
||||
|
||||
# 2. Servisleri başlat
|
||||
docker compose up -d
|
||||
|
||||
# 3. Admin kullanıcı oluştur
|
||||
docker compose exec backend node scripts/create-admin.js
|
||||
|
||||
# 4. Erişim
|
||||
# Frontend: http://localhost:4173
|
||||
# Backend: http://localhost:3000
|
||||
```
|
||||
|
||||
**Development Modu (Hot Reload):**
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml up
|
||||
```
|
||||
|
||||
**Detaylı Döküman:** `DOCKER.md` 📦
|
||||
|
||||
---
|
||||
|
||||
### 🖥️ Native Deployment (Linux Sunucu)
|
||||
|
||||
Otomatik deployment scripti ile:
|
||||
|
||||
```bash
|
||||
sudo bash deploy.sh # Tüm Linux dağıtımları desteklenir
|
||||
```
|
||||
|
||||
**Önemli dosyalar:**
|
||||
- `DEPLOYMENT.md` - Detaylı sunucu kurulum kılavuzu
|
||||
- `deploy.sh` - Otomatik kurulum scripti
|
||||
- `deploy.sh` - Otomatik kurulum scripti (apt & dnf/yum)
|
||||
- `DOCKER.md` - Docker deployment kılavuzu 🐳
|
||||
- `systemd/` - Systemd servis dosyaları
|
||||
- `nginx/` - Nginx konfigürasyon örneği
|
||||
|
||||
**Desteklenen Sistemler:**
|
||||
- ✅ Ubuntu, Debian, Oracle Linux, RHEL, CentOS, Fedora
|
||||
|
||||
**Portlar:**
|
||||
- Backend: `3000` (değiştirilebilir)
|
||||
- Frontend: `4173` (değiştirilebilir)
|
||||
@@ -239,7 +272,8 @@ node scripts/change-password.js
|
||||
## 📚 Dokümantasyon
|
||||
|
||||
- **Ana Doküman:** `README.md` (bu dosya)
|
||||
- **Sunucu Kurulumu:** `DEPLOYMENT.md` 🚀 (Production kurulum)
|
||||
- **Docker Deployment:** `DOCKER.md` 🐳 (Docker kurulum ve yönetim)
|
||||
- **Sunucu Kurulumu:** `DEPLOYMENT.md` 🚀 (Native Linux kurulum)
|
||||
- **Ollama AI Entegrasyonu:** `OLLAMA_SETUP.md` 🤖 (AI mail şablon oluşturma)
|
||||
- **Domain Yapılandırma:** `docs/DOMAIN_SETUP.md` 🌐 (Tek/İki domain)
|
||||
- **Nginx Proxy Manager:** `docs/NGINX_PROXY_MANAGER.md` 🔄 (Reverse proxy)
|
||||
|
||||
12
SECURITY.md
12
SECURITY.md
@@ -155,11 +155,11 @@ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; prelo
|
||||
```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
|
||||
sudo chown oltalama:oltalama /opt/oltalama/backend/database/oltalama.db
|
||||
|
||||
# Backup dizini izinleri
|
||||
sudo chmod 700 /opt/oltalama/backups
|
||||
sudo chown www-data:www-data /opt/oltalama/backups
|
||||
sudo chown oltalama:oltalama /opt/oltalama/backups
|
||||
```
|
||||
|
||||
#### Düzenli Yedekleme
|
||||
@@ -201,8 +201,8 @@ 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
|
||||
sudo chown oltalama:oltalama /opt/oltalama/backend/.env
|
||||
sudo chown oltalama:oltalama /opt/oltalama/frontend/.env
|
||||
```
|
||||
|
||||
#### Session Secret
|
||||
@@ -273,7 +273,7 @@ echo ".env" >> .gitignore
|
||||
```bash
|
||||
# Log dizini izinleri
|
||||
sudo chmod 750 /var/log/oltalama
|
||||
sudo chown www-data:www-data /var/log/oltalama
|
||||
sudo chown oltalama:oltalama /var/log/oltalama
|
||||
|
||||
# Log dosyaları izinleri
|
||||
sudo chmod 640 /var/log/oltalama/*.log
|
||||
@@ -290,7 +290,7 @@ sudo chmod 640 /var/log/oltalama/*.log
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 0640 www-data www-data
|
||||
create 0640 oltalama oltalama
|
||||
sharedscripts
|
||||
postrotate
|
||||
pm2 reloadLogs
|
||||
|
||||
43
backend/.dockerignore
Normal file
43
backend/.dockerignore
Normal file
@@ -0,0 +1,43 @@
|
||||
# Node modules
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# Database (will be in volume)
|
||||
database/
|
||||
*.db
|
||||
*.db-journal
|
||||
|
||||
# Logs (will be in volume)
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# Migrations (already applied)
|
||||
migrations/
|
||||
seeders/
|
||||
|
||||
# Temporary
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
54
backend/Dockerfile
Normal file
54
backend/Dockerfile
Normal file
@@ -0,0 +1,54 @@
|
||||
# Backend Dockerfile
|
||||
# Multi-stage build for optimized production image
|
||||
|
||||
# Stage 1: Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Stage 2: Production stage
|
||||
FROM node:20-alpine
|
||||
|
||||
# Install required packages
|
||||
RUN apk add --no-cache \
|
||||
sqlite \
|
||||
tini
|
||||
|
||||
# Create app user
|
||||
RUN addgroup -g 1001 -S oltalama && \
|
||||
adduser -S oltalama -u 1001 -G oltalama
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy dependencies from builder
|
||||
COPY --from=builder /app/node_modules ./node_modules
|
||||
|
||||
# Copy application code
|
||||
COPY --chown=oltalama:oltalama . .
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p database logs && \
|
||||
chown -R oltalama:oltalama /app
|
||||
|
||||
# Switch to non-root user
|
||||
USER oltalama
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||||
|
||||
# Use tini as entrypoint for proper signal handling
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
|
||||
# Start application
|
||||
CMD ["node", "src/app.js"]
|
||||
|
||||
33
backend/Dockerfile.dev
Normal file
33
backend/Dockerfile.dev
Normal file
@@ -0,0 +1,33 @@
|
||||
# Backend Development Dockerfile
|
||||
# Hot reload enabled with nodemon
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
# Install required packages
|
||||
RUN apk add --no-cache \
|
||||
sqlite \
|
||||
tini
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install all dependencies (including dev)
|
||||
RUN npm install
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Create necessary directories
|
||||
RUN mkdir -p database logs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Use tini as entrypoint
|
||||
ENTRYPOINT ["/sbin/tini", "--"]
|
||||
|
||||
# Start with nodemon for hot reload
|
||||
CMD ["npm", "run", "dev"]
|
||||
|
||||
83
deploy.sh
83
deploy.sh
@@ -19,7 +19,7 @@ NC='\033[0m' # No Color
|
||||
INSTALL_DIR="/opt/oltalama"
|
||||
LOG_DIR="/var/log/oltalama"
|
||||
BACKUP_DIR="${INSTALL_DIR}/backups"
|
||||
USER="www-data"
|
||||
USER="oltalama" # Tüm sistemler için ortak kullanıcı
|
||||
NODE_VERSION="20"
|
||||
|
||||
# Fonksiyonlar
|
||||
@@ -62,6 +62,21 @@ check_os() {
|
||||
OS=$ID
|
||||
VERSION=$VERSION_ID
|
||||
print_success "İşletim sistemi tespit edildi: $PRETTY_NAME"
|
||||
|
||||
# Paket yöneticisi tespit et
|
||||
if command -v apt-get &> /dev/null; then
|
||||
PKG_MANAGER="apt"
|
||||
print_info "Paket yöneticisi: APT (Debian/Ubuntu)"
|
||||
elif command -v dnf &> /dev/null; then
|
||||
PKG_MANAGER="dnf"
|
||||
print_info "Paket yöneticisi: DNF (RedHat/Oracle/Fedora)"
|
||||
elif command -v yum &> /dev/null; then
|
||||
PKG_MANAGER="yum"
|
||||
print_info "Paket yöneticisi: YUM (RedHat/CentOS)"
|
||||
else
|
||||
print_error "Desteklenen paket yöneticisi bulunamadı!"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_error "İşletim sistemi tespit edilemedi!"
|
||||
exit 1
|
||||
@@ -80,8 +95,13 @@ install_nodejs() {
|
||||
fi
|
||||
|
||||
# NodeSource repository ekle
|
||||
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
apt-get install -y nodejs
|
||||
if [[ "$PKG_MANAGER" == "apt" ]]; then
|
||||
curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
apt-get install -y nodejs
|
||||
elif [[ "$PKG_MANAGER" == "dnf" ]] || [[ "$PKG_MANAGER" == "yum" ]]; then
|
||||
curl -fsSL https://rpm.nodesource.com/setup_${NODE_VERSION}.x | bash -
|
||||
$PKG_MANAGER install -y nodejs
|
||||
fi
|
||||
|
||||
print_success "Node.js $(node -v) kuruldu."
|
||||
print_success "npm $(npm -v) kuruldu."
|
||||
@@ -89,8 +109,20 @@ install_nodejs() {
|
||||
|
||||
install_dependencies() {
|
||||
print_info "Sistem bağımlılıkları kuruluyor..."
|
||||
apt-get update
|
||||
apt-get install -y git curl wget build-essential sqlite3
|
||||
|
||||
if [[ "$PKG_MANAGER" == "apt" ]]; then
|
||||
apt-get update
|
||||
apt-get install -y git curl wget build-essential sqlite3
|
||||
elif [[ "$PKG_MANAGER" == "dnf" ]] || [[ "$PKG_MANAGER" == "yum" ]]; then
|
||||
$PKG_MANAGER update -y
|
||||
# EPEL repository (bazı paketler için gerekli)
|
||||
$PKG_MANAGER install -y epel-release 2>/dev/null || true
|
||||
# Paketleri kur
|
||||
$PKG_MANAGER install -y git curl wget gcc-c++ make sqlite
|
||||
# Development tools
|
||||
$PKG_MANAGER groupinstall -y "Development Tools" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
print_success "Sistem bağımlılıkları kuruldu."
|
||||
}
|
||||
|
||||
@@ -110,6 +142,18 @@ setup_directories() {
|
||||
print_success "Dizinler oluşturuldu."
|
||||
}
|
||||
|
||||
create_user() {
|
||||
print_info "Sistem kullanıcısı kontrol ediliyor..."
|
||||
|
||||
if id "$USER" &>/dev/null; then
|
||||
print_success "Kullanıcı '$USER' zaten mevcut."
|
||||
else
|
||||
print_info "Kullanıcı '$USER' oluşturuluyor..."
|
||||
useradd -r -s /bin/bash -d $INSTALL_DIR -m $USER
|
||||
print_success "Kullanıcı '$USER' oluşturuldu."
|
||||
fi
|
||||
}
|
||||
|
||||
install_project() {
|
||||
print_info "Proje dosyaları kontrol ediliyor..."
|
||||
|
||||
@@ -441,6 +485,9 @@ setup_firewall() {
|
||||
print_info "Firewall ayarlanıyor..."
|
||||
|
||||
if command -v ufw &> /dev/null; then
|
||||
# Ubuntu/Debian - UFW
|
||||
print_info "UFW kullanılıyor..."
|
||||
|
||||
# SSH izin ver
|
||||
ufw allow 22/tcp comment 'SSH'
|
||||
|
||||
@@ -460,9 +507,30 @@ setup_firewall() {
|
||||
echo "y" | ufw enable
|
||||
fi
|
||||
|
||||
print_success "Firewall ayarlandı."
|
||||
print_success "Firewall ayarlandı (UFW)."
|
||||
elif command -v firewall-cmd &> /dev/null; then
|
||||
# RedHat/Oracle/CentOS - firewalld
|
||||
print_info "firewalld kullanılıyor..."
|
||||
|
||||
# firewalld'yi başlat ve aktifleştir
|
||||
systemctl start firewalld 2>/dev/null || true
|
||||
systemctl enable firewalld 2>/dev/null || true
|
||||
|
||||
# HTTP/HTTPS izin ver
|
||||
firewall-cmd --permanent --add-service=http
|
||||
firewall-cmd --permanent --add-service=https
|
||||
firewall-cmd --permanent --add-service=ssh
|
||||
|
||||
# Backend ve Frontend portları sadece localhost'tan erişilebilir
|
||||
# (reverse proxy kullanılacağı için dışarıdan erişim kapalı)
|
||||
|
||||
# Yeniden yükle
|
||||
firewall-cmd --reload
|
||||
|
||||
print_success "Firewall ayarlandı (firewalld)."
|
||||
else
|
||||
print_warning "UFW bulunamadı, firewall ayarları atlanıyor."
|
||||
print_warning "Firewall bulunamadı, firewall ayarları atlanıyor."
|
||||
print_warning "Güvenlik için manuel olarak firewall yapılandırmanız önerilir."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -540,6 +608,7 @@ main() {
|
||||
install_dependencies
|
||||
install_nodejs
|
||||
install_pm2
|
||||
create_user
|
||||
setup_directories
|
||||
install_project
|
||||
install_backend
|
||||
|
||||
67
docker-compose.dev.yml
Normal file
67
docker-compose.dev.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
version: '3.8'
|
||||
|
||||
# Development Docker Compose
|
||||
# Hot reload enabled, volumes mounted
|
||||
|
||||
services:
|
||||
# Backend Service (Development)
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile.dev
|
||||
container_name: oltalama-backend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- PORT=3000
|
||||
- SESSION_SECRET=dev-secret-change-in-production
|
||||
- GMAIL_USER=${GMAIL_USER}
|
||||
- GMAIL_APP_PASSWORD=${GMAIL_APP_PASSWORD}
|
||||
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
|
||||
- DOMAIN_URL=http://localhost:3000
|
||||
- FRONTEND_URL=http://localhost:5173
|
||||
- OLLAMA_SERVER_URL=${OLLAMA_SERVER_URL:-http://host.docker.internal:11434}
|
||||
- OLLAMA_MODEL=${OLLAMA_MODEL:-llama3.2:latest}
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
- /app/node_modules
|
||||
- backend-data:/app/database
|
||||
- backend-logs:/app/logs
|
||||
networks:
|
||||
- oltalama-network
|
||||
command: npm run dev
|
||||
|
||||
# Frontend Service (Development)
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile.dev
|
||||
container_name: oltalama-frontend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5173:5173"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- VITE_API_URL=http://localhost:3000
|
||||
volumes:
|
||||
- ./frontend:/app
|
||||
- /app/node_modules
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- oltalama-network
|
||||
command: npm run dev
|
||||
|
||||
volumes:
|
||||
backend-data:
|
||||
driver: local
|
||||
backend-logs:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
oltalama-network:
|
||||
driver: bridge
|
||||
|
||||
89
docker-compose.yml
Normal file
89
docker-compose.yml
Normal file
@@ -0,0 +1,89 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# Backend Service
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: oltalama-backend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
- SESSION_SECRET=${SESSION_SECRET:-change-this-secret-in-production}
|
||||
- GMAIL_USER=${GMAIL_USER}
|
||||
- GMAIL_APP_PASSWORD=${GMAIL_APP_PASSWORD}
|
||||
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
||||
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
|
||||
- DOMAIN_URL=${DOMAIN_URL:-http://localhost:3000}
|
||||
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:4173}
|
||||
- OLLAMA_SERVER_URL=${OLLAMA_SERVER_URL:-http://host.docker.internal:11434}
|
||||
- OLLAMA_MODEL=${OLLAMA_MODEL:-llama3.2:latest}
|
||||
volumes:
|
||||
- backend-data:/app/database
|
||||
- backend-logs:/app/logs
|
||||
networks:
|
||||
- oltalama-network
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Frontend Service
|
||||
frontend:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
container_name: oltalama-frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "4173:4173"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- VITE_API_URL=${VITE_API_URL:-http://localhost:3000}
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- oltalama-network
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:4173"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Nginx Reverse Proxy (Optional)
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: oltalama-nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/ssl:/etc/nginx/ssl:ro
|
||||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
networks:
|
||||
- oltalama-network
|
||||
profiles:
|
||||
- with-nginx
|
||||
|
||||
volumes:
|
||||
backend-data:
|
||||
driver: local
|
||||
backend-logs:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
oltalama-network:
|
||||
driver: bridge
|
||||
|
||||
33
frontend/.dockerignore
Normal file
33
frontend/.dockerignore
Normal file
@@ -0,0 +1,33 @@
|
||||
# Node modules
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# Build artifacts
|
||||
dist/
|
||||
.cache/
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Testing
|
||||
coverage/
|
||||
|
||||
# Temporary
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
49
frontend/Dockerfile
Normal file
49
frontend/Dockerfile
Normal file
@@ -0,0 +1,49 @@
|
||||
# Frontend Dockerfile
|
||||
# Multi-stage build for optimized production image
|
||||
|
||||
# Stage 1: Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Build for production
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Production stage
|
||||
FROM node:20-alpine
|
||||
|
||||
# Create app user
|
||||
RUN addgroup -g 1001 -S oltalama && \
|
||||
adduser -S oltalama -u 1001 -G oltalama
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy built assets from builder
|
||||
COPY --from=builder --chown=oltalama:oltalama /app/dist ./dist
|
||||
COPY --from=builder --chown=oltalama:oltalama /app/package*.json ./
|
||||
|
||||
# Install only production dependencies (for preview server)
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Switch to non-root user
|
||||
USER oltalama
|
||||
|
||||
# Expose port
|
||||
EXPOSE 4173
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:4173 || exit 1
|
||||
|
||||
# Start preview server
|
||||
CMD ["npm", "run", "preview", "--", "--host", "0.0.0.0"]
|
||||
|
||||
22
frontend/Dockerfile.dev
Normal file
22
frontend/Dockerfile.dev
Normal file
@@ -0,0 +1,22 @@
|
||||
# Frontend Development Dockerfile
|
||||
# Hot reload enabled with Vite
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install all dependencies (including dev)
|
||||
RUN npm install
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Expose Vite dev server port
|
||||
EXPOSE 5173
|
||||
|
||||
# Start Vite dev server with host binding
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
|
||||
@@ -5,8 +5,8 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
Group=www-data
|
||||
User=oltalama
|
||||
Group=oltalama
|
||||
WorkingDirectory=/opt/oltalama/backend
|
||||
Environment=NODE_ENV=production
|
||||
Environment=PORT=3000
|
||||
|
||||
@@ -5,8 +5,8 @@ After=network.target oltalama-backend.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
Group=www-data
|
||||
User=oltalama
|
||||
Group=oltalama
|
||||
WorkingDirectory=/opt/oltalama/frontend
|
||||
Environment=NODE_ENV=production
|
||||
Environment=PORT=4173
|
||||
|
||||
Reference in New Issue
Block a user