Sửa lỗi đăng nhập vào admin mà không reload được page do lỗi tạo scene trước đó, sử dụng lệnh resetDB.js để khởi tạo lại, xóa các scene trước và ảnh đã upload

This commit is contained in:
2026-06-08 10:48:54 +07:00
parent 81de520071
commit c495efad36
25 changed files with 290 additions and 47 deletions
+90 -2
View File
@@ -43,12 +43,33 @@ const upload = multer({
}
});
/**
* Wrapper for Multer middleware to catch "Request aborted" and other upload errors gracefully.
*/
const uploadSinglePanorama = (req, res, next) => {
const multerUpload = upload.single('panorama');
multerUpload(req, res, (err) => {
if (err) {
// Bắt lỗi khi client ngắt kết nối đột ngột
if (err.message === 'Request aborted') {
console.warn(`[Multer Warning]: Upload aborted by client at ${req.method} ${req.originalUrl}`);
return res.status(499).json({ message: 'Client aborted the request' });
}
if (err instanceof multer.MulterError) {
return res.status(400).json({ message: `Multer error: ${err.message}` });
}
return res.status(400).json({ message: err.message });
}
next();
});
};
/**
* @route POST /api/scenes
* @desc Create a new 3D scene (with 360 photo, 8K resize, EXIF injection)
* @access Private (Registered Users)
*/
router.post('/scenes', protect, upload.single('panorama'), async (req, res) => {
router.post('/scenes', protect, uploadSinglePanorama, async (req, res) => {
try {
const { title, lat, lng, privacy, sharedWithUsers } = req.body;
@@ -145,6 +166,7 @@ router.post('/scenes', protect, upload.single('panorama'), async (req, res) => {
*/
router.get('/scenes', optionalAuth, async (req, res) => {
try {
console.log(`[Data Load] Bắt đầu truy vấn scenes cho: ${req.user ? req.user._id : 'Khách'}`);
let query = {};
if (req.user) {
@@ -172,8 +194,10 @@ router.get('/scenes', optionalAuth, async (req, res) => {
.populate('owner', 'username')
.populate('assetId', 'coordinates createdAt');
console.log(`[Data Load] Đã tìm thấy ${scenes.length} scenes. Gửi phản hồi về Frontend...`);
res.json(scenes);
} catch (error) {
console.error(`[Data Load Error]: ${error.stack}`);
res.status(500).json({ message: error.message });
}
});
@@ -257,6 +281,33 @@ router.post('/scenes/:id/hotspots', protect, async (req, res) => {
await scene.save();
// LOGIC "MẸ - CON": TỰ ĐỘNG TẠO ĐIỂM QUAY LẠI
if (targetSceneId && targetSceneId !== 'null' && targetSceneId !== '' && typeof targetSceneId === 'string') {
try {
const targetScene = await Scene.findById(targetSceneId);
if (targetScene) {
const hasReverse = targetScene.hotspots.some(h =>
h.targetSceneId && h.targetSceneId.toString() === scene._id.toString()
);
if (!hasReverse) {
const originYaw = parseFloat(yaw) || 0;
const reverseYaw = originYaw > 0 ? originYaw - 180 : originYaw + 180;
targetScene.hotspots.push({
pitch: 0,
yaw: reverseYaw,
text: `Quay lại: ${scene.title}`,
targetSceneId: scene._id
});
await targetScene.save();
}
}
} catch (err) {
console.error("Lỗi tạo hotspot ngược:", err.message);
}
}
res.status(201).json({
message: 'Hotspot added successfully',
hotspots: scene.hotspots
@@ -325,7 +376,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, upload.single('panorama'), async (req, res) => {
router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
try {
const { title, privacy, sharedWithUsers, lat, lng } = req.body;
const scene = await Scene.findById(req.params.id);
@@ -396,4 +447,41 @@ router.delete('/scenes/:id', protect, async (req, res) => {
}
});
/**
* @route POST /api/maintenance/reset-all
* @desc Wipe all scenes, assets, and physical files (DANGEROUS: For dev reset only)
* @access Private (Owner only)
*/
router.post('/maintenance/reset-all', protect, async (req, res) => {
try {
// 1. Xóa toàn bộ dữ liệu trong Database
await Scene.deleteMany({});
await Asset.deleteMany({});
// Lưu ý: Không xóa Users trừ khi bạn muốn reset cả tài khoản
// 2. Dọn dẹp thư mục uploads (trừ các file .gitkeep hoặc thư mục temp)
const files = fs.readdirSync(uploadDir);
for (const file of files) {
const fullPath = path.join(uploadDir, file);
if (fs.lstatSync(fullPath).isFile()) {
fs.unlinkSync(fullPath);
}
}
// 3. Dọn dẹp thư mục temp
const tempFiles = fs.readdirSync(tempDir);
for (const file of tempFiles) {
const fullPath = path.join(tempDir, file);
if (fs.lstatSync(fullPath).isFile()) {
fs.unlinkSync(fullPath);
}
}
console.warn(`[Maintenance]: Toàn bộ dữ liệu tour đã bị xóa bởi ${req.user.username}`);
res.json({ message: 'Dữ liệu đã được xóa sạch. Hãy clear localStorage ở trình duyệt để bắt đầu lại.' });
} catch (error) {
res.status(500).json({ message: error.message });
}
});
module.exports = router;