fix(security): пер-юзер лимиты ИИ + SSE через одноразовый тикет (Спринт1 #5,#6)
#5 rate-limit (byUser) на дорогих LLM-эндпоинтах: /assistant/ask (20/мин), /assistant/flashcards (10/мин), /imggen (20/мин) — поверх cooldown/дневного лимита. Защита от «сжигания» бюджета провайдера одним аккаунтом. #6 SSE больше не таскает JWT в URL: добавлен authed /notifications/stream-ticket (одноразовый тикет, TTL 30с), клиент берёт тикет заголовком и подключается с ?ticket=. ?token= оставлен как временный фоллбэк для старых клиентов. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,16 +3,21 @@
|
||||
* 'pet' навешивается при монтировании в server.js. */
|
||||
const router = require('express').Router();
|
||||
const { authMiddleware, requireRole } = require('../middleware/auth');
|
||||
const rateLimit = require('../middleware/rateLimit');
|
||||
const ctrl = require('../controllers/assistantController');
|
||||
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Дорогие LLM-вызовы — пер-юзер лимит (защита от «сжигания» бюджета провайдера).
|
||||
const askLimiter = rateLimit({ windowMs: 60_000, max: 20, byUser: true, message: 'Слишком много запросов к ИИ — подожди минутку' });
|
||||
const fcLimiter = rateLimit({ windowMs: 60_000, max: 10, byUser: true, message: 'Слишком часто — подожди минутку' });
|
||||
|
||||
router.get('/context', ctrl.getContext);
|
||||
router.post('/seen', ctrl.markSeen);
|
||||
router.post('/dismiss', ctrl.dismiss);
|
||||
router.patch('/settings', ctrl.setSettings);
|
||||
router.post('/ask', ctrl.ask);
|
||||
router.post('/flashcards', ctrl.flashcardsFromText);
|
||||
router.post('/ask', askLimiter, ctrl.ask);
|
||||
router.post('/flashcards', fcLimiter, ctrl.flashcardsFromText);
|
||||
router.post('/feedback', ctrl.feedback);
|
||||
router.get('/memory', ctrl.getMemory);
|
||||
router.delete('/memory', ctrl.clearMemory);
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
'use strict';
|
||||
const router = require('express').Router();
|
||||
const { authMiddleware } = require('../middleware/auth');
|
||||
const rateLimit = require('../middleware/rateLimit');
|
||||
const ctrl = require('../controllers/imggenController');
|
||||
|
||||
// Пер-юзер лимит поверх cooldown/дневного лимита в контроллере (защита от абуза).
|
||||
const genLimiter = rateLimit({ windowMs: 60_000, max: 20, byUser: true, message: 'Слишком много генераций — подожди минутку' });
|
||||
|
||||
router.use(authMiddleware);
|
||||
router.get('/status', ctrl.status);
|
||||
router.post('/', ctrl.generate);
|
||||
router.post('/', genLimiter, ctrl.generate);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -6,6 +6,7 @@ const ctrl = require('../controllers/notificationController');
|
||||
router.get('/stream', ctrl.stream);
|
||||
|
||||
router.use(authMiddleware);
|
||||
router.get('/stream-ticket', ctrl.streamTicket);
|
||||
router.get('/', ctrl.list);
|
||||
router.post('/read-all', ctrl.markAllRead);
|
||||
router.patch('/:id/read', ctrl.markRead);
|
||||
|
||||
Reference in New Issue
Block a user