Chỉnh sửa và tối ưu form đăng nhập và đăng ký
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
const mongoose = require('mongoose');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const connectDB = require('../config/db');
|
||||
const Asset = require('../models/Asset');
|
||||
const Scene = require('../models/Scene');
|
||||
|
||||
/**
|
||||
* Script kiểm tra tính toàn vẹn dữ liệu
|
||||
* So sánh sự đồng bộ giữa Database (MongoDB) và Filesystem (thư mục uploads)
|
||||
*/
|
||||
const checkIntegrity = async () => {
|
||||
try {
|
||||
console.log('=== KHỞI CHẠY KIỂM TRA TÍNH TOÀN VẸN HỆ THỐNG ===');
|
||||
await connectDB();
|
||||
|
||||
const assets = await Asset.find().lean();
|
||||
const scenes = await Scene.find().lean();
|
||||
const uploadDir = path.resolve(__dirname, '../uploads');
|
||||
const tempDir = path.resolve(uploadDir, 'temp');
|
||||
|
||||
const dbFilePaths = new Set();
|
||||
const brokenAssets = [];
|
||||
const assetIdsInDB = new Set(assets.map(a => a._id.toString()));
|
||||
|
||||
console.log(`\n1. Kiểm tra Database -> Filesystem (${assets.length} Assets):`);
|
||||
for (const asset of assets) {
|
||||
if (asset.filePath) {
|
||||
const absolutePath = path.resolve(asset.filePath);
|
||||
dbFilePaths.add(absolutePath);
|
||||
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
brokenAssets.push({
|
||||
id: asset._id,
|
||||
path: asset.filePath,
|
||||
user: asset.uploadedBy
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (brokenAssets.length > 0) {
|
||||
console.error(` [!] CẢNH BÁO: Tìm thấy ${brokenAssets.length} bản ghi mất file vật lý:`);
|
||||
brokenAssets.forEach(ba => console.error(` - Asset ID: ${ba.id} | Path: ${ba.path}`));
|
||||
} else {
|
||||
console.log(' [✓] Tất cả bản ghi Asset đều có file vật lý tương ứng.');
|
||||
}
|
||||
|
||||
console.log(`\n2. Kiểm tra Filesystem -> Database:`);
|
||||
let strayFilesCount = 0;
|
||||
if (fs.existsSync(uploadDir)) {
|
||||
const files = fs.readdirSync(uploadDir);
|
||||
files.forEach(file => {
|
||||
const fullPath = path.join(uploadDir, file);
|
||||
// Bỏ qua thư mục temp và các thư mục con khác
|
||||
if (fs.lstatSync(fullPath).isFile() && !file.startsWith('.')) {
|
||||
if (!dbFilePaths.has(path.resolve(fullPath))) {
|
||||
console.warn(` [?] File không có bản ghi DB: ${file}`);
|
||||
strayFilesCount++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
console.log(` [i] Tìm thấy ${strayFilesCount} file mồ côi (không được quản lý bởi Asset).`);
|
||||
|
||||
console.log(`\n3. Kiểm tra Liên kết Scene -> Asset (${scenes.length} Scenes):`);
|
||||
let brokenScenesCount = 0;
|
||||
for (const scene of scenes) {
|
||||
const assetId = scene.assetId?.toString();
|
||||
if (assetId && !assetIdsInDB.has(assetId)) {
|
||||
console.error(` [!] Scene "${scene.name || scene.title}" (ID: ${scene._id}) trỏ tới Asset ID không tồn tại: ${assetId}`);
|
||||
brokenScenesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (brokenScenesCount === 0) {
|
||||
console.log(' [✓] Tất cả các Scene đều liên kết với Asset hợp lệ.');
|
||||
} else {
|
||||
console.error(` [!] Tìm thấy ${brokenScenesCount} Scene bị hỏng liên kết.`);
|
||||
}
|
||||
|
||||
console.log('\n=== TỔNG KẾT ===');
|
||||
console.log(`- Bản ghi Asset lỗi: ${brokenAssets.length}`);
|
||||
console.log(`- File mồ côi: ${strayFilesCount}`);
|
||||
console.log(`- Scene hỏng liên kết: ${brokenScenesCount}`);
|
||||
console.log('================================================');
|
||||
|
||||
mongoose.connection.close();
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
console.error('Lỗi nghiêm trọng khi kiểm tra:', err.message);
|
||||
if (mongoose.connection) mongoose.connection.close();
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
checkIntegrity();
|
||||
@@ -0,0 +1,91 @@
|
||||
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();
|
||||
@@ -0,0 +1,36 @@
|
||||
const mongoose = require('mongoose');
|
||||
const connectDB = require('../config/db');
|
||||
const User = require('../models/User');
|
||||
|
||||
const promote = async () => {
|
||||
const username = process.argv[2]; // Lấy username từ câu lệnh: node promoteAdmin.js <username>
|
||||
|
||||
if (!username) {
|
||||
console.error('Lỗi: Vui lòng cung cấp username. Ví dụ: node promoteAdmin.js locpham');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
await connectDB();
|
||||
|
||||
const user = await User.findOneAndUpdate(
|
||||
{ username: username },
|
||||
{ $set: { role: 'admin' } },
|
||||
{ new: true }
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
console.error(`Không tìm thấy người dùng có tên: ${username}`);
|
||||
} else {
|
||||
console.log(`--- THÀNH CÔNG ---`);
|
||||
console.log(`Người dùng ${user.username} đã được nâng cấp lên quyền: ${user.role}`);
|
||||
}
|
||||
|
||||
mongoose.connection.close();
|
||||
} catch (err) {
|
||||
console.error('Lỗi:', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
promote();
|
||||
@@ -0,0 +1,86 @@
|
||||
const mongoose = require('mongoose');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const connectDB = require('../config/db');
|
||||
const User = require('../models/User');
|
||||
const Asset = require('../models/Asset');
|
||||
|
||||
/**
|
||||
* Hàm định dạng dung lượng file
|
||||
*/
|
||||
function formatBytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Script thống kê dung lượng lưu trữ theo từng người dùng
|
||||
*/
|
||||
const getStorageStats = async () => {
|
||||
try {
|
||||
console.log('=== ĐANG TRUY XUẤT THỐNG KÊ DUNG LƯỢNG LƯU TRỮ ===');
|
||||
await connectDB();
|
||||
|
||||
// 1. Lấy tất cả user để map tên
|
||||
const users = await User.find().select('username email').lean();
|
||||
const userMap = {};
|
||||
const stats = {};
|
||||
|
||||
users.forEach(u => {
|
||||
userMap[u._id.toString()] = u.username;
|
||||
stats[u._id.toString()] = {
|
||||
username: u.username,
|
||||
email: u.email,
|
||||
fileCount: 0,
|
||||
totalBytes: 0
|
||||
};
|
||||
});
|
||||
|
||||
// Thêm mục cho các asset không có chủ sở hữu (nếu có)
|
||||
stats['unknown'] = { username: 'Không xác định', email: 'N/A', fileCount: 0, totalBytes: 0 };
|
||||
|
||||
// 2. Lấy tất cả Asset
|
||||
const assets = await Asset.find().lean();
|
||||
console.log(`- Đang kiểm tra ${assets.length} tệp tin trong hệ thống...`);
|
||||
|
||||
for (const asset of assets) {
|
||||
const userId = asset.uploadedBy ? asset.uploadedBy.toString() : 'unknown';
|
||||
|
||||
if (asset.filePath && fs.existsSync(asset.filePath)) {
|
||||
const fileStat = fs.statSync(asset.filePath);
|
||||
if (stats[userId]) {
|
||||
stats[userId].totalBytes += fileStat.size;
|
||||
stats[userId].fileCount += 1;
|
||||
} else {
|
||||
stats['unknown'].totalBytes += fileStat.size;
|
||||
stats['unknown'].fileCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Hiển thị kết quả
|
||||
console.log('\nKết quả thống kê:');
|
||||
console.log(''.padEnd(60, '-'));
|
||||
console.log(`${'Username'.padEnd(20)} | ${'Số file'.padEnd(10)} | ${'Dung lượng'}`);
|
||||
console.log(''.padEnd(60, '-'));
|
||||
|
||||
Object.values(stats).forEach(userStat => {
|
||||
if (userStat.fileCount > 0) {
|
||||
console.log(`${userStat.username.padEnd(20)} | ${userStat.fileCount.toString().padEnd(10)} | ${formatBytes(userStat.totalBytes)}`);
|
||||
}
|
||||
});
|
||||
console.log(''.padEnd(60, '-'));
|
||||
|
||||
mongoose.connection.close();
|
||||
} catch (err) {
|
||||
console.error('Lỗi khi thống kê:', err.message);
|
||||
if (mongoose.connection) mongoose.connection.close();
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
getStorageStats();
|
||||
Reference in New Issue
Block a user