TransHub_backend / routes /weeklyPracticeFiles.js
linguabot's picture
Upload folder using huggingface_hub
da819ac verified
Raw
History Blame Contribute Delete
5.23 kB
const express = require('express');
const router = express.Router();
const multer = require('multer');
const WeeklyPracticeFile = require('../models/WeeklyPracticeFile');
const { authenticateToken } = require('./auth');
// Use memory storage and persist to MongoDB Grid-like buffer
const upload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 25 * 1024 * 1024 } // 25MB
});
// Helper to read role from headers set by frontend interceptor
const getRole = (req) => req.headers['user-role'] || 'visitor';
const getUserInfo = (req) => {
try {
return req.headers['user-info'] ? JSON.parse(req.headers['user-info']) : {};
} catch { return {}; }
};
// Consistently decode filenames from latin1 -> utf8 (multer provides latin1 strings)
const decodeMaybeLatin1 = (name) => {
try {
if (!name) return name;
return Buffer.from(name, 'latin1').toString('utf8');
} catch (_) {
return name;
}
};
const encodeRFC5987 = (str) => {
return encodeURIComponent(str)
.replace(/['()]/g, escape)
.replace(/\*/g, '%2A');
};
// List files for a given week
router.get('/week/:weekNumber', authenticateToken, async (req, res) => {
try {
const weekNumber = parseInt(req.params.weekNumber);
const type = req.query.type; // optional: 'source' | 'translation'
const criteria = { weekNumber };
if (type === 'source' || type === 'translation') criteria.type = type;
const files = await WeeklyPracticeFile.find(criteria).select('-data').sort({ createdAt: 1 });
res.json({ success: true, files });
} catch (error) {
console.error('List weekly practice files error:', error);
res.status(500).json({ error: 'Failed to list files' });
}
});
// Download file by id
router.get('/:id/download', authenticateToken, async (req, res) => {
try {
const file = await WeeklyPracticeFile.findById(req.params.id);
if (!file) return res.status(404).json({ error: 'File not found' });
res.setHeader('Content-Type', file.mimeType);
const safeName = decodeMaybeLatin1(file.fileName || 'download');
const asciiFallback = safeName.replace(/[^\x20-\x7E]/g, '_');
const utf8Star = encodeRFC5987(safeName);
res.setHeader('Content-Disposition', `attachment; filename="${asciiFallback}"; filename*=UTF-8''${utf8Star}`);
return res.send(file.data);
} catch (error) {
console.error('Download weekly practice file error:', error);
res.status(500).json({ error: 'Failed to download file' });
}
});
// Admin uploads source files for a week
router.post('/week/:weekNumber/source', authenticateToken, upload.single('file'), async (req, res) => {
try {
if (getRole(req) !== 'admin') return res.status(403).json({ error: 'Admin access required' });
const weekNumber = parseInt(req.params.weekNumber);
if (!req.file) return res.status(400).json({ error: 'Missing file' });
const { description } = req.body || {};
const saved = await WeeklyPracticeFile.create({
type: 'source',
weekNumber,
fileName: decodeMaybeLatin1(req.file.originalname),
mimeType: req.file.mimetype,
size: req.file.size,
data: req.file.buffer,
description
});
res.status(201).json({ success: true, file: { ...saved.toObject(), data: undefined } });
} catch (error) {
console.error('Upload weekly source file error:', error);
res.status(500).json({ error: 'Failed to upload source file' });
}
});
// Students upload translations, optionally referencing a specific sourceFileId
router.post('/week/:weekNumber/translation', authenticateToken, upload.single('file'), async (req, res) => {
try {
const weekNumber = parseInt(req.params.weekNumber);
if (!req.file) return res.status(400).json({ error: 'Missing file' });
const { description, sourceFileId } = req.body || {};
const user = getUserInfo(req);
const displayUsername = user?.username || user?.name || user?.displayName || (user?.email ? String(user.email).split('@')[0] : undefined) || 'Unknown User';
const saved = await WeeklyPracticeFile.create({
type: 'translation',
weekNumber,
sourceFileId: sourceFileId || undefined,
userId: user._id || undefined,
username: displayUsername,
fileName: decodeMaybeLatin1(req.file.originalname),
mimeType: req.file.mimetype,
size: req.file.size,
data: req.file.buffer,
description
});
res.status(201).json({ success: true, file: { ...saved.toObject(), data: undefined } });
} catch (error) {
console.error('Upload weekly translation file error:', error);
res.status(500).json({ error: 'Failed to upload translation file' });
}
});
// Admin delete a file by id
router.delete('/:id', authenticateToken, async (req, res) => {
try {
if (getRole(req) !== 'admin') return res.status(403).json({ error: 'Admin access required' });
const deleted = await WeeklyPracticeFile.findByIdAndDelete(req.params.id);
if (!deleted) return res.status(404).json({ error: 'File not found' });
res.json({ success: true });
} catch (error) {
console.error('Delete weekly practice file error:', error);
res.status(500).json({ error: 'Failed to delete file' });
}
});
module.exports = router;