Files
hope-events/backend/scripts/seed-users.js
T
joshuaandClaude Opus 5.5 f2be3a8b45 Upgrade to Prisma 7 and clear npm audit advisories
- Prisma 5.22 -> 7.10 using the @prisma/adapter-pg driver adapter; the
  datasource URL moves to backend/prisma.config.js. All clients are built
  in src/config/db.js, and index.js reuses the shared client.
- Backend: patch follow-redirects, drop nodemon for `node --watch`, and use
  js-yaml 4 for the Jest coverage loader. npm audit reports 0.
- Frontend: override postcss-selector-parser to 7.1.7 (Tailwind 3 output
  is unchanged). braces has no patched release and stays, build/lint only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 09:58:11 +02:00

135 lines
4.7 KiB
JavaScript

/**
* Seed script: inserts 75 demo users (mix of roles, ~25 inactive)
* Run: node scripts/seed-users.js
*/
require('dotenv').config({ path: require('path').resolve(__dirname, '../.env') });
const { createPrismaClient } = require('../src/config/db');
const bcrypt = require('bcryptjs');
const prisma = createPrismaClient();
const firstNames = [
'Liam', 'Olivia', 'Noah', 'Emma', 'James', 'Ava', 'Elijah', 'Sophia',
'Lucas', 'Isabella', 'Mason', 'Mia', 'Logan', 'Charlotte', 'Ethan',
'Amelia', 'Aiden', 'Harper', 'Jackson', 'Evelyn', 'Thabo', 'Zanele',
'Sipho', 'Nomsa', 'Lebo', 'Thandeka', 'Kagiso', 'Palesa', 'Bongani',
'Ayanda', 'Pieter', 'Anri', 'Francois', 'Elizma', 'Hendrik', 'Marius',
'Chloé', 'Dylan', 'Amber', 'Caleb', 'Zoe', 'Hunter', 'Lily', 'Owen',
'Grace', 'Ryan', 'Chloe', 'Nathan', 'Stella', 'Connor', 'Hazel',
'Miles', 'Violet', 'Eli', 'Aurora', 'Isaiah', 'Luna', 'Nolan',
'Scarlett', 'Wyatt', 'Penelope', 'Sebastian', 'Layla', 'Asher',
'Riley', 'Oliver', 'Zoey', 'Ezra', 'Nora', 'Micah', 'Hannah',
'Jonah', 'Leah', 'Aaron', 'Aubrey', 'Charles',
];
const lastNames = [
'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller',
'Davis', 'Wilson', 'Moore', 'Taylor', 'Anderson', 'Thomas', 'Jackson',
'White', 'Harris', 'Martin', 'Thompson', 'Robinson', 'Lewis',
'Dlamini', 'Nkosi', 'Mokoena', 'Ndlovu', 'Mthembu', 'Zulu', 'Nxumalo',
'du Plessis', 'van der Berg', 'Botha', 'Steyn', 'Fourie', 'Pretorius',
'Venter', 'Coetzee', 'van Niekerk', 'Lombard', 'Janse van Rensburg',
'Walker', 'Hall', 'Allen', 'Young', 'Hernandez', 'King', 'Wright',
'Scott', 'Green', 'Baker', 'Adams', 'Nelson', 'Carter', 'Mitchell',
'Perez', 'Roberts', 'Turner', 'Phillips', 'Campbell', 'Parker', 'Evans',
'Edwards', 'Collins', 'Stewart', 'Morris', 'Reed', 'Cook', 'Morgan',
];
function pick(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
function randomPhone() {
// SA mobile format: 07x/08x xxxxxxx
const prefixes = ['071', '072', '073', '074', '076', '078', '079', '081', '082', '083', '084'];
return `${pick(prefixes)}${String(Math.floor(Math.random() * 9000000) + 1000000)}`;
}
async function main() {
const passwordHash = await bcrypt.hash('Demo@1234', 10);
// Role distribution: ~55 user, ~15 staff, ~5 supervisor
const rolePool = [
...Array(55).fill('user'),
...Array(15).fill('staff'),
...Array(5).fill('supervisor'),
];
const notifPool = ['email', 'email', 'email', 'whatsapp', 'both'];
const usedEmails = new Set();
const usedPhones = new Set();
const users = [];
// Build 75 user records
while (users.length < 75) {
const first = pick(firstNames);
const last = pick(lastNames);
const tag = Math.floor(Math.random() * 999) + 1;
const email = `${first.toLowerCase().replace(/ /g, '')}.${last.toLowerCase().replace(/ /g, '').replace(/'/g, '')}${tag}@demo.church`;
if (usedEmails.has(email)) continue;
usedEmails.add(email);
// ~60% of users have a phone number
let phone = null;
if (Math.random() < 0.6) {
let attempt = randomPhone();
let tries = 0;
while (usedPhones.has(attempt) && tries < 20) {
attempt = randomPhone();
tries++;
}
if (!usedPhones.has(attempt)) {
usedPhones.add(attempt);
phone = attempt;
}
}
users.push({
name: `${first} ${last}`,
email,
password: passwordHash,
role: pick(rolePool),
isActive: true, // will override 25 below
phoneNumber: phone,
notificationPreference: pick(notifPool),
});
}
// Mark exactly 25 as inactive (spread across the list)
const inactiveIndexes = new Set();
while (inactiveIndexes.size < 25) {
inactiveIndexes.add(Math.floor(Math.random() * 75));
}
inactiveIndexes.forEach(i => { users[i].isActive = false; });
console.log(`Inserting ${users.length} users…`);
console.log(` Active: ${users.filter(u => u.isActive).length}`);
console.log(` Inactive: ${users.filter(u => !u.isActive).length}`);
console.log(` Roles: user=${users.filter(u => u.role === 'user').length}, staff=${users.filter(u => u.role === 'staff').length}, supervisor=${users.filter(u => u.role === 'supervisor').length}`);
let created = 0;
let skipped = 0;
for (const user of users) {
try {
await prisma.user.create({ data: user });
created++;
} catch (err) {
if (err.code === 'P2002') {
skipped++;
} else {
throw err;
}
}
}
console.log(`Done. Created: ${created}, Skipped (duplicate): ${skipped}`);
console.log('Default password for all demo users: Demo@1234');
}
main()
.catch(err => { console.error(err); process.exit(1); })
.finally(() => prisma.$disconnect());