Sửa chế độ chia sẻ privacy trực tiếp khi nhấn chuột phải lên scene
This commit is contained in:
@@ -165,6 +165,33 @@ router.post('/scenes', protect, uploadSinglePanorama, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/users/search
|
||||
* @desc Tìm kiếm người dùng theo username hoặc email để chia sẻ
|
||||
* @access Private
|
||||
*/
|
||||
router.get('/users/search', protect, async (req, res) => {
|
||||
const query = req.query.q;
|
||||
if (!query || query.length < 2) return res.json([]);
|
||||
|
||||
try {
|
||||
const users = await User.find({
|
||||
$and: [
|
||||
{ _id: { $ne: req.user._id } }, // Không tìm chính mình
|
||||
{
|
||||
$or: [
|
||||
{ username: { $regex: query, $options: 'i' } },
|
||||
{ email: { $regex: query, $options: 'i' } }
|
||||
]
|
||||
}
|
||||
]
|
||||
}).select('username email').limit(10);
|
||||
res.json(users);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/scenes
|
||||
* @desc Get all accessible scenes for the map (respecting privacy rules)
|
||||
@@ -183,7 +210,8 @@ router.get('/scenes', optionalAuth, async (req, res) => {
|
||||
{ privacy: 'member' },
|
||||
{ privacy: 'shared' }, // shareToken will be required to fetch panorama, but coordinates show on map
|
||||
{ createdBy: req.user._id },
|
||||
{ sharedWith: req.user._id }
|
||||
{ sharedWith: req.user._id },
|
||||
{ sharedEmails: req.user.email }
|
||||
]
|
||||
};
|
||||
} else {
|
||||
@@ -223,12 +251,17 @@ router.get('/scenes/:id', optionalAuth, async (req, res) => {
|
||||
return res.status(404).json({ message: 'Scene not found' });
|
||||
}
|
||||
|
||||
// Kiểm tra Token hết hạn
|
||||
const isTokenValid = scene.shareToken &&
|
||||
(!scene.shareTokenExpires || new Date() < scene.shareTokenExpires);
|
||||
|
||||
const userEmail = req.user ? req.user.email : null;
|
||||
const hasAccess =
|
||||
scene.privacy === 'public' ||
|
||||
(scene.privacy === 'member' && req.user) ||
|
||||
(scene.privacy === 'member' && req.user && (scene.sharedWith.includes(req.user._id) || (userEmail && scene.sharedEmails.includes(userEmail)))) ||
|
||||
(req.user && scene.createdBy._id.toString() === req.user._id.toString()) ||
|
||||
(req.user && scene.sharedWith.includes(req.user._id)) ||
|
||||
(scene.privacy === 'shared' && req.query.token === scene.shareToken);
|
||||
(scene.privacy === 'shared' && req.query.token === scene.shareToken && isTokenValid);
|
||||
|
||||
if (!hasAccess) {
|
||||
return res.status(403).json({ message: 'Access denied to this scene' });
|
||||
@@ -382,12 +415,17 @@ router.get('/assets/view/:assetId', verifyReferer, optionalAuth, async (req, res
|
||||
return res.status(403).json({ message: 'Access denied' });
|
||||
}
|
||||
} else {
|
||||
// Kiểm tra Token hết hạn
|
||||
const isTokenValid = scene.shareToken &&
|
||||
(!scene.shareTokenExpires || new Date() < scene.shareTokenExpires);
|
||||
|
||||
const userEmail = req.user ? req.user.email : null;
|
||||
const hasAccess =
|
||||
scene.privacy === 'public' ||
|
||||
(scene.privacy === 'member' && req.user) ||
|
||||
(scene.privacy === 'member' && req.user && (scene.sharedWith.includes(req.user._id) || (userEmail && scene.sharedEmails.includes(userEmail)))) ||
|
||||
(req.user && scene.createdBy.toString() === req.user._id.toString()) ||
|
||||
(req.user && scene.sharedWith.includes(req.user._id)) ||
|
||||
(scene.privacy === 'shared' && req.query.token === scene.shareToken);
|
||||
(scene.privacy === 'shared' && req.query.token === scene.shareToken && isTokenValid);
|
||||
|
||||
if (!hasAccess) {
|
||||
return res.status(403).json({ message: 'Access denied: You do not have permission to view this asset' });
|
||||
@@ -423,7 +461,7 @@ router.get('/assets/view/:assetId', verifyReferer, optionalAuth, async (req, res
|
||||
*/
|
||||
router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
|
||||
try {
|
||||
const { title, privacy, sharedWithUsers, lat, lng } = req.body;
|
||||
const { title, description, privacy, sharedWithUsers, sharedEmails, shareExpireDays, lat, lng } = req.body;
|
||||
const scene = await Scene.findById(req.params.id);
|
||||
|
||||
if (!scene || scene.createdBy.toString() !== req.user._id.toString()) {
|
||||
@@ -434,10 +472,27 @@ router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
|
||||
|
||||
// Update basic info
|
||||
scene.name = title || scene.name;
|
||||
scene.description = description !== undefined ? description : scene.description;
|
||||
scene.privacy = privacy || scene.privacy;
|
||||
if (lat) scene.gps.lat = parseFloat(lat);
|
||||
if (lng) scene.gps.lng = parseFloat(lng);
|
||||
|
||||
// Cập nhật danh sách chia sẻ
|
||||
if (sharedWithUsers) {
|
||||
try {
|
||||
scene.sharedWith = JSON.parse(sharedWithUsers);
|
||||
} catch (e) {
|
||||
console.error("Lỗi parse sharedWithUsers:", e);
|
||||
}
|
||||
}
|
||||
if (sharedEmails) {
|
||||
try {
|
||||
scene.sharedEmails = JSON.parse(sharedEmails);
|
||||
} catch (e) {
|
||||
console.error("Lỗi parse sharedEmails:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -467,8 +522,18 @@ router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (privacy === 'shared' && !scene.shareToken) {
|
||||
scene.shareToken = crypto.randomBytes(24).toString('hex');
|
||||
if (privacy === 'shared') {
|
||||
if (!scene.shareToken) {
|
||||
scene.shareToken = crypto.randomBytes(24).toString('hex');
|
||||
}
|
||||
// Thiết lập ngày hết hạn nếu có truyền lên
|
||||
if (shareExpireDays && shareExpireDays !== 'never') {
|
||||
const expires = new Date();
|
||||
expires.setDate(expires.getDate() + parseInt(shareExpireDays));
|
||||
scene.shareTokenExpires = expires;
|
||||
} else if (shareExpireDays === 'never') {
|
||||
scene.shareTokenExpires = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Update image if new one is uploaded
|
||||
|
||||
Reference in New Issue
Block a user