2025-11-10 17:00:40 +03:00
|
|
|
|
import { useState, useEffect } from 'react';
|
|
|
|
|
|
import {
|
|
|
|
|
|
Box,
|
|
|
|
|
|
Paper,
|
|
|
|
|
|
Typography,
|
|
|
|
|
|
TextField,
|
|
|
|
|
|
Button,
|
|
|
|
|
|
Alert,
|
|
|
|
|
|
CircularProgress,
|
|
|
|
|
|
Divider,
|
feat: Complete phishing panel with MUI v7 Grid fixes
- ✅ Full backend implementation (Auth, Companies, Tokens, Templates, Tracking)
- ✅ Complete frontend with Material-UI v7
- ✅ Fixed MUI Grid syntax for v7 (xs/sm/md -> size prop)
- ✅ Domain configuration (single/dual domain support with CORS)
- ✅ Gmail integration with App Password
- ✅ Telegram notifications
- ✅ Mail template management with HTML editor
- ✅ Security features (bcrypt, session-based auth, CORS)
- ✅ Deployment scripts and comprehensive documentation
- ✅ Admin user management scripts
- 📚 7 detailed documentation files (DEPLOYMENT, SECURITY, DOMAIN_SETUP, etc.)
Backend: Node.js + Express + SQLite + Sequelize
Frontend: React + Vite + MUI v7
Features: Token tracking, IP/GeoIP logging, company management, mail templates
2025-11-10 21:03:17 +03:00
|
|
|
|
FormControlLabel,
|
|
|
|
|
|
Checkbox,
|
|
|
|
|
|
Grid,
|
2025-11-10 17:00:40 +03:00
|
|
|
|
} from '@mui/material';
|
|
|
|
|
|
import { Save, Send } from '@mui/icons-material';
|
|
|
|
|
|
import axios from 'axios';
|
|
|
|
|
|
|
|
|
|
|
|
const API_URL = import.meta.env.VITE_API_URL;
|
|
|
|
|
|
|
|
|
|
|
|
function Settings() {
|
|
|
|
|
|
const [settings, setSettings] = useState({
|
2025-11-10 20:01:41 +03:00
|
|
|
|
base_url: '',
|
|
|
|
|
|
frontend_url: '',
|
|
|
|
|
|
cors_enabled: false,
|
2025-11-10 17:00:40 +03:00
|
|
|
|
gmail_user: '',
|
|
|
|
|
|
gmail_app_password: '',
|
2025-11-10 20:01:41 +03:00
|
|
|
|
gmail_from_name: '',
|
2025-11-10 17:00:40 +03:00
|
|
|
|
telegram_bot_token: '',
|
|
|
|
|
|
telegram_chat_id: '',
|
2025-11-10 21:13:58 +03:00
|
|
|
|
ollama_server_url: '',
|
|
|
|
|
|
ollama_model: '',
|
2025-11-10 17:00:40 +03:00
|
|
|
|
});
|
2025-11-11 05:39:13 +03:00
|
|
|
|
const [adminInfo, setAdminInfo] = useState({
|
|
|
|
|
|
username: '',
|
|
|
|
|
|
current_password: '',
|
|
|
|
|
|
new_password: '',
|
|
|
|
|
|
confirm_password: '',
|
|
|
|
|
|
});
|
2025-11-10 17:00:40 +03:00
|
|
|
|
const [loading, setLoading] = useState(true);
|
2025-11-10 21:13:58 +03:00
|
|
|
|
const [testLoading, setTestLoading] = useState({ mail: false, telegram: false, ollama: false });
|
2025-11-11 05:39:13 +03:00
|
|
|
|
const [alerts, setAlerts] = useState({ mail: null, telegram: null, ollama: null, admin: null });
|
2025-11-10 21:13:58 +03:00
|
|
|
|
const [ollamaModels, setOllamaModels] = useState([]);
|
2025-11-11 05:39:13 +03:00
|
|
|
|
const [adminSaving, setAdminSaving] = useState(false);
|
2025-11-10 17:00:40 +03:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
loadSettings();
|
2025-11-11 05:39:13 +03:00
|
|
|
|
loadAdminInfo();
|
2025-11-10 17:00:40 +03:00
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
const loadSettings = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await axios.get(`${API_URL}/api/settings`, {
|
|
|
|
|
|
withCredentials: true,
|
|
|
|
|
|
});
|
2025-11-10 19:22:27 +03:00
|
|
|
|
const data = response.data.data || {};
|
2025-11-10 20:01:41 +03:00
|
|
|
|
|
|
|
|
|
|
// Convert array to object
|
|
|
|
|
|
const settingsObj = {};
|
|
|
|
|
|
if (Array.isArray(data)) {
|
|
|
|
|
|
data.forEach(item => {
|
|
|
|
|
|
settingsObj[item.key] = item.value === '********' ? '' : item.value;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-10 19:22:27 +03:00
|
|
|
|
setSettings({
|
2025-11-10 20:01:41 +03:00
|
|
|
|
base_url: settingsObj.base_url || '',
|
|
|
|
|
|
frontend_url: settingsObj.frontend_url || '',
|
|
|
|
|
|
cors_enabled: settingsObj.cors_enabled === 'true',
|
|
|
|
|
|
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 || '',
|
2025-11-10 21:13:58 +03:00
|
|
|
|
ollama_server_url: settingsObj.ollama_server_url || '',
|
|
|
|
|
|
ollama_model: settingsObj.ollama_model || '',
|
2025-11-10 19:22:27 +03:00
|
|
|
|
});
|
2025-11-10 17:00:40 +03:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to load settings:', error);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-11 05:39:13 +03:00
|
|
|
|
const loadAdminInfo = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await axios.get(`${API_URL}/api/settings/admin`, {
|
|
|
|
|
|
withCredentials: true,
|
|
|
|
|
|
});
|
|
|
|
|
|
if (response.data.success && response.data.data) {
|
|
|
|
|
|
setAdminInfo({
|
|
|
|
|
|
username: response.data.data.username || '',
|
|
|
|
|
|
current_password: '',
|
|
|
|
|
|
new_password: '',
|
|
|
|
|
|
confirm_password: '',
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to load admin info:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-10 17:00:40 +03:00
|
|
|
|
const handleSave = async () => {
|
|
|
|
|
|
try {
|
2025-11-10 19:22:27 +03:00
|
|
|
|
await Promise.all([
|
2025-11-10 20:01:41 +03:00
|
|
|
|
axios.put(`${API_URL}/api/settings/system`, {
|
|
|
|
|
|
base_url: settings.base_url,
|
|
|
|
|
|
frontend_url: settings.frontend_url,
|
|
|
|
|
|
cors_enabled: settings.cors_enabled,
|
|
|
|
|
|
}, { withCredentials: true }),
|
2025-11-10 19:22:27 +03:00
|
|
|
|
axios.put(`${API_URL}/api/settings/gmail`, {
|
|
|
|
|
|
gmail_user: settings.gmail_user,
|
|
|
|
|
|
gmail_app_password: settings.gmail_app_password,
|
2025-11-10 20:01:41 +03:00
|
|
|
|
gmail_from_name: settings.gmail_from_name,
|
2025-11-10 19:22:27 +03:00
|
|
|
|
}, { withCredentials: true }),
|
|
|
|
|
|
axios.put(`${API_URL}/api/settings/telegram`, {
|
|
|
|
|
|
telegram_bot_token: settings.telegram_bot_token,
|
|
|
|
|
|
telegram_chat_id: settings.telegram_chat_id,
|
|
|
|
|
|
}, { withCredentials: true }),
|
2025-11-10 21:13:58 +03:00
|
|
|
|
axios.put(`${API_URL}/api/ollama/settings`, {
|
|
|
|
|
|
ollama_server_url: settings.ollama_server_url,
|
|
|
|
|
|
ollama_model: settings.ollama_model,
|
|
|
|
|
|
}, { withCredentials: true }),
|
2025-11-10 19:22:27 +03:00
|
|
|
|
]);
|
2025-11-10 17:00:40 +03:00
|
|
|
|
alert('Ayarlar kaydedildi!');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('Failed to save settings:', error);
|
2025-11-10 20:01:41 +03:00
|
|
|
|
alert('Ayarlar kaydedilemedi: ' + (error.response?.data?.error || error.message));
|
2025-11-10 17:00:40 +03:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleTestMail = async () => {
|
|
|
|
|
|
setTestLoading({ ...testLoading, mail: true });
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await axios.post(
|
2025-11-10 19:22:27 +03:00
|
|
|
|
`${API_URL}/api/settings/test-gmail`,
|
2025-11-10 17:00:40 +03:00
|
|
|
|
{},
|
|
|
|
|
|
{ withCredentials: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
setAlerts({ ...alerts, mail: { severity: 'success', message: response.data.message } });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
setAlerts({
|
|
|
|
|
|
...alerts,
|
|
|
|
|
|
mail: { severity: 'error', message: error.response?.data?.error || 'Test başarısız' },
|
|
|
|
|
|
});
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setTestLoading({ ...testLoading, mail: false });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleTestTelegram = async () => {
|
|
|
|
|
|
setTestLoading({ ...testLoading, telegram: true });
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await axios.post(
|
|
|
|
|
|
`${API_URL}/api/settings/test-telegram`,
|
|
|
|
|
|
{},
|
|
|
|
|
|
{ withCredentials: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
setAlerts({ ...alerts, telegram: { severity: 'success', message: response.data.message } });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
setAlerts({
|
|
|
|
|
|
...alerts,
|
|
|
|
|
|
telegram: {
|
|
|
|
|
|
severity: 'error',
|
|
|
|
|
|
message: error.response?.data?.error || 'Test başarısız',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setTestLoading({ ...testLoading, telegram: false });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-10 21:13:58 +03:00
|
|
|
|
const handleTestOllama = async () => {
|
|
|
|
|
|
setTestLoading({ ...testLoading, ollama: true });
|
|
|
|
|
|
setAlerts({ ...alerts, ollama: null });
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2025-11-10 21:21:29 +03:00
|
|
|
|
// First save the settings if they are filled
|
|
|
|
|
|
if (settings.ollama_server_url && settings.ollama_model) {
|
|
|
|
|
|
await axios.put(`${API_URL}/api/ollama/settings`, {
|
|
|
|
|
|
ollama_server_url: settings.ollama_server_url,
|
|
|
|
|
|
ollama_model: settings.ollama_model,
|
|
|
|
|
|
}, { withCredentials: true });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-10 21:13:58 +03:00
|
|
|
|
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 });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-11 05:39:13 +03:00
|
|
|
|
const handleSaveAdmin = async () => {
|
|
|
|
|
|
setAdminSaving(true);
|
|
|
|
|
|
setAlerts({ ...alerts, admin: null });
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const updateData = {};
|
|
|
|
|
|
|
|
|
|
|
|
// Only include fields that are being changed
|
|
|
|
|
|
if (adminInfo.username && adminInfo.username.trim() !== '') {
|
|
|
|
|
|
updateData.username = adminInfo.username.trim();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (adminInfo.new_password) {
|
|
|
|
|
|
updateData.new_password = adminInfo.new_password;
|
|
|
|
|
|
updateData.confirm_password = adminInfo.confirm_password;
|
|
|
|
|
|
updateData.current_password = adminInfo.current_password;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const response = await axios.put(
|
|
|
|
|
|
`${API_URL}/api/settings/admin`,
|
|
|
|
|
|
updateData,
|
|
|
|
|
|
{ withCredentials: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (response.data.success) {
|
|
|
|
|
|
setAlerts({
|
|
|
|
|
|
...alerts,
|
|
|
|
|
|
admin: { severity: 'success', message: response.data.message || 'Admin bilgileri güncellendi' },
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Clear password fields
|
|
|
|
|
|
setAdminInfo({
|
|
|
|
|
|
...adminInfo,
|
|
|
|
|
|
current_password: '',
|
|
|
|
|
|
new_password: '',
|
|
|
|
|
|
confirm_password: '',
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Reload admin info to get updated username
|
|
|
|
|
|
await loadAdminInfo();
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
setAlerts({
|
|
|
|
|
|
...alerts,
|
|
|
|
|
|
admin: {
|
|
|
|
|
|
severity: 'error',
|
|
|
|
|
|
message: error.response?.data?.error || 'Admin bilgileri güncellenemedi',
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setAdminSaving(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-10 17:00:40 +03:00
|
|
|
|
if (loading) {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
|
|
|
|
|
|
<CircularProgress />
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Box>
|
|
|
|
|
|
<Typography variant="h4" gutterBottom>
|
|
|
|
|
|
Sistem Ayarları
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
|
|
<Grid container spacing={3}>
|
2025-11-10 20:01:41 +03:00
|
|
|
|
{/* System Settings */}
|
feat: Complete phishing panel with MUI v7 Grid fixes
- ✅ Full backend implementation (Auth, Companies, Tokens, Templates, Tracking)
- ✅ Complete frontend with Material-UI v7
- ✅ Fixed MUI Grid syntax for v7 (xs/sm/md -> size prop)
- ✅ Domain configuration (single/dual domain support with CORS)
- ✅ Gmail integration with App Password
- ✅ Telegram notifications
- ✅ Mail template management with HTML editor
- ✅ Security features (bcrypt, session-based auth, CORS)
- ✅ Deployment scripts and comprehensive documentation
- ✅ Admin user management scripts
- 📚 7 detailed documentation files (DEPLOYMENT, SECURITY, DOMAIN_SETUP, etc.)
Backend: Node.js + Express + SQLite + Sequelize
Frontend: React + Vite + MUI v7
Features: Token tracking, IP/GeoIP logging, company management, mail templates
2025-11-10 21:03:17 +03:00
|
|
|
|
<Grid size={12}>
|
2025-11-10 20:01:41 +03:00
|
|
|
|
<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 */}
|
feat: Complete phishing panel with MUI v7 Grid fixes
- ✅ Full backend implementation (Auth, Companies, Tokens, Templates, Tracking)
- ✅ Complete frontend with Material-UI v7
- ✅ Fixed MUI Grid syntax for v7 (xs/sm/md -> size prop)
- ✅ Domain configuration (single/dual domain support with CORS)
- ✅ Gmail integration with App Password
- ✅ Telegram notifications
- ✅ Mail template management with HTML editor
- ✅ Security features (bcrypt, session-based auth, CORS)
- ✅ Deployment scripts and comprehensive documentation
- ✅ Admin user management scripts
- 📚 7 detailed documentation files (DEPLOYMENT, SECURITY, DOMAIN_SETUP, etc.)
Backend: Node.js + Express + SQLite + Sequelize
Frontend: React + Vite + MUI v7
Features: Token tracking, IP/GeoIP logging, company management, mail templates
2025-11-10 21:03:17 +03:00
|
|
|
|
<Grid size={{ xs: 12, md: 6 }}>
|
2025-11-10 17:00:40 +03:00
|
|
|
|
<Paper sx={{ p: 3 }}>
|
|
|
|
|
|
<Typography variant="h6" gutterBottom>
|
2025-11-10 20:01:41 +03:00
|
|
|
|
📧 Gmail Ayarları
|
2025-11-10 17:00:40 +03:00
|
|
|
|
</Typography>
|
|
|
|
|
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
|
|
|
|
Gmail App Password kullanın (2FA aktif olmalı)
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Gmail Adresi"
|
|
|
|
|
|
type="email"
|
|
|
|
|
|
value={settings.gmail_user}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setSettings({ ...settings, gmail_user: e.target.value })
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="App Password"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
value={settings.gmail_app_password}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setSettings({ ...settings, gmail_app_password: e.target.value })
|
|
|
|
|
|
}
|
2025-11-10 20:01:41 +03:00
|
|
|
|
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"
|
2025-11-10 17:00:40 +03:00
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
{alerts.mail && (
|
|
|
|
|
|
<Alert severity={alerts.mail.severity} sx={{ mt: 2 }}>
|
|
|
|
|
|
{alerts.mail.message}
|
|
|
|
|
|
</Alert>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<Box mt={2} display="flex" gap={2}>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="contained"
|
|
|
|
|
|
startIcon={<Save />}
|
|
|
|
|
|
onClick={handleSave}
|
|
|
|
|
|
>
|
|
|
|
|
|
Kaydet
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outlined"
|
|
|
|
|
|
startIcon={<Send />}
|
|
|
|
|
|
onClick={handleTestMail}
|
|
|
|
|
|
disabled={testLoading.mail}
|
|
|
|
|
|
>
|
|
|
|
|
|
Test Mail Gönder
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
</Paper>
|
|
|
|
|
|
</Grid>
|
|
|
|
|
|
|
feat: Complete phishing panel with MUI v7 Grid fixes
- ✅ Full backend implementation (Auth, Companies, Tokens, Templates, Tracking)
- ✅ Complete frontend with Material-UI v7
- ✅ Fixed MUI Grid syntax for v7 (xs/sm/md -> size prop)
- ✅ Domain configuration (single/dual domain support with CORS)
- ✅ Gmail integration with App Password
- ✅ Telegram notifications
- ✅ Mail template management with HTML editor
- ✅ Security features (bcrypt, session-based auth, CORS)
- ✅ Deployment scripts and comprehensive documentation
- ✅ Admin user management scripts
- 📚 7 detailed documentation files (DEPLOYMENT, SECURITY, DOMAIN_SETUP, etc.)
Backend: Node.js + Express + SQLite + Sequelize
Frontend: React + Vite + MUI v7
Features: Token tracking, IP/GeoIP logging, company management, mail templates
2025-11-10 21:03:17 +03:00
|
|
|
|
<Grid size={{ xs: 12, md: 6 }}>
|
2025-11-10 17:00:40 +03:00
|
|
|
|
<Paper sx={{ p: 3 }}>
|
|
|
|
|
|
<Typography variant="h6" gutterBottom>
|
|
|
|
|
|
Telegram Ayarları
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
|
|
|
|
@BotFather'dan bot token alın, @userinfobot'dan chat ID öğrenin
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Bot Token"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
value={settings.telegram_bot_token}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setSettings({ ...settings, telegram_bot_token: e.target.value })
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Chat ID"
|
|
|
|
|
|
value={settings.telegram_chat_id}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setSettings({ ...settings, telegram_chat_id: e.target.value })
|
|
|
|
|
|
}
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
{alerts.telegram && (
|
|
|
|
|
|
<Alert severity={alerts.telegram.severity} sx={{ mt: 2 }}>
|
|
|
|
|
|
{alerts.telegram.message}
|
|
|
|
|
|
</Alert>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<Box mt={2} display="flex" gap={2}>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="contained"
|
|
|
|
|
|
startIcon={<Save />}
|
|
|
|
|
|
onClick={handleSave}
|
|
|
|
|
|
>
|
|
|
|
|
|
Kaydet
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="outlined"
|
|
|
|
|
|
startIcon={<Send />}
|
|
|
|
|
|
onClick={handleTestTelegram}
|
|
|
|
|
|
disabled={testLoading.telegram}
|
|
|
|
|
|
>
|
|
|
|
|
|
Test Bildirimi
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
</Paper>
|
|
|
|
|
|
</Grid>
|
2025-11-10 21:13:58 +03:00
|
|
|
|
|
2025-11-11 05:39:13 +03:00
|
|
|
|
{/* Admin User Settings */}
|
|
|
|
|
|
<Grid size={12}>
|
|
|
|
|
|
<Paper sx={{ p: 3 }}>
|
|
|
|
|
|
<Typography variant="h6" gutterBottom>
|
|
|
|
|
|
🔐 Admin Kullanıcı Bilgileri
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
|
|
|
|
Kullanıcı adı ve şifrenizi değiştirebilirsiniz
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Kullanıcı Adı"
|
|
|
|
|
|
value={adminInfo.username}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setAdminInfo({ ...adminInfo, username: e.target.value })
|
|
|
|
|
|
}
|
|
|
|
|
|
helperText="En az 3 karakter olmalıdır"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<Divider sx={{ my: 2 }} />
|
|
|
|
|
|
<Typography variant="subtitle2" gutterBottom>
|
|
|
|
|
|
Şifre Değiştir (Opsiyonel)
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Mevcut Şifre"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
value={adminInfo.current_password}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setAdminInfo({ ...adminInfo, current_password: e.target.value })
|
|
|
|
|
|
}
|
|
|
|
|
|
helperText="Yeni şifre belirlemek için mevcut şifrenizi girin"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Yeni Şifre"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
value={adminInfo.new_password}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setAdminInfo({ ...adminInfo, new_password: e.target.value })
|
|
|
|
|
|
}
|
|
|
|
|
|
helperText="En az 8 karakter olmalıdır"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<TextField
|
|
|
|
|
|
fullWidth
|
|
|
|
|
|
margin="normal"
|
|
|
|
|
|
label="Yeni Şifre (Tekrar)"
|
|
|
|
|
|
type="password"
|
|
|
|
|
|
value={adminInfo.confirm_password}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setAdminInfo({ ...adminInfo, confirm_password: e.target.value })
|
|
|
|
|
|
}
|
|
|
|
|
|
helperText="Yeni şifreyi tekrar girin"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
{alerts.admin && (
|
|
|
|
|
|
<Alert severity={alerts.admin.severity} sx={{ mt: 2 }}>
|
|
|
|
|
|
{alerts.admin.message}
|
|
|
|
|
|
</Alert>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<Box mt={2}>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
variant="contained"
|
|
|
|
|
|
startIcon={adminSaving ? <CircularProgress size={20} /> : <Save />}
|
|
|
|
|
|
onClick={handleSaveAdmin}
|
|
|
|
|
|
disabled={adminSaving}
|
|
|
|
|
|
>
|
|
|
|
|
|
{adminSaving ? 'Kaydediliyor...' : 'Admin Bilgilerini Kaydet'}
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
</Paper>
|
|
|
|
|
|
</Grid>
|
|
|
|
|
|
|
2025-11-10 21:13:58 +03:00
|
|
|
|
{/* 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">
|
2025-11-10 21:21:29 +03:00
|
|
|
|
Mevcut Modeller (tıklayarak seçin):
|
2025-11-10 21:13:58 +03:00
|
|
|
|
</Typography>
|
|
|
|
|
|
{ollamaModels.map((model, idx) => (
|
2025-11-10 21:21:29 +03:00
|
|
|
|
<Typography
|
|
|
|
|
|
key={idx}
|
|
|
|
|
|
variant="body2"
|
|
|
|
|
|
sx={{
|
|
|
|
|
|
cursor: 'pointer',
|
|
|
|
|
|
'&:hover': { backgroundColor: 'action.hover' },
|
|
|
|
|
|
p: 0.5,
|
|
|
|
|
|
borderRadius: 1,
|
|
|
|
|
|
}}
|
|
|
|
|
|
onClick={() => setSettings({ ...settings, ollama_model: model.name })}
|
|
|
|
|
|
>
|
2025-11-10 21:13:58 +03:00
|
|
|
|
• {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>
|
2025-11-10 17:00:40 +03:00
|
|
|
|
</Grid>
|
|
|
|
|
|
|
|
|
|
|
|
<Paper sx={{ p: 3, mt: 3 }}>
|
|
|
|
|
|
<Typography variant="h6" gutterBottom>
|
|
|
|
|
|
Tracking URL Bilgisi
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
<Divider sx={{ my: 2 }} />
|
|
|
|
|
|
<Typography variant="body2" color="text.secondary">
|
|
|
|
|
|
Tracking URL formatı: <strong>http://your-domain.com/t/TOKEN</strong>
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
<Typography variant="body2" color="text.secondary" mt={1}>
|
|
|
|
|
|
Bu URL'ler mail şablonlarında otomatik olarak oluşturulur ve gönderilir.
|
|
|
|
|
|
</Typography>
|
|
|
|
|
|
</Paper>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default Settings;
|
|
|
|
|
|
|