ghostguild-org/server/migrations/import-babyghosts-preregistrations.js
Jennie Robinson Faber 501be10bfe feat: pre-registrant management and invitation system
Admin interface to review, filter, and batch-invite the 95 pre-registrants
from Baby Ghosts. Accept-invitation page pre-fills their data and collects
circle, pronouns, motivation, contribution tier, and agreement before
creating their member record.
2026-04-06 14:46:11 +01:00

122 lines
3.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Migration: copy pre-registrations from babyghosts → ghostguild
//
// Usage (from ghostguild-org root):
// BG_MONGODB_URI="<babyghosts uri>" node server/migrations/import-babyghosts-preregistrations.js
//
// The ghostguild URI is read from NUXT_MONGODB_URI or MONGODB_URI in your .env.
// Set DRY_RUN=1 to preview without writing anything.
import { MongoClient } from "mongodb";
import { config } from "dotenv";
import { resolve, dirname } from "path";
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));
config({ path: resolve(__dirname, "../../.env") });
const BG_URI = process.env.BG_MONGODB_URI;
const GG_URI = process.env.NUXT_MONGODB_URI || process.env.MONGODB_URI;
const DRY_RUN = process.env.DRY_RUN === "1";
if (!BG_URI) {
console.error("ERROR: BG_MONGODB_URI is not set.");
process.exit(1);
}
if (!GG_URI) {
console.error(
"ERROR: No ghostguild MongoDB URI found (NUXT_MONGODB_URI or MONGODB_URI).",
);
process.exit(1);
}
async function run() {
console.log("=== Importing babyghosts pre-registrations into ghostguild ===");
if (DRY_RUN) console.log("DRY RUN nothing will be written.\n");
const bgClient = new MongoClient(BG_URI);
const ggClient = new MongoClient(GG_URI);
await bgClient.connect();
await ggClient.connect();
try {
const source = bgClient.db("babyghosts").collection("guild_waitlist");
const dest = ggClient.db().collection("preregistrations");
const records = await source.find({}).toArray();
console.log(
`Found ${records.length} record(s) in babyghosts.guild_waitlist.\n`,
);
if (records.length === 0) {
console.log("Nothing to import.");
return;
}
let inserted = 0;
let skipped = 0;
let errors = 0;
for (const doc of records) {
const email = doc.email?.trim().toLowerCase();
if (!email) {
console.warn(` SKIP record ${doc._id} has no email.`);
skipped++;
continue;
}
if (DRY_RUN) {
console.log(` [dry-run] ${email}`);
inserted++;
continue;
}
try {
const result = await dest.updateOne(
{ email },
{ $setOnInsert: doc },
{ upsert: true },
);
if (result.upsertedCount > 0) {
console.log(` INSERT ${email}`);
inserted++;
} else {
console.log(` EXISTS ${email} (skipped)`);
skipped++;
}
} catch (err) {
console.error(` ERROR ${email}: ${err.message}`);
errors++;
}
}
// Normalize: ensure all docs have status field for Mongoose model compatibility
if (!DRY_RUN) {
const normalized = await dest.updateMany(
{ status: { $exists: false } },
{ $set: { status: "pending" } },
);
if (normalized.modifiedCount > 0) {
console.log(
`\nNormalized ${normalized.modifiedCount} record(s) with missing status field.`,
);
}
}
console.log("\n=== Summary ===");
console.log(` Inserted : ${inserted}`);
console.log(` Skipped : ${skipped}`);
console.log(` Errors : ${errors}`);
} finally {
await bgClient.close();
await ggClient.close();
console.log("\nDone.");
}
}
run().catch((err) => {
console.error("Unexpected error:", err);
process.exit(1);
});