diff --git a/OLLAMA_SETUP.md b/OLLAMA_SETUP.md
new file mode 100644
index 0000000..d8c865a
--- /dev/null
+++ b/OLLAMA_SETUP.md
@@ -0,0 +1,307 @@
+# 🤖 Ollama AI Entegrasyonu
+
+## Genel Bakış
+
+Phishing Test Yönetim Paneli, **Ollama AI** entegrasyonu ile otomatik mail şablonları oluşturabilir. Bu özellik, gerçekçi ve özelleştirilebilir phishing test maillerini dakikalar içinde üretmenizi sağlar.
+
+## 🚀 Özellikler
+
+### 1. **AI ile Mail Şablonu Oluşturma**
+- Şirket adı, senaryo ve hedef bilgilerine göre otomatik mail oluşturma
+- HTML formatında profesyonel mail içeriği
+- Özelleştirilebilir placeholder'lar (`{{company_name}}`, `{{employee_name}}`, `{{tracking_url}}`)
+- Otomatik konu satırı (subject) oluşturma
+
+### 2. **Test Mail Gönderimi**
+- Oluşturulan şablonları test etme
+- Placeholder'ları doldurarak gerçek görünüm
+- Herhangi bir mail adresine test gönderimi
+
+### 3. **Ollama Yönetimi**
+- Ollama sunucu bağlantı testi
+- Mevcut modelleri listeleme
+- Model seçimi ve yönetimi
+
+## 📋 Gereksinimler
+
+### 1. Ollama Kurulumu
+
+#### Linux
+```bash
+curl -fsSL https://ollama.com/install.sh | sh
+```
+
+#### macOS
+```bash
+brew install ollama
+```
+
+#### Windows
+[Ollama Windows İndirme](https://ollama.com/download/windows)
+
+### 2. Model İndirme
+
+Önerilen modeller:
+
+```bash
+# Llama 3.2 (Önerilen - hızlı ve etkili)
+ollama pull llama3.2
+
+# Alternatifler:
+ollama pull mistral # Hızlı, compact
+ollama pull gemma # Google'ın modeli
+ollama pull llama2 # Eski ama stabil
+```
+
+### 3. Ollama Servisini Başlatma
+
+```bash
+# Servis olarak başlat
+ollama serve
+
+# Veya systemd ile (Linux)
+systemctl start ollama
+systemctl enable ollama
+```
+
+## ⚙️ Yapılandırma
+
+### 1. Panel Ayarları
+
+1. **Ayarlar** sayfasına gidin
+2. **🤖 Ollama AI Ayarları** bölümünde:
+ - **Ollama Server URL**: `http://localhost:11434` (varsayılan)
+ - **Model**: `llama3.2` (veya tercih ettiğiniz model)
+3. **Bağlantıyı Test Et** butonuna tıklayın
+4. Mevcut modelleri görün
+5. **Kaydet** butonuna tıklayın
+
+### 2. Ortam Değişkenleri (Opsiyonel)
+
+`.env` dosyasında varsayılan değerleri ayarlayabilirsiniz:
+
+```bash
+OLLAMA_URL=http://localhost:11434
+OLLAMA_MODEL=llama3.2
+```
+
+## 🎯 Kullanım
+
+### AI ile Şablon Oluşturma
+
+1. **Mail Şablonları** sayfasına gidin
+2. **AI ile Oluştur** butonuna tıklayın
+3. Formu doldurun:
+ - **Şablon Adı**: Şablona verilecek isim
+ - **Template Type**: Şablon tipi (örn: `bank`, `hr`, `it_support`)
+ - **Şirket Adı**: Hedef şirket (örn: "Acme Corporation")
+ - **Senaryo**: Mail senaryosu (örn: "Şifre sıfırlama maili")
+ - **Çalışan Bilgisi** (opsiyonel): Hedef çalışan hakkında bilgi
+ - **Ek Talimatlar** (opsiyonel): AI'ya özel talimatlar
+4. **Oluştur** butonuna tıklayın
+5. AI, birkaç saniye içinde profesyonel bir mail şablonu oluşturacak
+6. Şablon otomatik olarak veritabanına kaydedilir
+
+### Senaryo Örnekleri
+
+#### Banka Şablonu
+```
+Senaryo: "Hesap güvenlik uyarısı - şüpheli aktivite tespit edildi ve doğrulama gerekiyor"
+Şirket: Garanti Bankası
+Çalışan Bilgisi: Bireysel müşteri
+```
+
+#### İK Şablonu
+```
+Senaryo: "Yıllık performans değerlendirme formu doldurulması gerekiyor"
+Şirket: Acme Tech
+Çalışan Bilgisi: Yazılım geliştirici
+Ek Talimat: Resmi ve profesyonel dil kullan
+```
+
+#### IT Destek Şablonu
+```
+Senaryo: "Sistem güncellemesi için kullanıcı bilgilerinin doğrulanması"
+Şirket: XYZ Corporation
+Çalışan Bilgisi: Ofis çalışanı
+Ek Talimat: Teknik terimler kullan, aciliyet vurgusu yap
+```
+
+### Test Mail Gönderme
+
+1. Şablonlar listesinde istediğiniz şablonun yanındaki **Test Mail** butonuna tıklayın
+2. Test mail adresini girin
+3. Placeholder değerlerini düzenleyin:
+ - Şirket Adı
+ - Çalışan Adı
+4. **Gönder** butonuna tıklayın
+5. Test maili belirttiğiniz adrese gönderilir
+
+## 🔧 Teknik Detaylar
+
+### API Endpoints
+
+#### Ollama Bağlantı Testi
+```http
+GET /api/ollama/test
+```
+
+#### Mevcut Modelleri Listeleme
+```http
+GET /api/ollama/models
+```
+
+#### AI ile Şablon Oluşturma
+```http
+POST /api/ollama/generate-template
+Content-Type: application/json
+
+{
+ "company_name": "Acme Corp",
+ "scenario": "Şifre sıfırlama",
+ "employee_info": "IT personeli",
+ "custom_prompt": "Aciliyet vurgusu yap",
+ "template_name": "Acme Şifre Sıfırlama",
+ "template_type": "it_support"
+}
+```
+
+#### Test Mail Gönderme
+```http
+POST /api/ollama/send-test-mail
+Content-Type: application/json
+
+{
+ "test_email": "test@example.com",
+ "subject": "Test Subject",
+ "body": "...",
+ "company_name": "Test Company",
+ "employee_name": "John Doe"
+}
+```
+
+### Ollama Servisi Yapılandırması
+
+Backend servisi Ollama'yı otomatik olarak yapılandırır:
+
+```javascript
+// backend/src/services/ollama.service.js
+class OllamaService {
+ constructor() {
+ this.serverUrl = 'http://localhost:11434';
+ this.model = 'llama3.2';
+ }
+}
+```
+
+### AI Prompt Yapısı
+
+AI'ya gönderilen prompt:
+
+```
+System: Sen profesyonel bir güvenlik uzmanısın ve phishing test maileri oluşturuyorsun.
+
+User: Aşağıdaki bilgilere göre bir phishing test mail şablonu oluştur:
+- Şirket: [company_name]
+- Senaryo: [scenario]
+- Çalışan Bilgisi: [employee_info]
+- Ek Talimatlar: [custom_prompt]
+
+Yanıt JSON formatında:
+{
+ "subject": "Mail konusu",
+ "body": "Mail içeriği"
+}
+```
+
+## 🛠️ Troubleshooting
+
+### Ollama bağlantı hatası
+
+**Hata**: `Ollama sunucusuna bağlanılamadı`
+
+**Çözüm**:
+```bash
+# Ollama servisinin çalıştığını kontrol edin
+curl http://localhost:11434/api/tags
+
+# Servis çalışmıyorsa başlatın
+ollama serve
+```
+
+### Model bulunamadı hatası
+
+**Hata**: `Model not found`
+
+**Çözüm**:
+```bash
+# Modeli indirin
+ollama pull llama3.2
+
+# Mevcut modelleri listeleyin
+ollama list
+```
+
+### Yavaş yanıt süresi
+
+**Çözüm**:
+- Daha küçük bir model kullanın (`mistral`, `llama3.2:1b`)
+- GPU kullanımını etkinleştirin (CUDA destekli sistem gerekir)
+- RAM ve CPU kaynaklarını artırın
+
+### Timeout hatası
+
+**Hata**: `AI yanıt veremedi: timeout`
+
+**Çözüm**:
+- Backend `ollama.service.js` dosyasında timeout süresini artırın:
+ ```javascript
+ timeout: 180000, // 3 dakika
+ ```
+
+## 📊 Performans
+
+### Model Karşılaştırması
+
+| Model | Boyut | Hız | Kalite | RAM |
+|-------|-------|-----|--------|-----|
+| llama3.2 | 2GB | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 8GB |
+| mistral | 4GB | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 8GB |
+| gemma | 2GB | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 6GB |
+| llama2 | 4GB | ⭐⭐⭐ | ⭐⭐⭐⭐ | 8GB |
+
+### Yanıt Süreleri (Ortalama)
+
+- **CPU**: 30-60 saniye
+- **GPU (CUDA)**: 5-15 saniye
+- **Apple Silicon (M1/M2)**: 10-25 saniye
+
+## 🔒 Güvenlik Notları
+
+1. **Ollama Erişimi**: Ollama sunucusu varsayılan olarak `localhost:11434` üzerinde çalışır. Production'da bu portu dışarıya açmayın.
+
+2. **API Rate Limiting**: Backend API'sinde rate limiting aktiftir, kötüye kullanımı önler.
+
+3. **Model Seçimi**: Güvenilir ve test edilmiş modeller kullanın.
+
+4. **Veri Gizliliği**: AI'ya gönderilen veriler Ollama sunucusunda işlenir, dışarıya gönderilmez (self-hosted).
+
+## 📚 Kaynaklar
+
+- [Ollama Resmi Dokümantasyon](https://github.com/ollama/ollama)
+- [Ollama Model Library](https://ollama.com/library)
+- [Llama 3.2 Model Bilgileri](https://ollama.com/library/llama3.2)
+
+## 🆘 Destek
+
+Sorun yaşıyorsanız:
+
+1. Ollama servisinin çalıştığını kontrol edin
+2. Model indirildiyse kontrol edin
+3. Panel **Ayarlar** sayfasında bağlantı testini yapın
+4. Backend loglarını kontrol edin: `backend/logs/`
+
+---
+
+**Not**: Ollama entegrasyonu tamamen opsiyoneldir. Sistemi Ollama olmadan da kullanabilir, manuel olarak mail şablonları oluşturabilirsiniz.
+
diff --git a/README.md b/README.md
index efd358c..e7763c9 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,11 @@ Güvenlik farkındalık eğitimleri için basit ve etkili phishing test yönetim
- 🏢 **Şirket Bazlı Yönetim** - Her şirket için ayrı tracking
- 📧 **Gmail Entegrasyonu** - App Password ile kolay mail gönderimi
- 💬 **Telegram Bildirimleri** - Gerçek zamanlı tıklama bildirimleri
+- 🤖 **Ollama AI Entegrasyonu** - AI ile otomatik mail şablonu oluşturma
- 📊 **Detaylı İstatistikler** - IP, konum, cihaz bilgileri
- 💾 **SQLite** - Tek dosya, kolay yedekleme
- 🎨 **Modern UI** - React ile responsive admin paneli
-- ✉️ **Mail Şablonları** - HTML editör ve önizleme paneli
+- ✉️ **Mail Şablonları** - HTML editör, önizleme ve test gönderimi
## 🚀 Hızlı Başlangıç
@@ -47,7 +48,7 @@ oltalama/
│ │ ├── controllers/ ✅ 7 dosya (auth, company, token, vb.)
│ │ ├── models/ ✅ 6 model + ilişkiler
│ │ ├── routes/ ✅ 7 route dosyası
-│ │ ├── services/ ✅ Mail, Telegram, Token
+│ │ ├── services/ ✅ Mail, Telegram, Token, Ollama AI
│ │ ├── utils/ ✅ GeoIP, User-Agent, Token Generator
│ │ └── app.js ✅
│ └── database/ ✅ oltalama.db (3 şirket, 2 şablon)
@@ -239,6 +240,7 @@ node scripts/change-password.js
- **Ana Doküman:** `README.md` (bu dosya)
- **Sunucu Kurulumu:** `DEPLOYMENT.md` 🚀 (Production 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)
- **Güvenlik Rehberi:** `SECURITY.md` 🔒 (Güvenlik en iyi uygulamaları)
diff --git a/backend/src/app.js b/backend/src/app.js
index 06daec0..362d614 100644
--- a/backend/src/app.js
+++ b/backend/src/app.js
@@ -87,6 +87,7 @@ app.use('/api/companies', require('./routes/company.routes'));
app.use('/api/tokens', require('./routes/token.routes'));
app.use('/api/templates', require('./routes/template.routes'));
app.use('/api/settings', require('./routes/settings.routes'));
+app.use('/api/ollama', require('./routes/ollama.routes'));
app.use('/api/stats', require('./routes/stats.routes'));
// Public tracking route (no rate limit on this specific route)
diff --git a/backend/src/controllers/ollama.controller.js b/backend/src/controllers/ollama.controller.js
new file mode 100644
index 0000000..62f7e1f
--- /dev/null
+++ b/backend/src/controllers/ollama.controller.js
@@ -0,0 +1,188 @@
+const ollamaService = require('../services/ollama.service');
+const { Settings, MailTemplate } = require('../models');
+const logger = require('../utils/logger');
+
+/**
+ * Test Ollama connection
+ */
+exports.testConnection = async (req, res, next) => {
+ try {
+ const result = await ollamaService.testConnection();
+ res.json({
+ success: result.success,
+ message: result.message,
+ data: {
+ models: result.models,
+ },
+ });
+ } catch (error) {
+ next(error);
+ }
+};
+
+/**
+ * List available Ollama models
+ */
+exports.listModels = async (req, res, next) => {
+ try {
+ const models = await ollamaService.listModels();
+ res.json({
+ success: true,
+ data: models,
+ });
+ } catch (error) {
+ next(error);
+ }
+};
+
+/**
+ * Update Ollama settings
+ */
+exports.updateSettings = async (req, res, next) => {
+ try {
+ const { ollama_server_url, ollama_model } = req.body;
+
+ if (ollama_server_url !== undefined) {
+ if (ollama_server_url) {
+ // Validate URL format
+ const cleanUrl = ollama_server_url.trim().replace(/\/$/, '');
+ try {
+ new URL(cleanUrl);
+ } catch (e) {
+ return res.status(400).json({
+ success: false,
+ error: 'Geçersiz Ollama URL formatı. Örnek: http://localhost:11434',
+ });
+ }
+
+ await Settings.upsert({
+ key: 'ollama_server_url',
+ value: cleanUrl,
+ is_encrypted: false,
+ description: 'Ollama server URL',
+ });
+ } else {
+ await Settings.destroy({ where: { key: 'ollama_server_url' } });
+ }
+ }
+
+ if (ollama_model !== undefined) {
+ if (ollama_model) {
+ await Settings.upsert({
+ key: 'ollama_model',
+ value: ollama_model,
+ is_encrypted: false,
+ description: 'Ollama model name',
+ });
+ } else {
+ await Settings.destroy({ where: { key: 'ollama_model' } });
+ }
+ }
+
+ // Reinitialize service with new settings
+ await ollamaService.initialize();
+
+ res.json({
+ success: true,
+ message: 'Ollama ayarları güncellendi',
+ });
+ } catch (error) {
+ next(error);
+ }
+};
+
+/**
+ * Generate mail template with AI
+ */
+exports.generateTemplate = async (req, res, next) => {
+ try {
+ const { company_name, scenario, employee_info, custom_prompt, template_name, template_type } = req.body;
+
+ // Validation
+ if (!company_name || !scenario) {
+ return res.status(400).json({
+ success: false,
+ error: 'company_name ve scenario zorunludur',
+ });
+ }
+
+ logger.info(`AI template generation requested for: ${company_name} - ${scenario}`);
+
+ // Generate template using Ollama
+ const templateData = await ollamaService.generateMailTemplate({
+ company_name,
+ scenario,
+ employee_info,
+ custom_prompt,
+ });
+
+ // Save to database if template_name is provided
+ let savedTemplate = null;
+ if (template_name && template_type) {
+ savedTemplate = await MailTemplate.create({
+ name: template_name,
+ type: template_type,
+ subject_template: templateData.subject_template,
+ body_template: templateData.body_template,
+ description: `AI tarafından oluşturuldu - ${scenario}`,
+ });
+
+ logger.info(`AI-generated template saved: ${template_name}`);
+ }
+
+ res.json({
+ success: true,
+ message: savedTemplate
+ ? 'Template başarıyla oluşturuldu ve kaydedildi'
+ : 'Template başarıyla oluşturuldu',
+ data: {
+ template: savedTemplate || templateData,
+ generated_by: templateData.model,
+ },
+ });
+ } catch (error) {
+ logger.error('AI template generation failed:', error);
+ next(error);
+ }
+};
+
+/**
+ * Send test mail with generated template
+ */
+exports.sendTestMail = async (req, res, next) => {
+ try {
+ const { test_email, subject, body, company_name, employee_name } = req.body;
+
+ if (!test_email || !subject || !body) {
+ return res.status(400).json({
+ success: false,
+ error: 'test_email, subject ve body zorunludur',
+ });
+ }
+
+ const mailService = require('../services/mail.service');
+
+ // Replace placeholders
+ const finalSubject = subject
+ .replace(/\{\{company_name\}\}/g, company_name || 'Test Şirketi')
+ .replace(/\{\{employee_name\}\}/g, employee_name || 'Test Kullanıcı');
+
+ const finalBody = body
+ .replace(/\{\{company_name\}\}/g, company_name || 'Test Şirketi')
+ .replace(/\{\{employee_name\}\}/g, employee_name || 'Test Kullanıcı')
+ .replace(/\{\{tracking_url\}\}/g, 'http://example.com/test-tracking-link');
+
+ await mailService.sendMail(test_email, finalSubject, finalBody);
+
+ logger.info(`Test mail sent to: ${test_email}`);
+
+ res.json({
+ success: true,
+ message: 'Test maili başarıyla gönderildi',
+ });
+ } catch (error) {
+ logger.error('Test mail sending failed:', error);
+ next(error);
+ }
+};
+
diff --git a/backend/src/routes/ollama.routes.js b/backend/src/routes/ollama.routes.js
new file mode 100644
index 0000000..fb659cc
--- /dev/null
+++ b/backend/src/routes/ollama.routes.js
@@ -0,0 +1,25 @@
+const express = require('express');
+const router = express.Router();
+const ollamaController = require('../controllers/ollama.controller');
+const { isAuthenticated } = require('../middleware/auth.middleware');
+
+// All routes require authentication
+router.use(isAuthenticated);
+
+// Test Ollama connection
+router.get('/test', ollamaController.testConnection);
+
+// List available models
+router.get('/models', ollamaController.listModels);
+
+// Update Ollama settings
+router.put('/settings', ollamaController.updateSettings);
+
+// Generate template with AI
+router.post('/generate-template', ollamaController.generateTemplate);
+
+// Send test mail
+router.post('/send-test-mail', ollamaController.sendTestMail);
+
+module.exports = router;
+
diff --git a/backend/src/services/ollama.service.js b/backend/src/services/ollama.service.js
new file mode 100644
index 0000000..e93247e
--- /dev/null
+++ b/backend/src/services/ollama.service.js
@@ -0,0 +1,221 @@
+const axios = require('axios');
+const logger = require('../utils/logger');
+const { Settings } = require('../models');
+
+class OllamaService {
+ constructor() {
+ this.serverUrl = null;
+ this.model = null;
+ this.initialized = false;
+ }
+
+ /**
+ * Initialize Ollama service with settings from database
+ */
+ async initialize() {
+ try {
+ const serverUrlSetting = await Settings.findOne({
+ where: { key: 'ollama_server_url' },
+ });
+ const modelSetting = await Settings.findOne({
+ where: { key: 'ollama_model' },
+ });
+
+ this.serverUrl = serverUrlSetting?.value || process.env.OLLAMA_URL || 'http://localhost:11434';
+ this.model = modelSetting?.value || process.env.OLLAMA_MODEL || 'llama3.2';
+
+ this.initialized = true;
+ logger.info(`Ollama service initialized: ${this.serverUrl} with model ${this.model}`);
+ } catch (error) {
+ logger.error('Failed to initialize Ollama service:', error);
+ throw error;
+ }
+ }
+
+ /**
+ * Test Ollama connection
+ */
+ async testConnection() {
+ if (!this.initialized) {
+ await this.initialize();
+ }
+
+ try {
+ const response = await axios.get(`${this.serverUrl}/api/tags`, {
+ timeout: 5000,
+ });
+
+ return {
+ success: true,
+ models: response.data.models || [],
+ message: 'Ollama bağlantısı başarılı',
+ };
+ } catch (error) {
+ logger.error('Ollama connection test failed:', error.message);
+ return {
+ success: false,
+ error: error.message,
+ message: 'Ollama sunucusuna bağlanılamadı',
+ };
+ }
+ }
+
+ /**
+ * List available models
+ */
+ async listModels() {
+ if (!this.initialized) {
+ await this.initialize();
+ }
+
+ try {
+ const response = await axios.get(`${this.serverUrl}/api/tags`, {
+ timeout: 5000,
+ });
+
+ return response.data.models || [];
+ } catch (error) {
+ logger.error('Failed to list Ollama models:', error.message);
+ throw new Error('Ollama model listesi alınamadı: ' + error.message);
+ }
+ }
+
+ /**
+ * Generate mail template using Ollama
+ * @param {Object} params - Template generation parameters
+ * @param {string} params.company_name - Target company name
+ * @param {string} params.scenario - Phishing scenario type
+ * @param {string} params.employee_info - Employee information (optional)
+ * @param {string} params.custom_prompt - Custom instructions (optional)
+ */
+ async generateMailTemplate(params) {
+ if (!this.initialized) {
+ await this.initialize();
+ }
+
+ const { company_name, scenario, employee_info, custom_prompt } = params;
+
+ // Build the prompt
+ const systemPrompt = `Sen profesyonel bir güvenlik uzmanısın ve phishing test maileri oluşturuyorsun.
+Amacın gerçekçi, ikna edici ancak zararsız test mailleri oluşturmak.
+Mail şablonları HTML formatında olmalı ve modern, profesyonel görünmeli.
+Şablon içinde {{company_name}} ve {{employee_name}} placeholder'ları kullan.`;
+
+ let userPrompt = `Aşağıdaki bilgilere göre bir phishing test mail şablonu oluştur:
+
+Şirket: ${company_name}
+Senaryo: ${scenario}`;
+
+ if (employee_info) {
+ userPrompt += `\nÇalışan Bilgisi: ${employee_info}`;
+ }
+
+ if (custom_prompt) {
+ userPrompt += `\nEk Talimatlar: ${custom_prompt}`;
+ }
+
+ userPrompt += `
+
+ÖNEMLI:
+1. Yanıtını JSON formatında ver
+2. İki alan olmalı: "subject" (konu) ve "body" (HTML mail içeriği)
+3. Body HTML formatında, modern ve profesyonel olmalı
+4. {{company_name}} ve {{employee_name}} placeholder'larını kullan
+5. Gerçekçi ve ikna edici olmalı
+6. Link için {{tracking_url}} placeholder'ını kullan
+
+Örnek format:
+{
+ "subject": "Mail konusu buraya",
+ "body": "Mail içeriği buraya"
+}`;
+
+ try {
+ logger.info(`Generating template for company: ${company_name}, scenario: ${scenario}`);
+
+ const response = await axios.post(
+ `${this.serverUrl}/api/chat`,
+ {
+ model: this.model,
+ messages: [
+ {
+ role: 'system',
+ content: systemPrompt,
+ },
+ {
+ role: 'user',
+ content: userPrompt,
+ },
+ ],
+ stream: false,
+ format: 'json',
+ },
+ {
+ timeout: 120000, // 2 minutes timeout for AI generation
+ }
+ );
+
+ const aiResponse = response.data.message.content;
+ logger.info('Ollama response received');
+
+ // Parse JSON response
+ let templateData;
+ try {
+ templateData = JSON.parse(aiResponse);
+ } catch (parseError) {
+ // If JSON parsing fails, try to extract JSON from response
+ const jsonMatch = aiResponse.match(/\{[\s\S]*\}/);
+ if (jsonMatch) {
+ templateData = JSON.parse(jsonMatch[0]);
+ } else {
+ throw new Error('AI yanıtı JSON formatında değil');
+ }
+ }
+
+ if (!templateData.subject || !templateData.body) {
+ throw new Error('AI yanıtında subject veya body eksik');
+ }
+
+ return {
+ subject_template: templateData.subject,
+ body_template: templateData.body,
+ generated_by: 'ollama',
+ model: this.model,
+ };
+ } catch (error) {
+ logger.error('Failed to generate template with Ollama:', error.message);
+ throw new Error('Template oluşturulamadı: ' + error.message);
+ }
+ }
+
+ /**
+ * Generate a simple text completion
+ */
+ async generate(prompt) {
+ if (!this.initialized) {
+ await this.initialize();
+ }
+
+ try {
+ const response = await axios.post(
+ `${this.serverUrl}/api/generate`,
+ {
+ model: this.model,
+ prompt: prompt,
+ stream: false,
+ },
+ {
+ timeout: 60000,
+ }
+ );
+
+ return response.data.response;
+ } catch (error) {
+ logger.error('Ollama generation failed:', error.message);
+ throw new Error('Ollama yanıt veremedi: ' + error.message);
+ }
+ }
+}
+
+module.exports = new OllamaService();
+
diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx
index cf74fbb..30ed8ed 100644
--- a/frontend/src/pages/Settings.jsx
+++ b/frontend/src/pages/Settings.jsx
@@ -27,10 +27,13 @@ function Settings() {
gmail_from_name: '',
telegram_bot_token: '',
telegram_chat_id: '',
+ ollama_server_url: '',
+ ollama_model: '',
});
const [loading, setLoading] = useState(true);
- const [testLoading, setTestLoading] = useState({ mail: false, telegram: false });
- const [alerts, setAlerts] = useState({ mail: null, telegram: null });
+ const [testLoading, setTestLoading] = useState({ mail: false, telegram: false, ollama: false });
+ const [alerts, setAlerts] = useState({ mail: null, telegram: null, ollama: null });
+ const [ollamaModels, setOllamaModels] = useState([]);
useEffect(() => {
loadSettings();
@@ -60,6 +63,8 @@ function Settings() {
gmail_from_name: settingsObj.gmail_from_name || '',
telegram_bot_token: settingsObj.telegram_bot_token || '',
telegram_chat_id: settingsObj.telegram_chat_id || '',
+ ollama_server_url: settingsObj.ollama_server_url || '',
+ ollama_model: settingsObj.ollama_model || '',
});
} catch (error) {
console.error('Failed to load settings:', error);
@@ -85,6 +90,10 @@ function Settings() {
telegram_bot_token: settings.telegram_bot_token,
telegram_chat_id: settings.telegram_chat_id,
}, { withCredentials: true }),
+ axios.put(`${API_URL}/api/ollama/settings`, {
+ ollama_server_url: settings.ollama_server_url,
+ ollama_model: settings.ollama_model,
+ }, { withCredentials: true }),
]);
alert('Ayarlar kaydedildi!');
} catch (error) {
@@ -134,6 +143,44 @@ function Settings() {
}
};
+ const handleTestOllama = async () => {
+ setTestLoading({ ...testLoading, ollama: true });
+ setAlerts({ ...alerts, ollama: null });
+
+ try {
+ const response = await axios.get(
+ `${API_URL}/api/ollama/test`,
+ { withCredentials: true }
+ );
+
+ if (response.data.success) {
+ setOllamaModels(response.data.data.models || []);
+ setAlerts({
+ ...alerts,
+ ollama: {
+ severity: 'success',
+ message: `${response.data.message} - ${response.data.data.models.length} model bulundu`
+ },
+ });
+ } else {
+ setAlerts({
+ ...alerts,
+ ollama: { severity: 'error', message: response.data.message },
+ });
+ }
+ } catch (error) {
+ setAlerts({
+ ...alerts,
+ ollama: {
+ severity: 'error',
+ message: error.response?.data?.error || 'Ollama bağlantısı başarısız',
+ },
+ });
+ } finally {
+ setTestLoading({ ...testLoading, ollama: false });
+ }
+ };
+
if (loading) {
return (
@@ -333,6 +380,80 @@ function Settings() {
+
+ {/* Ollama Settings */}
+
+
+
+ 🤖 Ollama AI Ayarları
+
+
+ AI ile mail şablonu oluşturmak için Ollama yapılandırması
+
+
+
+ setSettings({ ...settings, ollama_server_url: e.target.value })
+ }
+ helperText="Ollama sunucu adresi (varsayılan: http://localhost:11434)"
+ />
+
+
+ setSettings({ ...settings, ollama_model: e.target.value })
+ }
+ helperText="Kullanılacak Ollama model adı (örn: llama3.2, mistral, gemma)"
+ />
+
+ {alerts.ollama && (
+
+ {alerts.ollama.message}
+
+ )}
+
+ {ollamaModels.length > 0 && (
+
+
+ Mevcut Modeller:
+
+ {ollamaModels.map((model, idx) => (
+
+ • {model.name} ({(model.size / 1024 / 1024 / 1024).toFixed(1)} GB)
+
+ ))}
+
+ )}
+
+
+ }
+ onClick={handleSave}
+ >
+ Kaydet
+
+ }
+ onClick={handleTestOllama}
+ disabled={testLoading.ollama}
+ >
+ {testLoading.ollama ? : 'Bağlantıyı Test Et'}
+
+
+
+
diff --git a/frontend/src/pages/Templates.jsx b/frontend/src/pages/Templates.jsx
index 49fd8ee..05321cc 100644
--- a/frontend/src/pages/Templates.jsx
+++ b/frontend/src/pages/Templates.jsx
@@ -28,9 +28,14 @@ import {
Delete,
Preview,
ContentCopy,
+ AutoAwesome,
+ Send as SendIcon,
} from '@mui/icons-material';
import { templateService } from '../services/templateService';
import { format } from 'date-fns';
+import axios from 'axios';
+
+const API_URL = import.meta.env.VITE_API_URL;
const defaultForm = {
name: '',
@@ -54,6 +59,18 @@ function Templates() {
const [selectedTemplate, setSelectedTemplate] = useState(null);
const [companyPlaceholder, setCompanyPlaceholder] = useState('Örnek Şirket');
const [employeePlaceholder, setEmployeePlaceholder] = useState('Ahmet Yılmaz');
+ const [aiDialogOpen, setAiDialogOpen] = useState(false);
+ const [aiGenerating, setAiGenerating] = useState(false);
+ const [aiForm, setAiForm] = useState({
+ company_name: '',
+ scenario: '',
+ employee_info: '',
+ custom_prompt: '',
+ template_name: '',
+ template_type: '',
+ });
+ const [testMailDialogOpen, setTestMailDialogOpen] = useState(false);
+ const [testMailAddress, setTestMailAddress] = useState('');
useEffect(() => {
loadTemplates();
@@ -138,6 +155,69 @@ function Templates() {
}
};
+ const handleGenerateWithAI = async () => {
+ if (!aiForm.company_name || !aiForm.scenario || !aiForm.template_name || !aiForm.template_type) {
+ alert('Lütfen tüm zorunlu alanları doldurun');
+ return;
+ }
+
+ setAiGenerating(true);
+ try {
+ const response = await axios.post(
+ `${API_URL}/api/ollama/generate-template`,
+ aiForm,
+ { withCredentials: true }
+ );
+
+ alert(response.data.message);
+ setAiDialogOpen(false);
+ setAiForm({
+ company_name: '',
+ scenario: '',
+ employee_info: '',
+ custom_prompt: '',
+ template_name: '',
+ template_type: '',
+ });
+ loadTemplates();
+ } catch (error) {
+ const message = error.response?.data?.error || 'AI ile şablon oluşturulamadı';
+ alert(message);
+ console.error('AI generation failed:', error);
+ } finally {
+ setAiGenerating(false);
+ }
+ };
+
+ const handleSendTestMail = async () => {
+ if (!testMailAddress || !selectedTemplate) {
+ alert('Lütfen mail adresi girin');
+ return;
+ }
+
+ try {
+ await axios.post(
+ `${API_URL}/api/ollama/send-test-mail`,
+ {
+ test_email: testMailAddress,
+ subject: selectedTemplate.subject_template,
+ body: selectedTemplate.body_html,
+ company_name: companyPlaceholder,
+ employee_name: employeePlaceholder,
+ },
+ { withCredentials: true }
+ );
+
+ alert('Test maili gönderildi!');
+ setTestMailDialogOpen(false);
+ setTestMailAddress('');
+ } catch (error) {
+ const message = error.response?.data?.error || 'Test maili gönderilemedi';
+ alert(message);
+ console.error('Test mail failed:', error);
+ }
+ };
+
const handlePreview = async (htmlOverride, options = { openModal: false }) => {
try {
setPreviewLoading(true);
@@ -196,9 +276,19 @@ function Templates() {
Mail Şablonları
- } onClick={handleOpenCreate}>
- Yeni Şablon
-
+
+ }
+ onClick={() => setAiDialogOpen(true)}
+ color="secondary"
+ >
+ AI ile Oluştur
+
+ } onClick={handleOpenCreate}>
+ Yeni Şablon
+
+
@@ -273,6 +363,18 @@ function Templates() {
>
Önizleme
+ }
+ onClick={() => {
+ setSelectedTemplate(template);
+ setTestMailDialogOpen(true);
+ }}
+ sx={{ mr: 1 }}
+ >
+ Test Mail
+
}
@@ -445,6 +547,153 @@ function Templates() {
+
+ {/* AI Generation Dialog */}
+
+
+ {/* Test Mail Dialog */}
+
);
}