190 lines
5.1 KiB
TypeScript
190 lines
5.1 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
import { readFileSync } from 'node:fs';
|
|
import { resolve, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import argon2 from 'argon2';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
interface RegionRow {
|
|
code: string;
|
|
name_ko: string;
|
|
full_name_ko: string;
|
|
region_type: string;
|
|
parent_code: string;
|
|
depth: string;
|
|
path_code: string;
|
|
sort_order: string;
|
|
is_active: string;
|
|
is_beta_enabled: string;
|
|
latitude: string;
|
|
longitude: string;
|
|
}
|
|
|
|
interface IndustryRow {
|
|
code: string;
|
|
name_ko: string;
|
|
parent_code: string;
|
|
depth: string;
|
|
sort_order: string;
|
|
is_leaf: string;
|
|
is_active: string;
|
|
is_beta_enabled: string;
|
|
}
|
|
|
|
function parseCsv(filePath: string): Record<string, string>[] {
|
|
const content = readFileSync(filePath, 'utf-8');
|
|
const lines = content.trim().split('\n');
|
|
const headers = lines[0]!.split(',');
|
|
|
|
return lines.slice(1).map((line) => {
|
|
const values = line.split(',');
|
|
const row: Record<string, string> = {};
|
|
for (let i = 0; i < headers.length; i++) {
|
|
row[headers[i]!] = values[i] ?? '';
|
|
}
|
|
return row;
|
|
});
|
|
}
|
|
|
|
async function seedRegions(): Promise<void> {
|
|
const csvPath = resolve(__dirname, 'master-data/regions.v1.csv');
|
|
const rows = parseCsv(csvPath) as unknown as RegionRow[];
|
|
|
|
// code → id 매핑을 위한 맵
|
|
const codeToId = new Map<string, bigint>();
|
|
|
|
// depth 순으로 정렬하여 부모를 먼저 upsert
|
|
const sorted = [...rows].sort((a, b) => parseInt(a.depth) - parseInt(b.depth));
|
|
|
|
for (const row of sorted) {
|
|
const parentId = row.parent_code ? (codeToId.get(row.parent_code) ?? null) : null;
|
|
|
|
const result = await prisma.regionHierarchy.upsert({
|
|
where: { code: row.code },
|
|
update: {
|
|
nameKo: row.name_ko,
|
|
fullNameKo: row.full_name_ko || null,
|
|
regionType: row.region_type as never,
|
|
parentId,
|
|
depth: parseInt(row.depth),
|
|
pathCode: row.path_code,
|
|
sortOrder: parseInt(row.sort_order),
|
|
isActive: row.is_active === 'true',
|
|
isBetaEnabled: row.is_beta_enabled === 'true',
|
|
latitude: row.latitude ? parseFloat(row.latitude) : null,
|
|
longitude: row.longitude ? parseFloat(row.longitude) : null,
|
|
},
|
|
create: {
|
|
code: row.code,
|
|
nameKo: row.name_ko,
|
|
fullNameKo: row.full_name_ko || null,
|
|
regionType: row.region_type as never,
|
|
parentId,
|
|
depth: parseInt(row.depth),
|
|
pathCode: row.path_code,
|
|
sortOrder: parseInt(row.sort_order),
|
|
isActive: row.is_active === 'true',
|
|
isBetaEnabled: row.is_beta_enabled === 'true',
|
|
latitude: row.latitude ? parseFloat(row.latitude) : null,
|
|
longitude: row.longitude ? parseFloat(row.longitude) : null,
|
|
},
|
|
});
|
|
|
|
codeToId.set(row.code, result.id);
|
|
}
|
|
|
|
console.log(`Seeded ${sorted.length} regions`);
|
|
}
|
|
|
|
async function seedIndustries(): Promise<void> {
|
|
const csvPath = resolve(__dirname, 'master-data/industries.v1.csv');
|
|
const rows = parseCsv(csvPath) as unknown as IndustryRow[];
|
|
|
|
const codeToId = new Map<string, bigint>();
|
|
|
|
const sorted = [...rows].sort((a, b) => parseInt(a.depth) - parseInt(b.depth));
|
|
|
|
for (const row of sorted) {
|
|
const parentId = row.parent_code ? (codeToId.get(row.parent_code) ?? null) : null;
|
|
|
|
const result = await prisma.industryTaxonomy.upsert({
|
|
where: { code: row.code },
|
|
update: {
|
|
nameKo: row.name_ko,
|
|
parentId,
|
|
depth: parseInt(row.depth),
|
|
sortOrder: parseInt(row.sort_order),
|
|
isLeaf: row.is_leaf === 'true',
|
|
isActive: row.is_active === 'true',
|
|
isBetaEnabled: row.is_beta_enabled === 'true',
|
|
},
|
|
create: {
|
|
code: row.code,
|
|
nameKo: row.name_ko,
|
|
parentId,
|
|
depth: parseInt(row.depth),
|
|
sortOrder: parseInt(row.sort_order),
|
|
isLeaf: row.is_leaf === 'true',
|
|
isActive: row.is_active === 'true',
|
|
isBetaEnabled: row.is_beta_enabled === 'true',
|
|
},
|
|
});
|
|
|
|
codeToId.set(row.code, result.id);
|
|
}
|
|
|
|
console.log(`Seeded ${sorted.length} industries`);
|
|
}
|
|
|
|
async function seedAdminUser(): Promise<void> {
|
|
const email = 'admin@admin.com';
|
|
const emailNormalized = email.toLowerCase().trim();
|
|
|
|
const existing = await prisma.user.findFirst({
|
|
where: { emailNormalized },
|
|
});
|
|
|
|
if (existing) {
|
|
console.log(`Admin user already exists (id: ${existing.id})`);
|
|
return;
|
|
}
|
|
|
|
const passwordHash = await argon2.hash('admin123');
|
|
|
|
const user = await prisma.user.create({
|
|
data: {
|
|
email,
|
|
emailNormalized,
|
|
name: '운영자',
|
|
passwordHash,
|
|
primaryRole: 'SUPER_ADMIN',
|
|
status: 'ACTIVE',
|
|
emailVerifiedAt: new Date(),
|
|
},
|
|
});
|
|
|
|
console.log(`Admin user created (id: ${user.id}, email: ${email})`);
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
console.log('Starting seed...');
|
|
|
|
await seedRegions();
|
|
await seedIndustries();
|
|
await seedAdminUser();
|
|
|
|
console.log('Seed completed successfully');
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error('Seed failed:', e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|