gohorsejobs/seeder-api/src/seeders/users.js

210 lines
9.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

import bcrypt from 'bcrypt';
import { pool } from '../db.js';
// Get pepper from environment - MUST match backend PASSWORD_PEPPER
const PASSWORD_PEPPER = process.env.PASSWORD_PEPPER || '';
export async function seedUsers() {
console.log('👤 Seeding users (Unified Architecture)...');
console.log(' SuperAdmin is created via backend migration (010_seed_super_admin.sql)');
try {
// Fetch companies to map users (now using companies table, not core_companies)
const companiesResult = await pool.query('SELECT id, name, slug FROM companies');
const companyMap = {}; // name -> id
companiesResult.rows.forEach(c => companyMap[c.name] = c.id);
// Get system tenant ID
const systemResult = await pool.query("SELECT id FROM companies WHERE slug = 'gohorse-system'");
const systemTenantId = systemResult.rows[0]?.id || null;
// NOTE: SuperAdmin is now created via migration 010_seed_super_admin.sql
// No longer created here to avoid PASSWORD_PEPPER mismatch issues
// 1. Create Company Admins
const admins = [
{ identifier: 'takeshi_yamamoto', fullName: 'Takeshi Yamamoto', company: 'TechCorp', email: 'takeshi@techcorp.com', pass: 'Takeshi@2025', roles: ['admin'] },
{ identifier: 'kenji', fullName: 'Kenji Tanaka', company: 'AppMakers', email: 'kenji@appmakers.mobile', pass: 'Takeshi@2025', roles: ['admin'] },
{ identifier: 'maria_santos', fullName: 'Maria Santos', company: 'DesignHub', email: 'maria@designhub.com', pass: 'User@2025', roles: ['recruiter'] },
{ identifier: 'wile_e_coyote', fullName: 'Wile E. Coyote', company: 'ACME Corporation', email: 'wile@acme.com', pass: 'MeepMeep@123', roles: ['admin'] }
];
for (const admin of admins) {
const hash = await bcrypt.hash(admin.pass + PASSWORD_PEPPER, 10);
const tenantId = companyMap[admin.company];
if (!tenantId) {
console.warn(` ⚠️ Company ${admin.company} not found for user ${admin.fullName}, skipping.`);
continue;
}
const result = await pool.query(`
INSERT INTO users (identifier, password_hash, role, full_name, email, name, tenant_id, status)
VALUES ($1, $2, $3, $4, $5, $6, $7, 'ACTIVE')
ON CONFLICT (identifier) DO UPDATE SET password_hash = EXCLUDED.password_hash
RETURNING id
`, [admin.identifier, hash, admin.roles[0], admin.fullName, admin.email, admin.fullName, tenantId]);
const userId = result.rows[0].id;
for (const role of admin.roles) {
await pool.query(`
INSERT INTO user_roles (user_id, role)
VALUES ($1, $2)
ON CONFLICT (user_id, role) DO NOTHING
`, [userId, role]);
}
console.log(` ✓ User created: ${admin.identifier}`);
}
// 3. Create Candidates (Job Seekers)
const candidates = [
{ identifier: 'paulo_santos', fullName: 'Paulo Santos', email: 'paulo@email.com', pass: 'User@2025' },
{ identifier: 'maria_email', fullName: 'Maria Reyes', email: 'maria@email.com', pass: 'User@2025' }
];
for (const cand of candidates) {
const hash = await bcrypt.hash(cand.pass + PASSWORD_PEPPER, 10);
const result = await pool.query(`
INSERT INTO users (identifier, password_hash, role, full_name, email, name, status)
VALUES ($1, $2, 'candidate', $3, $4, $5, 'ACTIVE')
ON CONFLICT (identifier) DO UPDATE SET password_hash = EXCLUDED.password_hash
RETURNING id
`, [cand.identifier, hash, cand.fullName, cand.email, cand.fullName]);
const userId = result.rows[0].id;
await pool.query(`
INSERT INTO user_roles (user_id, role)
VALUES ($1, 'candidate')
ON CONFLICT (user_id, role) DO NOTHING
`, [userId]);
console.log(` ✓ Candidate created: ${cand.identifier}`);
}
console.log('👤 Seeding legacy candidates...');
const legacyCandidates = [
{
identifier: 'ana_silva',
fullName: 'Ana Silva',
email: 'ana.silva@example.com',
phone: '+55 11 98765-4321',
city: 'São Paulo',
state: 'SP',
title: 'Full Stack Developer',
experience: '5 years of experience',
skills: ['React', 'Node.js', 'TypeScript', 'AWS', 'Docker'],
bio: 'Developer passionate about building innovative solutions.',
objective: 'Grow as a full stack developer building scalable products.'
},
{
identifier: 'carlos_santos',
fullName: 'Carlos Santos',
email: 'carlos.santos@example.com',
phone: '+55 11 91234-5678',
city: 'Rio de Janeiro',
state: 'RJ',
title: 'UX/UI Designer',
experience: '3 years of experience',
skills: ['Figma', 'Adobe XD', 'UI Design', 'Prototyping', 'Design Systems'],
bio: 'Designer focused on creating memorable experiences.',
objective: 'Design intuitive experiences for web and mobile products.'
},
{
identifier: 'maria_oliveira',
fullName: 'Maria Oliveira',
email: 'maria.oliveira@example.com',
phone: '+55 21 99876-5432',
city: 'Belo Horizonte',
state: 'MG',
title: 'Data Engineer',
experience: '7 years of experience',
skills: ['Python', 'SQL', 'Spark', 'Machine Learning', 'Data Visualization'],
bio: 'Data engineer with a strong background in machine learning.',
objective: 'Build robust data pipelines and analytics products.'
},
{
identifier: 'pedro_costa',
fullName: 'Pedro Costa',
email: 'pedro.costa@example.com',
phone: '+55 31 98765-1234',
city: 'Curitiba',
state: 'PR',
title: 'Product Manager',
experience: '6 years of experience',
skills: ['Product Management', 'Agile', 'Scrum', 'Data Analysis', 'User Research'],
bio: 'Product Manager with experience in digital products.',
objective: 'Lead cross-functional teams to deliver customer-centric products.'
},
{
identifier: 'juliana_ferreira',
fullName: 'Juliana Ferreira',
email: 'juliana.ferreira@example.com',
phone: '+55 41 91234-8765',
city: 'Porto Alegre',
state: 'RS',
title: 'DevOps Engineer',
experience: '4 years of experience',
skills: ['Docker', 'Kubernetes', 'AWS', 'Terraform', 'CI/CD'],
bio: 'DevOps engineer specialized in automation and cloud infrastructure.',
objective: 'Improve delivery pipelines and cloud reliability.'
}
];
for (const cand of legacyCandidates) {
const hash = await bcrypt.hash('User@2025' + PASSWORD_PEPPER, 10);
await pool.query(`
INSERT INTO users (
identifier, password_hash, role, full_name, email, name, phone,
city, state, title, experience, skills, objective, bio, status
) VALUES ($1, $2, 'candidate', $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, 'ACTIVE')
ON CONFLICT (identifier) DO UPDATE SET
full_name = EXCLUDED.full_name,
email = EXCLUDED.email,
phone = EXCLUDED.phone,
city = EXCLUDED.city,
state = EXCLUDED.state,
title = EXCLUDED.title,
experience = EXCLUDED.experience,
skills = EXCLUDED.skills,
objective = EXCLUDED.objective,
bio = EXCLUDED.bio
`, [
cand.identifier,
hash,
cand.fullName,
cand.email,
cand.fullName,
cand.phone,
cand.city,
cand.state,
cand.title,
cand.experience,
cand.skills,
cand.objective,
cand.bio
]);
console.log(` ✓ Legacy candidate created: ${cand.email}`);
// Fix: Insert role into user_roles
const result = await pool.query('SELECT id FROM users WHERE identifier = $1', [cand.identifier]);
if (result.rows[0]) {
const userId = result.rows[0].id;
await pool.query(`
INSERT INTO user_roles (user_id, role)
VALUES ($1, 'candidate')
ON CONFLICT (user_id, role) DO NOTHING
`, [userId]);
}
}
} catch (error) {
console.error(' ❌ Error seeding users:', error.message);
throw error;
}
}