seed-mock-data.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #!/usr/bin/env node
  2. /**
  3. * Seed script โ€” injects mock data into the VidReview database.
  4. * Run via Docker so Prisma client binary matches the environment.
  5. *
  6. * Usage:
  7. * npm run seed # via package.json script
  8. * docker compose run --rm api npx tsx scripts/seed-mock-data.ts
  9. *
  10. * Env vars (optional, defaults work for local docker):
  11. * DATABASE_URL=postgresql://vidreview:vidreview123@localhost:5432/vidreview
  12. */
  13. import { PrismaClient } from '@prisma/client';
  14. import bcrypt from 'bcryptjs';
  15. const prisma = new PrismaClient();
  16. const BCRYPT_ROUNDS = 10;
  17. const PASSWORD = 'demo1234';
  18. async function cleanAll() {
  19. console.log('\n๐Ÿงน Cleaning existing data...');
  20. await prisma.comment.deleteMany();
  21. await prisma.asset.deleteMany();
  22. await prisma.invitation.deleteMany();
  23. await prisma.projectMember.deleteMany();
  24. await prisma.project.deleteMany();
  25. await prisma.user.deleteMany();
  26. console.log(' All tables cleared.');
  27. }
  28. async function createUser(email: string, name: string, globalRole: 'ADMIN' | 'MEMBER', projectRole?: 'ADMIN' | 'EDITOR' | 'REVIEWER' | 'VIEWER') {
  29. const password = await bcrypt.hash(PASSWORD, BCRYPT_ROUNDS);
  30. return prisma.user.create({
  31. data: { email, name, password, globalRole, storageQuota: 524288000, storageUsed: 0 },
  32. select: { id: true, email: true, name: true, globalRole: true },
  33. });
  34. }
  35. async function createProject(name: string, ownerId: string, editorId: string, reviewerId: string) {
  36. const slug = name.toLowerCase().replace(/\s+/g, '-');
  37. // Create project
  38. const project = await prisma.project.create({
  39. data: {
  40. name,
  41. ownerId,
  42. },
  43. });
  44. // Add owner as ADMIN of project
  45. await prisma.projectMember.create({
  46. data: { userId: ownerId, projectId: project.id, role: 'ADMIN', invitedBy: ownerId },
  47. });
  48. // Add editor
  49. await prisma.projectMember.create({
  50. data: { userId: editorId, projectId: project.id, role: 'EDITOR', invitedBy: ownerId },
  51. });
  52. // Add reviewer
  53. await prisma.projectMember.create({
  54. data: { userId: reviewerId, projectId: project.id, role: 'REVIEWER', invitedBy: ownerId },
  55. });
  56. return project;
  57. }
  58. async function main() {
  59. console.log('\nโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”');
  60. console.log(' VidReview โ€” Mock Data Seeder');
  61. console.log('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n');
  62. await cleanAll();
  63. // โ”€โ”€ 1. Admin โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  64. const admin = await createUser('admin@vidreview.local', 'Admin', 'ADMIN');
  65. console.log(`โœ… Admin created`);
  66. // โ”€โ”€ 2. Members โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  67. const memberDefs = [
  68. { email: 'alice@vidreview.local', name: 'Alice Johnson' },
  69. { email: 'bob@vidreview.local', name: 'Bob Smith' },
  70. { email: 'carol@vidreview.local', name: 'Carol White' },
  71. ];
  72. const members = await Promise.all(
  73. memberDefs.map(m => createUser(m.email, m.name, 'MEMBER'))
  74. );
  75. console.log(`โœ… ${members.length} members created`);
  76. // โ”€โ”€ 3. Projects per member โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  77. const projectDefs = [
  78. // Alice owns 3 projects
  79. { name: 'Brand Campaign Q2', ownerIdx: 0, editorIdx: 1, reviewerIdx: 2 },
  80. { name: 'Product Launch Video', ownerIdx: 0, editorIdx: 2, reviewerIdx: 1 },
  81. { name: 'Internal Training Clips', ownerIdx: 0, editorIdx: 1, reviewerIdx: 2 },
  82. // Bob owns 2 projects
  83. { name: 'Customer Testimonials', ownerIdx: 1, editorIdx: 0, reviewerIdx: 2 },
  84. { name: 'Event Highlights Reel', ownerIdx: 1, editorIdx: 2, reviewerIdx: 0 },
  85. // Carol owns 3 projects
  86. { name: 'Social Media Shorts', ownerIdx: 2, editorIdx: 0, reviewerIdx: 1 },
  87. { name: 'How-To Tutorial Series', ownerIdx: 2, editorIdx: 1, reviewerIdx: 0 },
  88. { name: 'Partner Collaboration', ownerIdx: 2, editorIdx: 0, reviewerIdx: 1 },
  89. ];
  90. const projects = await Promise.all(
  91. projectDefs.map(p =>
  92. createProject(
  93. p.name,
  94. members[p.ownerIdx].id,
  95. members[p.editorIdx].id,
  96. members[p.reviewerIdx].id,
  97. )
  98. )
  99. );
  100. console.log(`โœ… ${projects.length} projects created\n`);
  101. // โ”€โ”€ Output credentials table โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
  102. const divider = 'โ”€'.repeat(72);
  103. console.log(divider);
  104. console.log(' ๐Ÿ“‹ Login Credentials');
  105. console.log(divider);
  106. console.log(` ${'Type'.padEnd(10)} ${'Name'.padEnd(22)} ${'Email'.padEnd(35)} Password`);
  107. console.log(divider);
  108. console.log(` ${'ADMIN'.padEnd(10)} ${admin.name.padEnd(22)} ${admin.email.padEnd(35)} ${PASSWORD}`);
  109. for (const m of members) {
  110. console.log(` ${'MEMBER'.padEnd(10)} ${m.name.padEnd(22)} ${m.email.padEnd(35)} ${PASSWORD}`);
  111. }
  112. console.log(divider);
  113. console.log(`\n All users have storage quota: 500 MB`);
  114. console.log(` Password for all accounts: ${PASSWORD}`);
  115. console.log('');
  116. console.log(' Project membership per user:');
  117. console.log(divider);
  118. for (const m of members) {
  119. const memberProjects = projects.filter((_, i) => projectDefs[i].ownerIdx === members.indexOf(m));
  120. const roleMap: Record<string, string> = {};
  121. for (const proj of memberProjects) {
  122. const def = projectDefs.find(p => p.name === proj.name)!;
  123. const role = members[projectDefs.findIndex(p => p.name === proj.name)].id === m.id
  124. ? 'owner โ†’ project ADMIN'
  125. : `project ${projectDefs.findIndex(p => p.name === proj.name) < 3 ? ['EDITOR', 'REVIEWER', 'EDITOR'][projectDefs.findIndex(p => p.name === proj.name) % 3] : ['REVIEWER', 'EDITOR', 'REVIEWER'][projectDefs.findIndex(p => p.name === proj.name) % 3]}`;
  126. }
  127. const memberships = await prisma.projectMember.findMany({
  128. where: { userId: m.id },
  129. include: { project: true },
  130. });
  131. const roles = await prisma.projectMember.findMany({ where: { userId: m.id } });
  132. const lines = memberships.map((pm, i) => {
  133. const def = projectDefs.find(p => p.name === pm.project.name)!;
  134. const role = pm.userId === members[def.ownerIdx].id ? 'ADMIN (owner)' : roles[i].role;
  135. return ` โ€ข ${pm.project.name.padEnd(30)} [${role}]`;
  136. });
  137. console.log(` ${m.name} (${m.email}):`);
  138. lines.forEach(l => console.log(l));
  139. }
  140. console.log(divider);
  141. console.log('\nโœ… Done!\n');
  142. await prisma.$disconnect();
  143. }
  144. main().catch(err => {
  145. console.error('\nโŒ Seed failed:', err);
  146. process.exit(1);
  147. });