210 lines
9.3 KiB
JavaScript
210 lines
9.3 KiB
JavaScript
|
||
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;
|
||
}
|
||
}
|