auth middleware and role edit ui
This commit is contained in:
parent
950b892bec
commit
a283f57af3
39
auth-service/src/middleware/auth.js
Normal file
39
auth-service/src/middleware/auth.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { User } from '../models/index.js';
|
||||||
|
|
||||||
|
function getJwtSecret(payload) {
|
||||||
|
const base = process.env.JWT_SECRET || 'secret';
|
||||||
|
if (payload && payload.rememberMe) {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
const hour = new Date().toISOString().slice(0, 13); // YYYY-MM-DDTHH
|
||||||
|
return base + ':' + hour;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function authMiddleware(req, res, next) {
|
||||||
|
const authHeader = req.headers['authorization'];
|
||||||
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||||
|
return res.status(401).json({ error: 'No token provided' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = authHeader.split(' ')[1];
|
||||||
|
try {
|
||||||
|
// Сначала декодируем без проверки, чтобы узнать rememberMe
|
||||||
|
let payload = null;
|
||||||
|
try {
|
||||||
|
payload = jwt.decode(token);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const secret = getJwtSecret(payload);
|
||||||
|
payload = jwt.verify(token, secret);
|
||||||
|
|
||||||
|
// Найти пользователя по id
|
||||||
|
const user = await User.findByPk(payload.id);
|
||||||
|
if (!user) return res.status(401).json({ error: 'User not found' });
|
||||||
|
|
||||||
|
req.user = { id: user.id, email: user.email, role_id: user.role_id };
|
||||||
|
next();
|
||||||
|
} catch (err) {
|
||||||
|
return res.status(401).json({ error: 'Invalid token', details: err.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +1,13 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import roleController from '../controllers/roleController.js';
|
import roleController from '../controllers/roleController.js';
|
||||||
|
import authMiddleware from '../middleware/auth.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/', roleController.create);
|
router.post('/', authMiddleware, roleController.create);
|
||||||
router.get('/', roleController.getAll);
|
router.get('/', authMiddleware, roleController.getAll);
|
||||||
router.get('/:id', roleController.getById);
|
router.get('/:id', authMiddleware, roleController.getById);
|
||||||
router.put('/:id', roleController.update);
|
router.put('/:id', authMiddleware, roleController.update);
|
||||||
router.delete('/:id', roleController.delete);
|
router.delete('/:id', authMiddleware, roleController.delete);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@ -1,13 +1,14 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import userController from '../controllers/userController.js';
|
import userController from '../controllers/userController.js';
|
||||||
|
import authMiddleware from '../middleware/auth.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/', userController.create);
|
router.post('/', authMiddleware, userController.create);
|
||||||
router.get('/', userController.getAll);
|
router.get('/', authMiddleware, userController.getAll);
|
||||||
router.get('/:id', userController.getById);
|
router.get('/:id', authMiddleware, userController.getById);
|
||||||
router.put('/:id', userController.update);
|
router.put('/:id', authMiddleware, userController.update);
|
||||||
router.delete('/:id', userController.delete);
|
router.delete('/:id', authMiddleware, userController.delete);
|
||||||
router.post('/login', userController.login);
|
router.post('/login', userController.login);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
@ -18,7 +18,6 @@ const SideMenu = ({ active, onSelect }) => {
|
|||||||
<div className={styles.section}>Администрирование</div>
|
<div className={styles.section}>Администрирование</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li className={active === 'users' ? styles.active : ''} onClick={() => onSelect('users')}>Управление пользователями</li>
|
<li className={active === 'users' ? styles.active : ''} onClick={() => onSelect('users')}>Управление пользователями</li>
|
||||||
<li className={active === 'roles' ? styles.active : ''} onClick={() => onSelect('roles')}>Управление ролями</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@ -15,10 +15,19 @@ export default function CreateUserModal({ isOpen, onClose, user, roles, loading,
|
|||||||
</label>
|
</label>
|
||||||
<label className={styles.label}>Роль
|
<label className={styles.label}>Роль
|
||||||
<select value={user.role_id} onChange={e => onChange({ ...user, role_id: Number(e.target.value) })} required className={styles.input}>
|
<select value={user.role_id} onChange={e => onChange({ ...user, role_id: Number(e.target.value) })} required className={styles.input}>
|
||||||
{roles.map(role => (
|
{roles.length === 0 ? (
|
||||||
<option key={role.id} value={role.id}>{role.name}</option>
|
<option value="">Загрузка ролей...</option>
|
||||||
))}
|
) : (
|
||||||
|
roles.map(role => (
|
||||||
|
<option key={role.id} value={role.id}>{role.name}</option>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</select>
|
</select>
|
||||||
|
{process.env.NODE_ENV === 'development' && (
|
||||||
|
<div style={{ fontSize: '10px', color: '#666', marginTop: '4px' }}>
|
||||||
|
Debug: {roles.length} roles, user.role_id: {user.role_id}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
<label className={styles.label}>Пароль
|
<label className={styles.label}>Пароль
|
||||||
<input type="password" value={user.password} onChange={e => onChange({ ...user, password: e.target.value })} required className={styles.input} />
|
<input type="password" value={user.password} onChange={e => onChange({ ...user, password: e.target.value })} required className={styles.input} />
|
||||||
|
|||||||
@ -15,10 +15,19 @@ export default function EditUserModal({ isOpen, onClose, user, roles, loading, o
|
|||||||
</label>
|
</label>
|
||||||
<label className={styles.label}>Роль
|
<label className={styles.label}>Роль
|
||||||
<select value={user.role_id} onChange={e => onChange({ ...user, role_id: Number(e.target.value) })} required className={styles.input}>
|
<select value={user.role_id} onChange={e => onChange({ ...user, role_id: Number(e.target.value) })} required className={styles.input}>
|
||||||
{roles.map(role => (
|
{roles.length === 0 ? (
|
||||||
<option key={role.id} value={role.id}>{role.name}</option>
|
<option value="">Загрузка ролей...</option>
|
||||||
))}
|
) : (
|
||||||
|
roles.map(role => (
|
||||||
|
<option key={role.id} value={role.id}>{role.name}</option>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</select>
|
</select>
|
||||||
|
{process.env.NODE_ENV === 'development' && (
|
||||||
|
<div style={{ fontSize: '10px', color: '#666', marginTop: '4px' }}>
|
||||||
|
Debug: {roles.length} roles, user.role_id: {user.role_id}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
<button type="submit" disabled={loading} className={styles.saveBtn}>{loading ? 'Сохранение...' : 'Сохранить'}</button>
|
<button type="submit" disabled={loading} className={styles.saveBtn}>{loading ? 'Сохранение...' : 'Сохранить'}</button>
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import SideMenu from '../components/SideMenu';
|
|||||||
import Header from '../components/Header';
|
import Header from '../components/Header';
|
||||||
import { useUser } from '../context/UserContext';
|
import { useUser } from '../context/UserContext';
|
||||||
import UsersPage from './UsersPage';
|
import UsersPage from './UsersPage';
|
||||||
import RolesPage from './RolesPage';
|
|
||||||
import SmtpServersPage from './SmtpServersPage';
|
import SmtpServersPage from './SmtpServersPage';
|
||||||
import EmailTemplatesPage from './EmailTemplatesPage';
|
import EmailTemplatesPage from './EmailTemplatesPage';
|
||||||
import UnsubscribedPage from './UnsubscribedPage';
|
import UnsubscribedPage from './UnsubscribedPage';
|
||||||
@ -23,7 +22,6 @@ const Dashboard = () => {
|
|||||||
function renderPage() {
|
function renderPage() {
|
||||||
switch (active) {
|
switch (active) {
|
||||||
case 'users': return <UsersPage />;
|
case 'users': return <UsersPage />;
|
||||||
case 'roles': return <RolesPage />;
|
|
||||||
case 'smtp': return <SmtpServersPage />;
|
case 'smtp': return <SmtpServersPage />;
|
||||||
case 'template': return <EmailTemplatesPage />;
|
case 'template': return <EmailTemplatesPage />;
|
||||||
case 'unsubscribed': return <UnsubscribedPage />;
|
case 'unsubscribed': return <UnsubscribedPage />;
|
||||||
|
|||||||
@ -62,12 +62,18 @@ function UsersPage() {
|
|||||||
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (res.ok && Array.isArray(data)) {
|
console.log('Roles API response:', data); // Отладочная информация
|
||||||
setRoles(data);
|
if (res.ok) {
|
||||||
|
// API всегда возвращает объект с rows и count
|
||||||
|
const rolesData = data.rows || [];
|
||||||
|
console.log('Setting roles:', rolesData); // Отладочная информация
|
||||||
|
setRoles(rolesData);
|
||||||
} else {
|
} else {
|
||||||
|
console.error('Roles API error:', data); // Отладочная информация
|
||||||
setRoles([]);
|
setRoles([]);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch (error) {
|
||||||
|
console.error('Roles fetch error:', error); // Отладочная информация
|
||||||
setRoles([]);
|
setRoles([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -128,6 +134,11 @@ function UsersPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
|
// Убеждаемся, что у нас есть роли перед созданием пользователя
|
||||||
|
if (roles.length === 0) {
|
||||||
|
alert('Загрузка ролей... Пожалуйста, подождите.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
setCreateUser({ email: '', name: '', role_id: roles[0]?.id || 1, password: '' });
|
setCreateUser({ email: '', name: '', role_id: roles[0]?.id || 1, password: '' });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -232,6 +243,16 @@ function UsersPage() {
|
|||||||
onSave={handleCreateSave}
|
onSave={handleCreateSave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{/* Отладочная информация */}
|
||||||
|
{process.env.NODE_ENV === 'development' && (
|
||||||
|
<div style={{ marginTop: 20, padding: 10, background: '#f0f0f0', fontSize: '12px' }}>
|
||||||
|
<strong>Debug Info:</strong><br/>
|
||||||
|
Roles count: {roles.length}<br/>
|
||||||
|
Roles: {JSON.stringify(roles.slice(0, 3))}<br/>
|
||||||
|
Edit user: {editUser ? JSON.stringify(editUser) : 'null'}<br/>
|
||||||
|
Create user: {createUser ? JSON.stringify(createUser) : 'null'}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user