const { PrismaClient } = require("@prisma/client"); const prisma = new PrismaClient(); async function upsertCategories() { // 카테고리 트리 (projectmemo 기준 상위 그룹) const categories = [ { name: "암실소문", slug: "main", sortOrder: 1, status: "active" }, { name: "명예의 전당", slug: "hall-of-fame", sortOrder: 2, status: "active" }, { name: "주변 제휴업체", slug: "nearby-partners", sortOrder: 3, status: "active" }, { name: "제휴업소 정보", slug: "partner-info", sortOrder: 4, status: "active" }, { name: "방문후기", slug: "reviews", sortOrder: 5, status: "active" }, { name: "소통방", slug: "community", sortOrder: 6, status: "active" }, { name: "광고/제휴", slug: "ads-affiliates", sortOrder: 7, status: "active" }, ]; const map = {}; for (const c of categories) { const created = await prisma.boardCategory.upsert({ where: { slug: c.slug }, update: { name: c.name, sortOrder: c.sortOrder, status: c.status }, create: c, }); map[c.slug] = created; } return map; // { slug: category } } async function upsertRoles() { const roles = [ { name: "admin", description: "관리자" }, { name: "editor", description: "운영진" }, { name: "user", description: "일반 사용자" } ]; for (const r of roles) { await prisma.role.upsert({ where: { name: r.name }, update: { description: r.description }, create: r, }); } // 기본 권한 매핑 const roleMap = { admin: [ ["ADMIN", "ADMINISTER"], ["BOARD", "MODERATE"], ["POST", "CREATE"], ["POST", "UPDATE"], ["POST", "DELETE"], ["COMMENT", "DELETE"], ["USER", "UPDATE"], ], editor: [ ["BOARD", "MODERATE"], ["POST", "UPDATE"], ["POST", "DELETE"], ["COMMENT", "DELETE"], ], user: [ ["POST", "CREATE"], ["COMMENT", "CREATE"], ["POST", "READ"], ["COMMENT", "READ"], ], }; for (const [roleName, perms] of Object.entries(roleMap)) { const role = await prisma.role.findUnique({ where: { name: roleName } }); if (!role) continue; for (const [resource, action] of perms) { await prisma.rolePermission.upsert({ where: { roleId_resource_action: { roleId: role.roleId, resource, action, }, }, update: { allowed: true }, create: { roleId: role.roleId, resource, action, allowed: true }, }); } } } async function upsertAdmin() { const admin = await prisma.user.upsert({ where: { nickname: "admin" }, update: {}, create: { nickname: "admin", name: "Administrator", birth: new Date("1990-01-01"), phone: "010-0000-0001", agreementTermsAt: new Date(), authLevel: "ADMIN", }, }); const adminRole = await prisma.role.findUnique({ where: { name: "admin" } }); if (adminRole) { await prisma.userRole.upsert({ where: { userId_roleId: { userId: admin.userId, roleId: adminRole.roleId }, }, update: {}, create: { userId: admin.userId, roleId: adminRole.roleId }, }); } return admin; } async function upsertBoards(admin, categoryMap) { const boards = [ // 일반 { name: "공지사항", slug: "notice", description: "공지", type: "general", sortOrder: 1, writeLevel: "moderator" }, { name: "가입인사", slug: "greetings", description: "가입인사", type: "general", sortOrder: 2 }, { name: "버그건의", slug: "bug-report", description: "버그/건의", type: "general", sortOrder: 3 }, { name: "이벤트", slug: "event", description: "이벤트", type: "general", sortOrder: 4, requiredTags: { required: ["이벤트"] } }, { name: "자유게시판", slug: "free", description: "자유", type: "general", sortOrder: 5 }, { name: "무엇이든", slug: "qna", description: "무엇이든 물어보세요", type: "general", sortOrder: 6 }, { name: "마사지꿀팁", slug: "tips", description: "팁", type: "general", sortOrder: 7 }, { name: "익명게시판", slug: "anonymous", description: "익명", type: "general", sortOrder: 8, allowAnonymousPost: true, allowSecretComment: true }, { name: "관리사찾아요", slug: "find-therapist", description: "구인/구직", type: "general", sortOrder: 9 }, { name: "청와대", slug: "blue-house", description: "레벨 제한", type: "general", sortOrder: 10, readLevel: "member" }, { name: "방문후기", slug: "reviews", description: "운영자 승인 후 공개", type: "general", sortOrder: 11, requiresApproval: true, requiredTags: { anyOf: ["업체명", "지역"] } }, // 특수 { name: "출석부", slug: "attendance", description: "데일리 체크인", type: "special", sortOrder: 12 }, { name: "주변 제휴업체", slug: "nearby-partners", description: "위치 기반", type: "special", sortOrder: 13 }, { name: "회원랭킹", slug: "ranking", description: "랭킹", type: "special", sortOrder: 14 }, { name: "무료쿠폰", slug: "free-coupons", description: "쿠폰", type: "special", sortOrder: 15 }, { name: "월간집계", slug: "monthly-stats", description: "월간 통계", type: "special", sortOrder: 16 }, // 제휴업소 일반 { name: "제휴업소", slug: "partners-photos", description: "사진 전용 게시판", type: "general", sortOrder: 17, requiredFields: { imageOnly: true, minImages: 1, maxImages: 10 } }, // 광고/제휴 { name: "제휴문의", slug: "partner-contact", description: "제휴문의", type: "general", sortOrder: 18, requiredFields: { imageOnly: true, minImages: 1, maxImages: 10 } }, { name: "제휴업소 요청", slug: "partner-req", description: "제휴업소 요청", type: "general", sortOrder: 19, requiredFields: { imageOnly: true, minImages: 1, maxImages: 10 } }, ]; const created = []; for (const b of boards) { // 카테고리 매핑 규칙 (트리 기준 상위 카테고리) const mapBySlug = { // 암실소문 (메인) notice: "main", greetings: "main", "bug-report": "main", event: "main", attendance: "main", // 명예의 전당 ranking: "hall-of-fame", "free-coupons": "hall-of-fame", "monthly-stats": "hall-of-fame", // 주변 제휴업체 "nearby-partners": "nearby-partners", // 제휴업소 정보 "partners-photos": "partner-info", // 방문후기 reviews: "reviews", // 소통방(기본값 community로 처리) free: "community", qna: "community", tips: "community", anonymous: "community", "find-therapist": "community", "blue-house": "community", // 광고/제휴 "partner-contact": "ads-affiliates", "partner-req": "ads-affiliates", }; const categorySlug = mapBySlug[b.slug] || "community"; const category = categoryMap[categorySlug]; const board = await prisma.board.upsert({ where: { slug: b.slug }, update: { description: b.description, sortOrder: b.sortOrder, type: b.type, requiresApproval: !!b.requiresApproval, allowAnonymousPost: !!b.allowAnonymousPost, readLevel: b.readLevel || undefined, categoryId: category ? category.id : undefined, }, create: { name: b.name, slug: b.slug, description: b.description, sortOrder: b.sortOrder, type: b.type, requiresApproval: !!b.requiresApproval, allowAnonymousPost: !!b.allowAnonymousPost, readLevel: b.readLevel || undefined, categoryId: category ? category.id : undefined, }, }); created.push(board); // 공지/운영 보드는 관리자 모더레이터 지정 if (["notice", "bug-report"].includes(board.slug)) { await prisma.boardModerator.upsert({ where: { boardId_userId: { boardId: board.id, userId: admin.userId }, }, update: {}, create: { boardId: board.id, userId: admin.userId, role: "MANAGER" }, }); } } return created; } async function upsertViewTypes() { const viewTypes = [ // main scope { key: "main_default", name: "기본", scope: "main" }, { key: "main_text", name: "텍스트", scope: "main" }, { key: "main_preview", name: "미리보기", scope: "main" }, { key: "main_special_rank", name: "특수랭킹", scope: "main" }, // list scope { key: "list_default", name: "기본", scope: "list" }, { key: "list_text", name: "텍스트", scope: "list" }, { key: "list_preview", name: "미리보기", scope: "list" }, { key: "list_special_rank", name: "특수랭킹", scope: "list" }, ]; for (const vt of viewTypes) { await prisma.boardViewType.upsert({ where: { key: vt.key }, update: { name: vt.name, scope: vt.scope }, create: vt, }); } } async function seedPolicies() { // 금칙어 예시 const banned = [ { pattern: "광고", appliesTo: "POST", severity: 1 }, { pattern: "욕설", appliesTo: "COMMENT", severity: 2 }, { pattern: "스팸", appliesTo: "POST", severity: 2 }, ]; for (const k of banned) { await prisma.bannedKeyword.upsert({ where: { pattern: k.pattern }, update: { appliesTo: k.appliesTo, severity: k.severity, active: true }, create: k, }); } // 레벨 임계 예시 const levels = [ { level: 1, minPoints: 0 }, { level: 2, minPoints: 100 }, { level: 3, minPoints: 300 }, ]; for (const l of levels) { await prisma.levelThreshold.upsert({ where: { level: l.level }, update: { minPoints: l.minPoints }, create: l, }); } } async function main() { await upsertRoles(); const admin = await upsertAdmin(); const categoryMap = await upsertCategories(); await upsertViewTypes(); const boards = await upsertBoards(admin, categoryMap); // 샘플 글 하나 const free = boards.find((b) => b.slug === "free") || boards[0]; const post = await prisma.post.create({ data: { boardId: free.id, authorId: admin.userId, title: "첫 글", content: "메시지 앱 초기 설정 완료", status: "published", }, }); await prisma.comment.createMany({ data: [ { postId: post.id, authorId: admin.userId, content: "환영합니다!" }, { postId: post.id, authorId: admin.userId, content: "댓글 테스트" }, ], }); await seedPolicies(); // 제휴업체 예시 데이터 const partners = [ { name: "힐링마사지", category: "spa", latitude: 37.5665, longitude: 126.9780, address: "서울 중구" }, { name: "웰빙테라피", category: "spa", latitude: 37.5700, longitude: 126.9769, address: "서울 종로구" }, { name: "굿짐", category: "gym", latitude: 37.5600, longitude: 126.9820, address: "서울 중구" }, ]; for (const p of partners) { await prisma.partner.upsert({ where: { name: p.name }, update: p, create: p }); } } main() .catch((e) => { console.error(e); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });