feat: Add Ollama AI integration for automatic mail template generation
✨ New Features: - 🤖 AI-powered mail template generation with Ollama - 📧 Test mail sending with preview - 🔧 Ollama server and model management - 🎨 Beautiful AI generation dialog in Templates page - ⚙️ Ollama settings panel with connection test Backend: - Add ollama.service.js - Ollama API integration - Add ollama.controller.js - Template generation endpoint - Add ollama.routes.js - /api/ollama/* routes - Support for multiple Ollama models (llama3.2, mistral, gemma) - JSON-formatted AI responses with subject + HTML body - Configurable server URL and model selection Frontend: - Settings: Ollama configuration panel - Server URL input - Model selection - Connection test with model listing - Templates: AI generation dialog - Company name, scenario, employee info inputs - Custom prompt for AI instructions - Auto-save to database - Test mail sending functionality Documentation: - OLLAMA_SETUP.md - Comprehensive setup guide - Installation instructions - Model recommendations - Usage examples - Troubleshooting Tech Stack: - Ollama API integration (REST) - Axios HTTP client - React dialogs with MUI - Self-hosted AI (privacy-friendly) - Zero external API dependencies Example Usage: Company: Garanti Bankası Scenario: Account security warning → AI generates professional phishing test mail in seconds!
This commit is contained in:
@@ -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 (
|
||||
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
|
||||
@@ -333,6 +380,80 @@ function Settings() {
|
||||
</Box>
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
{/* Ollama Settings */}
|
||||
<Grid size={12}>
|
||||
<Paper sx={{ p: 3 }}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
🤖 Ollama AI Ayarları
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" gutterBottom>
|
||||
AI ile mail şablonu oluşturmak için Ollama yapılandırması
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Ollama Server URL"
|
||||
type="url"
|
||||
placeholder="http://localhost:11434"
|
||||
value={settings.ollama_server_url}
|
||||
onChange={(e) =>
|
||||
setSettings({ ...settings, ollama_server_url: e.target.value })
|
||||
}
|
||||
helperText="Ollama sunucu adresi (varsayılan: http://localhost:11434)"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Model"
|
||||
placeholder="llama3.2"
|
||||
value={settings.ollama_model}
|
||||
onChange={(e) =>
|
||||
setSettings({ ...settings, ollama_model: e.target.value })
|
||||
}
|
||||
helperText="Kullanılacak Ollama model adı (örn: llama3.2, mistral, gemma)"
|
||||
/>
|
||||
|
||||
{alerts.ollama && (
|
||||
<Alert severity={alerts.ollama.severity} sx={{ mt: 2 }}>
|
||||
{alerts.ollama.message}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{ollamaModels.length > 0 && (
|
||||
<Alert severity="info" sx={{ mt: 2 }}>
|
||||
<Typography variant="body2" fontWeight="bold">
|
||||
Mevcut Modeller:
|
||||
</Typography>
|
||||
{ollamaModels.map((model, idx) => (
|
||||
<Typography key={idx} variant="body2">
|
||||
• {model.name} ({(model.size / 1024 / 1024 / 1024).toFixed(1)} GB)
|
||||
</Typography>
|
||||
))}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Box mt={2} display="flex" gap={2}>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<Save />}
|
||||
onClick={handleSave}
|
||||
>
|
||||
Kaydet
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<Send />}
|
||||
onClick={handleTestOllama}
|
||||
disabled={testLoading.ollama}
|
||||
>
|
||||
{testLoading.ollama ? <CircularProgress size={20} /> : 'Bağlantıyı Test Et'}
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Paper sx={{ p: 3, mt: 3 }}>
|
||||
|
||||
Reference in New Issue
Block a user