const express = require('express'); const router = express.Router(); const multer = require('multer'); const path = require('path'); const crypto = require('crypto'); const { authMiddleware, requireRole } = require('../middleware/auth'); const { safeExt } = require('../utils/magic'); const ctrl = require('../controllers/avatarController'); /* ── multer: avatars only, 2 MB ────────────────────────────────────────── */ const AVATARS_DIR = path.join(__dirname, '../../uploads/avatars'); const AVATAR_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp']); const storage = multer.diskStorage({ destination: AVATARS_DIR, filename: (_req, file, cb) => { // Расширение — из проверенного MIME (fileFilter уже сузил до image/*), // НЕ из client-controlled originalname (иначе .html/.svg → stored-XSS). const ext = safeExt(file.mimetype, '.png'); const name = crypto.randomBytes(16).toString('hex') + ext; cb(null, name); }, }); const upload = multer({ storage, limits: { fileSize: 2 * 1024 * 1024 }, // 2 MB fileFilter: (_req, file, cb) => { cb(null, AVATAR_TYPES.has(file.mimetype)); }, }); /* ── student routes ─────────────────────────────────────────────────────── */ router.post('/request', authMiddleware, upload.single('avatar'), ctrl.requestAvatar); router.get('/my-status', authMiddleware, ctrl.myStatus); router.delete('/me', authMiddleware, ctrl.removeAvatar); /* ── preset avatars (available to all roles, no moderation) ─────────────── */ router.get('/presets', authMiddleware, ctrl.listPresets); router.post('/preset', authMiddleware, ctrl.setPreset); /* ── moderator routes (teacher or admin) ────────────────────────────────── */ router.get('/pending', authMiddleware, requireRole('teacher', 'admin'), ctrl.getPending); router.post('/:id/approve', authMiddleware, requireRole('teacher', 'admin'), ctrl.approveAvatar); router.post('/:id/reject', authMiddleware, requireRole('teacher', 'admin'), ctrl.rejectAvatar); module.exports = router;