Add admin user management in settings panel (username and password change)
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
const { Settings } = require('../models');
|
const { Settings, AdminUser } = require('../models');
|
||||||
const mailService = require('../services/mail.service');
|
const mailService = require('../services/mail.service');
|
||||||
const telegramService = require('../services/telegram.service');
|
const telegramService = require('../services/telegram.service');
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
// Get all settings
|
// Get all settings
|
||||||
exports.getAllSettings = async (req, res, next) => {
|
exports.getAllSettings = async (req, res, next) => {
|
||||||
@@ -208,5 +209,147 @@ exports.testTelegram = async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Get admin user info (current logged in user)
|
||||||
|
exports.getAdminInfo = async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const userId = req.session.userId;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Authentication required',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const admin = await AdminUser.findByPk(userId, {
|
||||||
|
attributes: ['id', 'username', 'email', 'full_name', 'created_at', 'updated_at'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!admin) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Admin user not found',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: admin,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update admin user info (username and/or password)
|
||||||
|
exports.updateAdminInfo = async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const userId = req.session.userId;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Authentication required',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { username, current_password, new_password, confirm_password } = req.body;
|
||||||
|
|
||||||
|
const admin = await AdminUser.findByPk(userId);
|
||||||
|
|
||||||
|
if (!admin) {
|
||||||
|
return res.status(404).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Admin user not found',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update username if provided
|
||||||
|
if (username && username.trim() !== '') {
|
||||||
|
const newUsername = username.trim();
|
||||||
|
|
||||||
|
// Validate username
|
||||||
|
if (newUsername.length < 3) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Kullanıcı adı en az 3 karakter olmalıdır',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if username is already taken by another user
|
||||||
|
const existingUser = await AdminUser.findOne({
|
||||||
|
where: {
|
||||||
|
username: newUsername,
|
||||||
|
id: { [require('sequelize').Op.ne]: userId },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Bu kullanıcı adı zaten kullanılıyor',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
admin.username = newUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update password if provided
|
||||||
|
if (new_password) {
|
||||||
|
// Validate password
|
||||||
|
if (new_password.length < 8) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Şifre en az 8 karakter olmalıdır',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check password confirmation
|
||||||
|
if (new_password !== confirm_password) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Yeni şifreler eşleşmiyor',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify current password
|
||||||
|
if (!current_password) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Mevcut şifre gereklidir',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const isPasswordValid = await bcrypt.compare(current_password, admin.password);
|
||||||
|
|
||||||
|
if (!isPasswordValid) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Mevcut şifre yanlış',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash new password
|
||||||
|
const hashedPassword = await bcrypt.hash(new_password, 10);
|
||||||
|
admin.password = hashedPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
await admin.save();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
message: 'Admin bilgileri başarıyla güncellendi',
|
||||||
|
data: {
|
||||||
|
id: admin.id,
|
||||||
|
username: admin.username,
|
||||||
|
email: admin.email,
|
||||||
|
full_name: admin.full_name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = exports;
|
module.exports = exports;
|
||||||
|
|
||||||
|
|||||||
@@ -13,5 +13,9 @@ router.put('/system', settingsController.updateSystemSettings);
|
|||||||
router.post('/test-gmail', settingsController.testGmail);
|
router.post('/test-gmail', settingsController.testGmail);
|
||||||
router.post('/test-telegram', settingsController.testTelegram);
|
router.post('/test-telegram', settingsController.testTelegram);
|
||||||
|
|
||||||
|
// Admin user management
|
||||||
|
router.get('/admin', settingsController.getAdminInfo);
|
||||||
|
router.put('/admin', settingsController.updateAdminInfo);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
||||||
|
|||||||
@@ -30,13 +30,21 @@ function Settings() {
|
|||||||
ollama_server_url: '',
|
ollama_server_url: '',
|
||||||
ollama_model: '',
|
ollama_model: '',
|
||||||
});
|
});
|
||||||
|
const [adminInfo, setAdminInfo] = useState({
|
||||||
|
username: '',
|
||||||
|
current_password: '',
|
||||||
|
new_password: '',
|
||||||
|
confirm_password: '',
|
||||||
|
});
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [testLoading, setTestLoading] = useState({ mail: false, telegram: false, ollama: false });
|
const [testLoading, setTestLoading] = useState({ mail: false, telegram: false, ollama: false });
|
||||||
const [alerts, setAlerts] = useState({ mail: null, telegram: null, ollama: null });
|
const [alerts, setAlerts] = useState({ mail: null, telegram: null, ollama: null, admin: null });
|
||||||
const [ollamaModels, setOllamaModels] = useState([]);
|
const [ollamaModels, setOllamaModels] = useState([]);
|
||||||
|
const [adminSaving, setAdminSaving] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
loadAdminInfo();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadSettings = async () => {
|
const loadSettings = async () => {
|
||||||
@@ -73,6 +81,24 @@ function Settings() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@@ -189,6 +215,60 @@ function Settings() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
|
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
|
||||||
@@ -389,6 +469,87 @@ function Settings() {
|
|||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
{/* 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>
|
||||||
|
|
||||||
{/* Ollama Settings */}
|
{/* Ollama Settings */}
|
||||||
<Grid size={12}>
|
<Grid size={12}>
|
||||||
<Paper sx={{ p: 3 }}>
|
<Paper sx={{ p: 3 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user