compress: снять лимиты (размер/кол-во/rate-limit) для админа
This commit is contained in:
parent
87f8c55da0
commit
4259c3daf7
@ -120,7 +120,7 @@
|
||||
<h1 class="text-3xl sm:text-4xl font-extrabold tracking-tight dark:text-white text-gray-900">
|
||||
Конвертер <span class="text-accent">изображений</span>
|
||||
</h1>
|
||||
<p class="description mt-2 text-sm dark:text-gray-500 text-gray-400 font-mono">Сжатие, ресайз и конвертация · до 50 файлов · до 20 МБ</p>
|
||||
<p id="limitsHint" class="description mt-2 text-sm dark:text-gray-500 text-gray-400 font-mono">Сжатие, ресайз и конвертация · до 50 файлов · до 20 МБ</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -297,7 +297,17 @@ document.getElementById('resize').addEventListener('change', function() {
|
||||
});
|
||||
|
||||
const VALID_TYPES = ['image/jpeg','image/png','image/webp','image/avif','image/tiff','image/gif','image/bmp','image/svg+xml'];
|
||||
const MAX_SIZE = 20 * 1024 * 1024;
|
||||
let MAX_SIZE = 20 * 1024 * 1024;
|
||||
let isAdmin = false;
|
||||
// Админу — без лимитов по размеру/количеству
|
||||
(function(){ fetch('/auth/me').then(function(r){return r.ok?r.json():null}).then(function(u){
|
||||
if (u && u.role === 'admin') {
|
||||
isAdmin = true;
|
||||
MAX_SIZE = Infinity;
|
||||
var d = document.getElementById('limitsHint');
|
||||
if (d) d.textContent = 'Сжатие, ресайз и конвертация · без лимитов';
|
||||
}
|
||||
}).catch(function(){}); })();
|
||||
|
||||
function handleFiles(files) {
|
||||
const arr = Array.from(files);
|
||||
|
||||
@ -35,21 +35,35 @@ setInterval(() => cleanupDir(RESULTS_DIR, 30 * 60 * 1000), 5 * 60 * 1000);
|
||||
const maxFileSize = (parseInt(process.env.MAX_FILE_SIZE_MB) || 20) * 1024 * 1024;
|
||||
const maxFiles = parseInt(process.env.MAX_FILES) || 50;
|
||||
|
||||
const upload = multer({
|
||||
dest: UPLOADS_DIR,
|
||||
limits: { fileSize: maxFileSize },
|
||||
fileFilter: (req, file, cb) => {
|
||||
const validTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/avif', 'image/tiff', 'image/gif', 'image/bmp', 'image/svg+xml'];
|
||||
if (validTypes.includes(file.mimetype) || file.originalname.match(/\.(jpe?g|png|webp|avif|tiff?|gif|bmp|svg|heic|heif)$/i)) cb(null, true);
|
||||
else cb(new Error('Неподдерживаемый формат изображения'));
|
||||
},
|
||||
});
|
||||
const imageFileFilter = (req, file, cb) => {
|
||||
const validTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/avif', 'image/tiff', 'image/gif', 'image/bmp', 'image/svg+xml'];
|
||||
if (validTypes.includes(file.mimetype) || file.originalname.match(/\.(jpe?g|png|webp|avif|tiff?|gif|bmp|svg|heic|heif)$/i)) cb(null, true);
|
||||
else cb(new Error('Неподдерживаемый формат изображения'));
|
||||
};
|
||||
|
||||
// Rate limiter
|
||||
// Админ — без лимитов (размер, кол-во файлов, rate-limit)
|
||||
function isAdmin(req) {
|
||||
return !!(req.session && req.session.user && req.session.user.role === 'admin');
|
||||
}
|
||||
|
||||
// User — лимит размера из .env; Admin — без лимита размера
|
||||
const uploadUser = multer({ dest: UPLOADS_DIR, limits: { fileSize: maxFileSize }, fileFilter: imageFileFilter });
|
||||
const uploadAdmin = multer({ dest: UPLOADS_DIR, fileFilter: imageFileFilter });
|
||||
|
||||
// Динамический выбор multer: админу — без лимита количества и размера
|
||||
function uploadArray(req, res, next) {
|
||||
return (isAdmin(req) ? uploadAdmin.array('images') : uploadUser.array('images', maxFiles))(req, res, next);
|
||||
}
|
||||
function uploadSingle(req, res, next) {
|
||||
return (isAdmin(req) ? uploadAdmin : uploadUser).single('image')(req, res, next);
|
||||
}
|
||||
|
||||
// Rate limiter (админ пропускается — безлимитная загрузка по одному файлу на запрос)
|
||||
const limiter = rateLimit({
|
||||
keyGenerator: (req) => (req.session && req.session.user && req.session.user.id) ? "user_" + req.session.user.id : req.ip,
|
||||
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 60000,
|
||||
max: parseInt(process.env.RATE_LIMIT_MAX) || 30,
|
||||
skip: isAdmin,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
message: { error: 'Слишком много запросов. Попробуйте через минуту.' },
|
||||
@ -104,7 +118,7 @@ router.get('/', (req, res) => {
|
||||
});
|
||||
|
||||
// Compress endpoint
|
||||
router.post('/', limiter, upload.array('images', maxFiles), async (req, res) => {
|
||||
router.post('/', limiter, uploadArray, async (req, res) => {
|
||||
const resize = parseInt(req.body.resize || '0', 10);
|
||||
const format = (req.body.format || 'original').toLowerCase();
|
||||
const quality = Math.min(100, Math.max(1, parseInt(req.body.quality || process.env.COMPRESS_QUALITY || '80', 10)));
|
||||
@ -196,7 +210,7 @@ router.post('/', limiter, upload.array('images', maxFiles), async (req, res) =>
|
||||
|
||||
|
||||
// Single file compress endpoint
|
||||
router.post('/single', limiter, upload.single('image'), async (req, res) => {
|
||||
router.post('/single', limiter, uploadSingle, async (req, res) => {
|
||||
const resize = parseInt(req.body.resize || '0', 10);
|
||||
const format = (req.body.format || 'original').toLowerCase();
|
||||
const quality = Math.min(100, Math.max(1, parseInt(req.body.quality || process.env.COMPRESS_QUALITY || '80', 10)));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user