Files
eene_dashboard/backend/scripts/import-hr-data.ts
EENE Dashboard 6066b5682d fix: production save errors and import HR dashboard data
Resolve invalid task creator IDs, fix API routing and file uploads on Vercel, and replace dummy seed data with HR_Dashboard import.
2026-06-05 22:08:56 +09:00

143 lines
4.5 KiB
TypeScript

import 'dotenv/config';
import { PrismaClient } from '@prisma/client';
import { mapAllHrProjects } from '../prisma/mapHrProjects';
const prisma = new PrismaClient();
const API_BASE = process.env.TARGET_API_URL?.replace(/\/$/, '');
async function importViaPrisma(adminId: string, memberId: string) {
const tasks = mapAllHrProjects();
await prisma.file.deleteMany({});
await prisma.taskDetail.deleteMany({});
await prisma.milestone.deleteMany({});
await prisma.kpiMetric.deleteMany({});
await prisma.task.deleteMany({});
for (const t of tasks) {
const { milestones, detailContent, ...taskData } = t;
const task = await prisma.task.create({
data: {
...taskData,
creatorId: adminId,
assigneeId: memberId,
},
});
for (const [order, ms] of milestones.entries()) {
await prisma.milestone.create({
data: { ...ms, taskId: task.id, order },
});
}
if (detailContent) {
await prisma.taskDetail.create({
data: {
taskId: task.id,
content: detailContent,
updatedBy: adminId,
},
});
}
}
console.log(`✅ Prisma import complete: ${tasks.length} tasks`);
}
async function importViaApi(adminId: string, memberId: string) {
const base = API_BASE!;
const tasks = mapAllHrProjects();
const existingRes = await fetch(`${base}/api/tasks`);
if (!existingRes.ok) throw new Error(`GET /api/tasks failed: ${existingRes.status}`);
const existing = (await existingRes.json()) as { id: string }[];
for (const task of existing) {
const del = await fetch(`${base}/api/tasks/${task.id}`, { method: 'DELETE' });
if (!del.ok && del.status !== 204) throw new Error(`DELETE ${task.id} failed: ${del.status}`);
}
for (const t of tasks) {
const { milestones, detailContent, ...taskData } = t;
const body = {
...taskData,
startDate: taskData.startDate?.toISOString() ?? null,
dueDate: taskData.dueDate?.toISOString() ?? null,
creatorId: adminId,
assigneeId: memberId,
};
const createRes = await fetch(`${base}/api/tasks`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (!createRes.ok) {
const err = await createRes.text();
throw new Error(`POST task "${t.title}" failed: ${createRes.status} ${err}`);
}
const created = (await createRes.json()) as { id: string };
for (const [order, ms] of milestones.entries()) {
const msRes = await fetch(`${base}/api/milestones/${created.id}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...ms, order }),
});
if (!msRes.ok) console.warn(` ⚠ milestone skip: ${t.title} / ${ms.title}`);
}
if (detailContent) {
const detailRes = await fetch(`${base}/api/details/${created.id}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: detailContent, authorName: '관리자' }),
});
if (!detailRes.ok) console.warn(` ⚠ detail skip: ${t.title}`);
}
}
console.log(`✅ API import complete: ${tasks.length} tasks → ${base}`);
}
async function main() {
const tasks = mapAllHrProjects();
console.log(`📦 HR_Dashboard → ${tasks.length} tasks mapped`);
let adminId: string;
let memberId: string;
if (API_BASE) {
const res = await fetch(`${API_BASE}/api/tasks`);
const existing = res.ok ? ((await res.json()) as { creatorId: string; assigneeId: string | null }[]) : [];
if (existing.length > 0) {
adminId = existing[0].creatorId;
memberId = existing[0].assigneeId ?? existing[0].creatorId;
} else {
throw new Error('API에 기존 task가 없어 creatorId를 확인할 수 없습니다.');
}
await importViaApi(adminId, memberId);
} else {
const admin = await prisma.user.upsert({
where: { email: 'admin@eene.com' },
update: {},
create: { email: 'admin@eene.com', password: '!', name: '관리자', role: 'ADMIN', department: 'EENE' },
});
const member = await prisma.user.upsert({
where: { email: 'member@eene.com' },
update: { name: '정성호' },
create: { email: 'member@eene.com', password: '!', name: '정성호', role: 'MEMBER', department: 'EENE' },
});
adminId = admin.id;
memberId = member.id;
await importViaPrisma(adminId, memberId);
}
}
main()
.catch((err) => {
console.error('❌ Import failed:', err);
process.exit(1);
})
.finally(() => prisma.$disconnect());