Files
gameno-api/components/professors/professorMigration.js
T

140 lines
5.4 KiB
JavaScript

// /components/professors/professorMigration.js
'use strict';
const mongoose = require('mongoose');
const Professor = require('./professorModel');
const User = require('../users/userModel');
const Role = require('../roles/roleModel');
const logger = require('../../utils/logger');
const { allocatePlaceholderNationalId } = require('../../utils/nationalId');
const { generateUsername, generateSimplePassword } = require('../../utils/credentials');
const bcrypt = require('bcryptjs');
const ensureProfessorRole = async () => {
let role = await Role.findOne({ name: 'Professor' });
if (!role) {
role = await Role.create({
name: 'Professor',
description: 'استاد و مدرس دوره‌ها',
permissions: ['professors:read', 'classes:read', 'sessions:read', 'attendances:read'],
isSystem: true
});
}
return role;
};
/**
* Automatically migrate and link all legacy Professor records to the Users table.
* Ensures every Professor document has a valid, populated `user` reference.
*/
const migrateAndLinkProfessorsToUsers = async () => {
try {
const professorRole = await ensureProfessorRole();
const professors = await Professor.find({}).setOptions({ _skipUserPopulate: true });
if (!professors.length) {
logger.info('Professor migration: No professor records found.');
return { total: 0, migrated: 0, alreadyLinked: 0 };
}
let migratedCount = 0;
let alreadyLinkedCount = 0;
for (const prof of professors) {
const rawDoc = prof._doc || prof;
let linkedUser = null;
// 1. Check if professor already has a valid user reference
if (prof.user && mongoose.Types.ObjectId.isValid(prof.user)) {
linkedUser = await User.findById(prof.user);
}
if (linkedUser) {
// Ensure user role is Professor if not an admin
if (!linkedUser.role || (String(linkedUser.role) !== String(professorRole._id) && linkedUser.role?.name !== 'SuperAdmin' && linkedUser.role?.name !== 'Admin')) {
linkedUser.role = professorRole._id;
await linkedUser.save();
}
alreadyLinkedCount++;
continue;
}
// 2. Professor is not linked to a user. Find matching user by phone or national ID
const rawPhone = String(rawDoc.phoneNumber || rawDoc.phone || '').trim();
const rawNationalId = String(rawDoc.nationalIdCode || rawDoc.nationalId || '').trim();
const rawName = String(rawDoc.name || '').trim();
const rawSurname = String(rawDoc.surname || '').trim();
const fullName = [rawName, rawSurname].filter(Boolean).join(' ').trim() || 'استاد بدون نام';
if (rawPhone) {
linkedUser = await User.findOne({ phoneNumber: rawPhone });
}
if (!linkedUser && rawNationalId) {
linkedUser = await User.findOne({ nationalIdCode: rawNationalId });
}
// 3. If matching user found, link and update role
if (linkedUser) {
if (!linkedUser.role || (String(linkedUser.role) !== String(professorRole._id) && linkedUser.role?.name !== 'SuperAdmin' && linkedUser.role?.name !== 'Admin')) {
linkedUser.role = professorRole._id;
}
if (!linkedUser.name && fullName) {
linkedUser.name = fullName;
}
if (!linkedUser.cardNumber && rawDoc.cardNumber) {
linkedUser.cardNumber = String(rawDoc.cardNumber).trim();
}
if (!linkedUser.shabaNumber && (rawDoc.shabaNumber || rawDoc.iban)) {
linkedUser.shabaNumber = String(rawDoc.shabaNumber || rawDoc.iban).trim();
}
await linkedUser.save();
prof.user = linkedUser._id;
await prof.save();
migratedCount++;
logger.info(`Professor migration: Linked professor "${fullName}" (${prof._id}) to existing user (${linkedUser._id}).`);
continue;
}
// 4. If no matching user found, create a new User for this professor
let nationalIdCode = rawNationalId;
if (!nationalIdCode) {
nationalIdCode = await allocatePlaceholderNationalId(rawPhone || fullName);
}
const generatedUsername = await generateUsername(fullName);
const plainPassword = generateSimplePassword();
const passwordHash = await bcrypt.hash(plainPassword, 10);
const newUser = await User.create({
name: fullName,
nationalIdCode,
phoneNumber: rawPhone || undefined,
email: rawDoc.email ? String(rawDoc.email).trim().toLowerCase() : undefined,
role: professorRole._id,
username: generatedUsername,
passwordHash,
cardNumber: rawDoc.cardNumber ? String(rawDoc.cardNumber).trim() : undefined,
shabaNumber: (rawDoc.shabaNumber || rawDoc.iban) ? String(rawDoc.shabaNumber || rawDoc.iban).trim() : undefined,
isActive: prof.isActive !== false
});
prof.user = newUser._id;
await prof.save();
migratedCount++;
logger.info(`Professor migration: Created new user (${newUser._id}) for legacy professor "${fullName}" (${prof._id}).`);
}
logger.info(`Professor migration complete: ${professors.length} total, ${migratedCount} newly linked/created, ${alreadyLinkedCount} already linked.`);
return { total: professors.length, migrated: migratedCount, alreadyLinked: alreadyLinkedCount };
} catch (error) {
logger.error('Error during migrateAndLinkProfessorsToUsers:', error);
return { error: error.message };
}
};
module.exports = {
migrateAndLinkProfessorsToUsers,
ensureProfessorRole
};