Files
3dtours/backend/scripts/cleanupOrphanedData.js

91 lines
4.1 KiB
JavaScript

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) {
try {
await fs.promises.unlink(asset.filePath);
filesDeleted++;
} catch (e) {
if (e.code !== 'ENOENT') 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();