import { PrismaClient, UserRole, UserStatus, AuthProvider, FieldType } from '@prisma/client'; import { PrismaPg } from '@prisma/adapter-pg'; import { Pool } from 'pg'; import * as argon2 from 'argon2'; async function main() { const connectionString = process.env.DATABASE_URL; const pool = new Pool({ connectionString }); const adapter = new PrismaPg(pool); const prisma = new PrismaClient({ adapter }); console.log('🌱 Starting database seeding...\n'); // ============================================= // Seed Agent Types // ============================================= console.log('šŸ“‹ Seeding Agent Types...'); const agentTypes = [ { name: 'Professional', description: 'Licensed real estate professionals', icon: 'briefcase', sortOrder: 1, }, { name: 'Lender', description: 'Mortgage and loan specialists', icon: 'bank', sortOrder: 2, }, ]; for (const agentType of agentTypes) { const existing = await prisma.agentType.findUnique({ where: { name: agentType.name }, }); if (existing) { console.log(` āš ļø Agent type "${agentType.name}" already exists`); } else { await prisma.agentType.create({ data: agentType, }); console.log(` āœ… Created agent type: ${agentType.name}`); } } console.log(''); // ============================================= // Seed Default Profile Sections & Fields // ============================================= console.log('šŸ“‹ Seeding Default Profile Sections...'); // Define default system sections with their fields const defaultSections = [ { name: 'Basic Information', slug: 'basic-information', description: 'Basic profile information', icon: 'user', sortOrder: 1, isActive: true, isGlobal: true, isSystem: true, fields: [ { name: 'First Name', slug: 'first_name', fieldType: FieldType.TEXT, description: 'Your first name', placeholder: 'Enter your first name', sortOrder: 1, isActive: true, isRequired: true, isSearchableOnly: false, }, { name: 'Last Name', slug: 'last_name', fieldType: FieldType.TEXT, description: 'Your last name', placeholder: 'Enter your last name', sortOrder: 2, isActive: true, isRequired: true, isSearchableOnly: false, }, ], }, { name: 'Contact Information', slug: 'contact-information', description: 'Contact details', icon: 'phone', sortOrder: 2, isActive: true, isGlobal: true, isSystem: true, fields: [ { name: 'Email Address', slug: 'email_address', fieldType: FieldType.TEXT, description: 'Your contact email address', placeholder: 'Enter your email address', sortOrder: 1, isActive: true, isRequired: true, isSearchableOnly: false, }, { name: 'Phone Number', slug: 'phone_number', fieldType: FieldType.TEXT, description: 'Your contact phone number', placeholder: 'Enter your phone number', sortOrder: 2, isActive: true, isRequired: false, isSearchableOnly: false, }, ], }, ]; for (const sectionData of defaultSections) { const { fields, ...sectionInfo } = sectionData; // Check if section already exists const existingSection = await prisma.profileSection.findUnique({ where: { slug: sectionInfo.slug }, }); if (existingSection) { console.log(` āš ļø Section "${sectionInfo.name}" already exists`); // Still check and create missing fields for (const fieldData of fields) { const existingField = await prisma.profileField.findFirst({ where: { sectionId: existingSection.id, slug: fieldData.slug, }, }); if (!existingField) { await prisma.profileField.create({ data: { ...fieldData, sectionId: existingSection.id, }, }); console.log(` āœ… Created field: ${fieldData.name}`); } } } else { // Create section with fields const section = await prisma.profileSection.create({ data: { ...sectionInfo, fields: { create: fields, }, }, }); console.log(` āœ… Created section: ${sectionInfo.name} with ${fields.length} fields`); } } console.log(''); // ============================================= // Seed Admin User // ============================================= console.log('šŸ‘¤ Seeding Admin User...'); const adminEmail = process.env.ADMIN_EMAIL || 'admin@realestate.com'; const adminPassword = process.env.ADMIN_PASSWORD || 'Admin@123456'; // Hash password with Argon2 (more secure than bcrypt) const hashedPassword = await argon2.hash(adminPassword); // Check if admin already exists const existingAdmin = await prisma.user.findUnique({ where: { email: adminEmail }, }); if (existingAdmin) { console.log(` āš ļø Admin user already exists: ${adminEmail}`); } else { // Create admin user const admin = await prisma.user.create({ data: { email: adminEmail, password: hashedPassword, role: UserRole.ADMIN, status: UserStatus.ACTIVE, emailVerified: true, emailVerifiedAt: new Date(), authProvider: AuthProvider.LOCAL, }, }); console.log(' āœ… Admin user created successfully!'); console.log(' ───────────────────────────────'); console.log(` šŸ“§ Email: ${adminEmail}`); console.log(` šŸ”‘ Password: ${adminPassword}`); console.log(` šŸ‘¤ Role: ${admin.role}`); console.log(` šŸ†” ID: ${admin.id}`); console.log(' ───────────────────────────────\n'); } // ============================================= // Summary // ============================================= const userCount = await prisma.user.count(); const adminCount = await prisma.user.count({ where: { role: UserRole.ADMIN } }); const agentTypeCount = await prisma.agentType.count(); const sectionCount = await prisma.profileSection.count(); const fieldCount = await prisma.profileField.count(); const systemSectionCount = await prisma.profileSection.count({ where: { isSystem: true } }); console.log('šŸ“Š Database Summary:'); console.log(` Total Users: ${userCount}`); console.log(` Admins: ${adminCount}`); console.log(` Agent Types: ${agentTypeCount}`); console.log(` Profile Sections: ${sectionCount} (${systemSectionCount} system)`); console.log(` Profile Fields: ${fieldCount}`); console.log('\n🌱 Seeding completed!\n'); await prisma.$disconnect(); await pool.end(); } main() .catch((e) => { console.error('āŒ Seeding failed:', e); process.exit(1); });