const mongoose = require('mongoose'); const fs = require('fs'); const path = require('path'); const connectDB = require('../config/db'); const User = require('../models/User'); const Scene = require('../models/Scene'); const Asset = require('../models/Asset'); const Hotspot = require('../models/Hotspot'); /** * Script dọn dẹp dữ liệu rác (orphaned data) * Xóa các Scene, Asset và File vật lý của những người dùng đã bị xóa khỏi hệ thống. * Đồng thời dọn dẹp các Asset "mồ côi" không còn được gắn vào bất kỳ Scene nào. */ const cleanup = async () => { try { console.log('--- Bắt đầu quy trình dọn dẹp dữ liệu rác ---'); await connectDB(); // 1. Lấy danh sách tất cả ID người dùng hiện đang tồn tại const validUserIds = await User.distinct('_id'); console.log(`- Tìm thấy ${validUserIds.length} người dùng hợp lệ.`); // 2. Tìm các Scene mồ côi (Người tạo không còn tồn tại) const orphanedScenes = await Scene.find({ createdBy: { $nin: validUserIds } }); const orphanedSceneIds = orphanedScenes.map(s => s._id); console.log(`- Tìm thấy ${orphanedSceneIds.length} cảnh (Scene) mồ côi.`); // 3. Xóa các Hotspot liên quan đến các Scene mồ côi // Xóa cả hotspot xuất phát từ và trỏ đến các scene bị xóa const hotspotResult = await Hotspot.deleteMany({ $or: [ { parent_scene_id: { $in: orphanedSceneIds } }, { target_scene_id: { $in: orphanedSceneIds } } ] }); console.log(`- Đã xóa ${hotspotResult.deletedCount} liên kết Hotspot liên quan.`); // 4. Xóa các Scene mồ côi trong DB const sceneResult = await Scene.deleteMany({ _id: { $in: orphanedSceneIds } }); console.log(`- Đã xóa ${sceneResult.deletedCount} bản ghi Scene trong Database.`); // 5. Tìm các Asset mồ côi (Người upload không tồn tại HOẶC không có Scene nào liên kết) // Chúng ta lấy danh sách assetId đang được sử dụng bởi các Scene còn lại const usedAssetIds = await Scene.distinct('assetId'); // Để an toàn, chỉ xóa các Asset không liên kết nếu chúng đã tồn tại hơn 2 giờ // (Tránh xóa nhầm ảnh đang trong hàng đợi xử lý của Worker) const safeDate = new Date(Date.now() - 2 * 3600 * 1000); const orphanedAssets = await Asset.find({ $or: [ { uploadedBy: { $nin: validUserIds } }, // User đã bị xóa { $and: [ { _id: { $nin: usedAssetIds } }, // Không có scene nào trỏ tới { createdAt: { $lt: safeDate } } // Đã quá 2 giờ ] } ] }); console.log(`- Tìm thấy ${orphanedAssets.length} ảnh (Asset) mồ côi hoặc không liên kết.`); // 6. Xóa tệp tin vật lý và bản ghi Asset let filesDeleted = 0; for (const asset of orphanedAssets) { if (asset.filePath && fs.existsSync(asset.filePath)) { try { fs.unlinkSync(asset.filePath); filesDeleted++; } catch (e) { console.error(` [Lỗi] Không thể xóa file: ${asset.filePath}`); } } await Asset.findByIdAndDelete(asset._id); } console.log(`- Đã dọn dẹp ${filesDeleted} tệp tin vật lý.`); console.log(`- Đã xóa ${orphanedAssets.length} bản ghi Asset trong Database.`); console.log('--- Hoàn tất dọn dẹp! Hệ thống đã sạch sẽ. ---'); mongoose.connection.close(); process.exit(0); } catch (err) { console.error('Lỗi nghiêm trọng khi dọn dẹp:', err.message); if (mongoose.connection) mongoose.connection.close(); process.exit(1); } }; cleanup();