Cài đặt quota cho các thành viên

This commit is contained in:
2026-06-09 12:29:38 +07:00
parent 9f9c38e6e7
commit 0eaaf074ba
6 changed files with 346 additions and 16 deletions
+62 -3
View File
@@ -13,6 +13,7 @@ const Setting = require('../models/Setting');
const { protect, optionalAuth } = require('../middlewares/authMiddleware');
const { verifyReferer, setNoCacheHeaders } = require('../middlewares/securityMiddleware');
const { checkQuota, ROLE_QUOTAS } = require('../middlewares/quotaMiddleware');
const { resizeTo8K } = require('../utils/imageHelper');
const { getGPSCoordinates, injectGPSCoordinates } = require('../utils/exifHelper');
const { imageQueue } = require('./imageQueue');
@@ -78,7 +79,7 @@ const uploadSinglePanorama = (req, res, next) => {
* @desc Create a new 3D scene (with 360 photo, 8K resize, EXIF injection)
* @access Private (Registered Users)
*/
router.post('/scenes', protect, uploadSinglePanorama, async (req, res) => {
router.post('/scenes', protect, uploadSinglePanorama, checkQuota, async (req, res) => {
try {
const { title, lat, lng, privacy, sharedWithUsers } = req.body;
@@ -107,6 +108,7 @@ router.post('/scenes', protect, uploadSinglePanorama, async (req, res) => {
// 5. Save Asset to DB
const asset = new Asset({
filePath: tempFilePath, // Tạm thời dùng file gốc cho đến khi worker xử lý xong
fileSize: req.file.size,
uploadedBy: req.user._id,
coordinates: originalGPS ? { lat: originalGPS.lat, lng: originalGPS.lng } : { lat: latitude, lng: longitude }
});
@@ -740,8 +742,65 @@ router.delete('/scenes/:id', protect, async (req, res) => {
*/
router.get('/me/profile', protect, async (req, res) => {
try {
const user = await User.findById(req.user._id).select('-password');
res.json(user);
const user = await User.findById(req.user._id).select('-password').lean();
// Tính toán dung lượng thực tế của người dùng
const usageResult = await Asset.aggregate([
{ $match: { uploadedBy: req.user._id } },
{
$group: {
_id: null,
totalUsage: { $sum: { $ifNull: ["$fileSize", 0] } }
}
}
]);
const currentUsage = usageResult.length > 0 ? usageResult[0].totalUsage : 0;
const quota = ROLE_QUOTAS[user.role] || ROLE_QUOTAS['Thành viên'];
res.json({
...user,
storage: {
used: currentUsage,
quota: quota === Infinity ? -1 : quota // -1 đại diện cho không giới hạn
}
});
} catch (error) {
res.status(500).json({ message: error.message });
}
});
/**
* @route GET /api/me/assets/top-large
* @desc Lấy danh sách 5 tệp tin chiếm dung lượng lớn nhất của người dùng
* @access Private
*/
router.get('/me/assets/top-large', protect, async (req, res) => {
try {
const topAssets = await Asset.aggregate([
{ $match: { uploadedBy: req.user._id } },
{ $sort: { fileSize: -1 } },
{ $limit: 5 },
{
$lookup: {
from: 'scenes',
localField: '_id',
foreignField: 'assetId',
as: 'scene'
}
},
{ $unwind: { path: '$scene', preserveNullAndEmptyArrays: true } },
{
$project: {
fileSize: 1,
createdAt: 1,
'scene.name': 1,
'scene.title': 1,
'scene._id': 1
}
}
]);
res.json(topAssets);
} catch (error) {
res.status(500).json({ message: error.message });
}