163 lines
5 KiB
JavaScript
163 lines
5 KiB
JavaScript
/**
|
|
* Migration Script: Profile Fields Restructure
|
|
*
|
|
* This script migrates member data from the old schema to the new schema:
|
|
* - Removes `skills` field
|
|
* - Converts `offering` from String to { text: String, tags: [String] }
|
|
* - Converts `lookingFor` from String to { text: String, tags: [String] }
|
|
* - Converts `peerSupport.topics` to `peerSupport.skillTopics` and `peerSupport.supportTopics`
|
|
* - Removes `privacy.skills`
|
|
*/
|
|
|
|
import mongoose from 'mongoose';
|
|
import Member from '../server/models/member.js';
|
|
import { connectDB } from '../server/utils/mongoose.js';
|
|
|
|
// Curated list of conversational support topics
|
|
const CONVERSATIONAL_TOPICS = [
|
|
'Co-founder relationships',
|
|
'Burnout prevention',
|
|
'Impostor syndrome',
|
|
'Work-life boundaries',
|
|
'Conflict resolution',
|
|
'General chat & support',
|
|
];
|
|
|
|
async function migrateProfileFields() {
|
|
try {
|
|
await connectDB();
|
|
console.log('Connected to database');
|
|
|
|
// Find all members
|
|
const members = await Member.find({});
|
|
console.log(`Found ${members.length} members to migrate`);
|
|
|
|
let migratedCount = 0;
|
|
let skippedCount = 0;
|
|
|
|
for (const member of members) {
|
|
let needsUpdate = false;
|
|
const updates = {};
|
|
|
|
// Migrate skills -> offering.tags (if offering doesn't have tags yet)
|
|
if (member.skills && member.skills.length > 0) {
|
|
console.log(`\nMember ${member.name} (${member.email}):`);
|
|
console.log(` - Has skills: ${member.skills.join(', ')}`);
|
|
|
|
// If offering is still a string, convert it and add skills as tags
|
|
if (typeof member.offering === 'string') {
|
|
updates['offering'] = {
|
|
text: member.offering || '',
|
|
tags: member.skills, // Move skills to offering tags
|
|
};
|
|
console.log(` - Migrating skills to offering.tags`);
|
|
needsUpdate = true;
|
|
}
|
|
|
|
// Remove skills field
|
|
updates.$unset = { skills: 1 };
|
|
needsUpdate = true;
|
|
}
|
|
|
|
// Migrate offering from string to object (if not already done)
|
|
if (typeof member.offering === 'string' && !updates['offering']) {
|
|
updates['offering'] = {
|
|
text: member.offering || '',
|
|
tags: [],
|
|
};
|
|
console.log(` - Converting offering to object structure`);
|
|
needsUpdate = true;
|
|
}
|
|
|
|
// Migrate lookingFor from string to object
|
|
if (typeof member.lookingFor === 'string') {
|
|
updates['lookingFor'] = {
|
|
text: member.lookingFor || '',
|
|
tags: [],
|
|
};
|
|
console.log(` - Converting lookingFor to object structure`);
|
|
needsUpdate = true;
|
|
}
|
|
|
|
// Migrate peer support topics
|
|
if (member.peerSupport?.topics && member.peerSupport.topics.length > 0) {
|
|
const skillTopics = [];
|
|
const supportTopics = [];
|
|
|
|
// Split topics into skill-based and conversational
|
|
for (const topic of member.peerSupport.topics) {
|
|
if (CONVERSATIONAL_TOPICS.includes(topic)) {
|
|
supportTopics.push(topic);
|
|
} else {
|
|
skillTopics.push(topic);
|
|
}
|
|
}
|
|
|
|
updates['peerSupport.skillTopics'] = skillTopics;
|
|
updates['peerSupport.supportTopics'] = supportTopics;
|
|
updates['$unset'] = {
|
|
...(updates['$unset'] || {}),
|
|
'peerSupport.topics': 1
|
|
};
|
|
|
|
console.log(` - Splitting peer support topics:`);
|
|
console.log(` Skill topics: ${skillTopics.join(', ') || 'none'}`);
|
|
console.log(` Support topics: ${supportTopics.join(', ') || 'none'}`);
|
|
needsUpdate = true;
|
|
}
|
|
|
|
// Remove privacy.skills if it exists
|
|
if (member.privacy?.skills) {
|
|
updates['$unset'] = {
|
|
...(updates['$unset'] || {}),
|
|
'privacy.skills': 1
|
|
};
|
|
needsUpdate = true;
|
|
}
|
|
|
|
// Apply updates
|
|
if (needsUpdate) {
|
|
const updateOps = { ...updates };
|
|
const unsetOps = updateOps.$unset;
|
|
delete updateOps.$unset;
|
|
|
|
const finalUpdate = {};
|
|
if (Object.keys(updateOps).length > 0) {
|
|
finalUpdate.$set = updateOps;
|
|
}
|
|
if (unsetOps && Object.keys(unsetOps).length > 0) {
|
|
finalUpdate.$unset = unsetOps;
|
|
}
|
|
|
|
await Member.updateOne({ _id: member._id }, finalUpdate);
|
|
console.log(` ✓ Updated`);
|
|
migratedCount++;
|
|
} else {
|
|
skippedCount++;
|
|
}
|
|
}
|
|
|
|
console.log('\n=== Migration Complete ===');
|
|
console.log(`Total members: ${members.length}`);
|
|
console.log(`Migrated: ${migratedCount}`);
|
|
console.log(`Skipped (already migrated): ${skippedCount}`);
|
|
|
|
} catch (error) {
|
|
console.error('Migration error:', error);
|
|
throw error;
|
|
} finally {
|
|
await mongoose.connection.close();
|
|
console.log('\nDatabase connection closed');
|
|
}
|
|
}
|
|
|
|
// Run migration
|
|
migrateProfileFields()
|
|
.then(() => {
|
|
console.log('\n✓ Migration script completed successfully');
|
|
process.exit(0);
|
|
})
|
|
.catch((error) => {
|
|
console.error('\n✗ Migration script failed:', error);
|
|
process.exit(1);
|
|
});
|