thay đổi quyền chia sẻ của user và quyền xem của các scene
This commit is contained in:
+100
-22
@@ -532,7 +532,56 @@ router.get('/scenes/:id', optionalAuth, async (req, res) => {
|
||||
return res.status(403).json({ message: 'Access denied to this scene' });
|
||||
}
|
||||
|
||||
res.json(scene);
|
||||
// Tăng số lượt xem nếu truy cập qua link chia sẻ
|
||||
if (scene.privacy === 'shared' && req.query.token === scene.shareToken && isTokenValid) {
|
||||
// Tăng tổng lượt xem
|
||||
scene.views = (scene.views || 0) + 1;
|
||||
|
||||
// Cập nhật lịch sử lượt xem theo ngày
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0); // Đặt về đầu ngày để nhóm theo ngày
|
||||
|
||||
const existingEntry = scene.viewHistory.find(entry =>
|
||||
entry.date.getTime() === today.getTime()
|
||||
);
|
||||
|
||||
if (existingEntry) {
|
||||
existingEntry.count = (existingEntry.count || 0) + 1;
|
||||
} else {
|
||||
scene.viewHistory.push({ date: today, count: 1 });
|
||||
}
|
||||
|
||||
await scene.save();
|
||||
}
|
||||
|
||||
// Kiểm tra xem scene này có phải là scene con của một hotspot nào đó không
|
||||
const isChildScene = await Hotspot.exists({ target_scene_id: scene._id });
|
||||
// Trả về đối tượng scene đã được chuyển đổi sang plain object để thêm thuộc tính
|
||||
res.json({ ...scene.toObject(), isChildScene: !!isChildScene });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/me/scenes/:id/view-stats
|
||||
* @desc Lấy dữ liệu thống kê lượt xem theo thời gian của một scene
|
||||
* @access Private (Owner only)
|
||||
*/
|
||||
router.get('/me/scenes/:id/view-stats', protect, async (req, res) => {
|
||||
try {
|
||||
const scene = await Scene.findById(req.params.id);
|
||||
|
||||
if (!scene) {
|
||||
return res.status(404).json({ message: 'Scene not found' });
|
||||
}
|
||||
|
||||
// Chỉ chủ sở hữu mới được xem thống kê chi tiết
|
||||
if (scene.createdBy.toString() !== req.user._id.toString()) {
|
||||
return res.status(403).json({ message: 'Bạn không có quyền xem thống kê này' });
|
||||
}
|
||||
|
||||
res.json(scene.viewHistory.sort((a, b) => a.date - b.date)); // Sắp xếp theo ngày tăng dần
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
@@ -759,7 +808,7 @@ router.get('/assets/view/:assetId', verifyReferer, optionalAuth, async (req, res
|
||||
* @desc Update an existing scene
|
||||
* @access Private (Owner only)
|
||||
*/
|
||||
router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
|
||||
router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res, next) => {
|
||||
try {
|
||||
const { title, description, privacy, sharedWithUsers, sharedEmails, shareExpireDays, lat, lng } = req.body;
|
||||
const scene = await Scene.findById(req.params.id);
|
||||
@@ -768,6 +817,10 @@ router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
|
||||
return res.status(403).json({ message: 'Not authorized' });
|
||||
}
|
||||
|
||||
// Đảm bảo req.user là một đối tượng thuần túy để ngăn chặn validation/save ngầm định của Mongoose
|
||||
// Đây là một biện pháp phòng ngừa nếu req.user là một Mongoose document và có middleware khác cố gắng lưu nó.
|
||||
if (req.user && typeof req.user.toObject === 'function') req.user = req.user.toObject();
|
||||
|
||||
const oldPrivacy = scene.privacy;
|
||||
|
||||
// Update basic info
|
||||
@@ -796,29 +849,43 @@ router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
|
||||
// LOGIC ĐỒNG BỘ QUYỀN RIÊNG TƯ (CASCADING PRIVACY)
|
||||
// Nếu quyền chia sẻ thay đổi, cập nhật toàn bộ các Scene con liên kết trực tiếp
|
||||
if (privacy && privacy !== oldPrivacy) {
|
||||
try {
|
||||
const linkedHotspots = await Hotspot.find({ parent_scene_id: scene._id });
|
||||
const targetSceneIds = linkedHotspots
|
||||
.map(h => h.target_scene_id)
|
||||
.filter(id => id && id.toString() !== scene._id.toString());
|
||||
const linkedHotspots = await Hotspot.find({ parent_scene_id: scene._id });
|
||||
const targetSceneIds = linkedHotspots
|
||||
.map(h => h.target_scene_id)
|
||||
.filter(id => id && id.toString() !== scene._id.toString());
|
||||
|
||||
if (targetSceneIds.length > 0) {
|
||||
for (const targetId of targetSceneIds) {
|
||||
const updateData = { privacy: privacy };
|
||||
|
||||
// Nếu chuyển sang 'shared', đảm bảo scene con cũng có token riêng
|
||||
if (privacy === 'shared') {
|
||||
const target = await Scene.findById(targetId);
|
||||
if (target && !target.shareToken) {
|
||||
updateData.shareToken = crypto.randomBytes(24).toString('hex');
|
||||
if (targetSceneIds.length > 0) {
|
||||
for (const targetId of targetSceneIds) {
|
||||
const updateData = { privacy: privacy };
|
||||
let newShareToken = null;
|
||||
|
||||
// Nếu chuyển sang 'shared', đảm bảo scene con cũng có token riêng
|
||||
if (privacy === 'shared') {
|
||||
const target = await Scene.findById(targetId);
|
||||
if (target && !target.shareToken) {
|
||||
newShareToken = crypto.randomBytes(24).toString('hex');
|
||||
updateData.shareToken = newShareToken;
|
||||
// Đặt thời hạn token của scene con giống scene cha nếu có
|
||||
if (scene.shareTokenExpires) {
|
||||
updateData.shareTokenExpires = scene.shareTokenExpires;
|
||||
}
|
||||
} else if (target && target.shareToken) {
|
||||
// Nếu scene con đã có token, giữ nguyên
|
||||
updateData.shareToken = target.shareToken;
|
||||
if (scene.shareTokenExpires) {
|
||||
updateData.shareTokenExpires = scene.shareTokenExpires;
|
||||
} else {
|
||||
updateData.shareTokenExpires = null;
|
||||
}
|
||||
}
|
||||
await Scene.updateOne({ _id: targetId }, { $set: updateData });
|
||||
} else {
|
||||
// Nếu không phải 'shared', xóa token và thời hạn của scene con
|
||||
updateData.shareToken = null;
|
||||
updateData.shareTokenExpires = null;
|
||||
}
|
||||
console.log(`[Privacy Sync] Cascaded ${privacy} status to ${targetSceneIds.length} linked scenes.`);
|
||||
await Scene.updateOne({ _id: targetId }, { $set: updateData });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Lỗi khi đồng bộ quyền riêng tư cho các scene con:", err.message);
|
||||
console.log(`[Privacy Sync] Cascaded privacy status to ${targetSceneIds.length} linked scenes.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,7 +924,10 @@ router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
|
||||
await scene.save();
|
||||
res.json({ message: 'Scene updated', scene });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
if (error.name === 'ValidationError') {
|
||||
return res.status(400).json({ message: error.message });
|
||||
}
|
||||
next(error); // Chuyển lỗi khác cho middleware xử lý lỗi chung
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1106,7 +1176,15 @@ router.get('/me/scenes', protect, async (req, res) => {
|
||||
const scenes = await Scene.find({ createdBy: req.user._id })
|
||||
.populate('createdBy', 'username')
|
||||
.populate('assetId')
|
||||
.sort({ createdAt: -1 });
|
||||
.select('+views') // Đảm bảo trường 'views' được chọn nếu nó bị ẩn theo mặc định trong schema
|
||||
.sort({ createdAt: -1 })
|
||||
.lean(); // Sử dụng .lean() để tăng hiệu suất khi thêm thuộc tính tùy chỉnh
|
||||
|
||||
// Kiểm tra xem mỗi scene có phải là scene con hay không
|
||||
for (let i = 0; i < scenes.length; i++) {
|
||||
const isChild = await Hotspot.exists({ target_scene_id: scenes[i]._id });
|
||||
scenes[i].isChildScene = !!isChild;
|
||||
}
|
||||
res.json(scenes);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
|
||||
Reference in New Issue
Block a user