auth service
This commit is contained in:
parent
790411a2d7
commit
26973a5126
38
auth-service/src/controllers/authController.js
Normal file
38
auth-service/src/controllers/authController.js
Normal file
@ -0,0 +1,38 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { User } from '../models/index.js';
|
||||
|
||||
function getJwtSecret(payload) {
|
||||
const base = process.env.JWT_SECRET || 'secret';
|
||||
// Если rememberMe, используем только base
|
||||
if (payload && payload.rememberMe) return base;
|
||||
// Иначе добавляем "часовой" компонент
|
||||
const hour = new Date().toISOString().slice(0, 13); // YYYY-MM-DDTHH
|
||||
return base + ':' + hour;
|
||||
}
|
||||
|
||||
export default {
|
||||
async verify(req, res) {
|
||||
let token = req.body.token;
|
||||
if (!token && req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
|
||||
token = req.headers.authorization.split(' ')[1];
|
||||
}
|
||||
if (!token) {
|
||||
return res.status(401).json({ error: 'No token provided' });
|
||||
}
|
||||
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' });
|
||||
res.json({ id: user.id, email: user.email, role_id: user.role_id });
|
||||
} catch (err) {
|
||||
return res.status(401).json({ error: 'Invalid token', details: err.message });
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -65,12 +65,22 @@ export default {
|
||||
},
|
||||
async login(req, res) {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
const { email, password, rememberMe } = req.body;
|
||||
const user = await User.findOne({ where: { email } });
|
||||
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
|
||||
const valid = await bcrypt.compare(password, user.password_hash);
|
||||
if (!valid) return res.status(401).json({ error: 'Invalid credentials' });
|
||||
const token = jwt.sign({ id: user.id, role_id: user.role_id }, process.env.JWT_SECRET, { expiresIn: '1d' });
|
||||
let token, secret, expiresIn;
|
||||
if (rememberMe) {
|
||||
secret = process.env.JWT_SECRET || 'secret';
|
||||
expiresIn = '30d';
|
||||
} else {
|
||||
const base = process.env.JWT_SECRET || 'secret';
|
||||
const hour = new Date().toISOString().slice(0, 13); // YYYY-MM-DDTHH
|
||||
secret = base + ':' + hour;
|
||||
expiresIn = '1h';
|
||||
}
|
||||
token = jwt.sign({ id: user.id, role_id: user.role_id, rememberMe: !!rememberMe }, secret, { expiresIn });
|
||||
res.json({ token, user: { id: user.id, email: user.email, name: user.name, role_id: user.role_id } });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
|
||||
@ -3,6 +3,7 @@ import userRoutes from './user.js';
|
||||
import roleRoutes from './role.js';
|
||||
import permissionRoutes from './permission.js';
|
||||
import rolePermissionRoutes from './rolePermission.js';
|
||||
import authController from '../controllers/authController.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@ -10,5 +11,6 @@ router.use('/users', userRoutes);
|
||||
router.use('/roles', roleRoutes);
|
||||
router.use('/permissions', permissionRoutes);
|
||||
router.use('/role-permissions', rolePermissionRoutes);
|
||||
router.post('/verify', authController.verify);
|
||||
|
||||
export default router;
|
||||
@ -3,13 +3,22 @@ import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-d
|
||||
import Login from './pages/Login';
|
||||
import Dashboard from './pages/Dashboard';
|
||||
import './App.css';
|
||||
import { useUser } from './context/UserContext';
|
||||
|
||||
function App() {
|
||||
const { user, token, isCheckingAuth } = useUser();
|
||||
|
||||
if (isCheckingAuth) {
|
||||
return <div style={{ textAlign: 'center', marginTop: 80 }}>Проверка авторизации...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/dashboard" element={<Dashboard />} />
|
||||
<Route
|
||||
path="/dashboard"
|
||||
element={user && token ? <Dashboard /> : <Navigate to="/login" replace />} />
|
||||
<Route path="*" element={<Navigate to="/login" replace />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
|
||||
@ -5,6 +5,7 @@ const UserContext = createContext();
|
||||
export function UserProvider({ children }) {
|
||||
const [user, setUser] = useState(null);
|
||||
const [token, setToken] = useState(null);
|
||||
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Инициализация из localStorage
|
||||
@ -13,7 +14,27 @@ export function UserProvider({ children }) {
|
||||
if (storedUser && storedToken) {
|
||||
setUser(JSON.parse(storedUser));
|
||||
setToken(storedToken);
|
||||
// Проверка токена
|
||||
fetch('/api/auth/verify', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${storedToken}` },
|
||||
body: JSON.stringify({ token: storedToken })
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error('Invalid token');
|
||||
return res.json();
|
||||
})
|
||||
.then(() => {
|
||||
setIsCheckingAuth(false);
|
||||
})
|
||||
.catch(() => {
|
||||
logout();
|
||||
window.location.href = '/login';
|
||||
});
|
||||
} else {
|
||||
setIsCheckingAuth(false);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, []);
|
||||
|
||||
const login = (user, token) => {
|
||||
@ -31,7 +52,7 @@ export function UserProvider({ children }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<UserContext.Provider value={{ user, token, login, logout }}>
|
||||
<UserContext.Provider value={{ user, token, login, logout, isCheckingAuth }}>
|
||||
{children}
|
||||
</UserContext.Provider>
|
||||
);
|
||||
|
||||
@ -25,12 +25,18 @@ function SmtpServersPage() {
|
||||
}, [page]);
|
||||
|
||||
const fetchServers = async (page) => {
|
||||
if (!token) {
|
||||
setError('Нет авторизации');
|
||||
setServers([]);
|
||||
setTotal(0);
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setError('');
|
||||
try {
|
||||
const offset = (page - 1) * PAGE_SIZE;
|
||||
const res = await fetch(`/api/mail/smtp-servers?limit=${PAGE_SIZE}&offset=${offset}`, {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
@ -51,12 +57,16 @@ function SmtpServersPage() {
|
||||
};
|
||||
|
||||
const handleDelete = async (id) => {
|
||||
if (!token) {
|
||||
alert('Нет авторизации');
|
||||
return;
|
||||
}
|
||||
if (!window.confirm('Удалить SMTP-сервер?')) return;
|
||||
setDeleteLoading(id);
|
||||
try {
|
||||
const res = await fetch(`/api/mail/smtp-servers/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json();
|
||||
@ -77,6 +87,10 @@ function SmtpServersPage() {
|
||||
|
||||
const handleEditSave = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!token) {
|
||||
alert('Нет авторизации');
|
||||
return;
|
||||
}
|
||||
setEditLoading(true);
|
||||
try {
|
||||
const { group_id, ...serverData } = editServer;
|
||||
@ -84,7 +98,7 @@ function SmtpServersPage() {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {})
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ ...serverData, user_id: user?.id })
|
||||
});
|
||||
@ -110,6 +124,10 @@ function SmtpServersPage() {
|
||||
|
||||
const handleCreateSave = async (e) => {
|
||||
e.preventDefault();
|
||||
if (!token) {
|
||||
alert('Нет авторизации');
|
||||
return;
|
||||
}
|
||||
setCreateLoading(true);
|
||||
try {
|
||||
const { group_id, ...serverData } = createServer;
|
||||
@ -117,7 +135,7 @@ function SmtpServersPage() {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {})
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ ...serverData, user_id: user?.id })
|
||||
});
|
||||
|
||||
@ -5,6 +5,7 @@ import { sequelize } from './models/index.js';
|
||||
import routes from './routes/index.js';
|
||||
import { processScheduledCampaigns } from './service/queueFillerJob.js';
|
||||
import { startMailSender } from './service/mailSender.js';
|
||||
import authMiddleware from './middleware/auth.js';
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
@ -13,7 +14,7 @@ app.get('/', (req, res) => {
|
||||
res.send('Mail Service is running');
|
||||
});
|
||||
|
||||
app.use('/api/mail', routes);
|
||||
app.use('/api/mail', authMiddleware, routes);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
|
||||
23
mail-service/src/middleware/auth.js
Normal file
23
mail-service/src/middleware/auth.js
Normal file
@ -0,0 +1,23 @@
|
||||
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 {
|
||||
// Проверяем токен через auth-service
|
||||
const resp = await fetch('http://auth-service:3000/api/auth/verify', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
|
||||
body: JSON.stringify({ token }),
|
||||
});
|
||||
if (!resp.ok) {
|
||||
return res.status(401).json({ error: 'Invalid token' });
|
||||
}
|
||||
const user = await resp.json();
|
||||
req.user = user;
|
||||
next();
|
||||
} catch (err) {
|
||||
return res.status(401).json({ error: 'Auth service error', details: err.message });
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user