From a21d0bd0e25c2c87e275c6251dd6cc6c34829dde Mon Sep 17 00:00:00 2001 From: mark8-1 Date: Fri, 27 Mar 2026 19:53:17 +0200 Subject: [PATCH] feat: add backend seed script for local development --- backend/README.md | 22 +++ backend/package.json | 1 + backend/scripts/seed.ts | 324 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 backend/scripts/seed.ts diff --git a/backend/README.md b/backend/README.md index 1fdfe763..5ee6aab9 100644 --- a/backend/README.md +++ b/backend/README.md @@ -44,6 +44,28 @@ $ npm run start:dev $ npm run start:prod ``` +## Database Seeding + +To populate the database with mock data for development and testing: + +```bash +# Seed the database with deterministic test data +$ npm run seed +``` + +The seed script creates: + +- 2 fan users (fan1@example.com, fan2@example.com) +- 2 creator users (creator1@example.com, creator2@example.com) +- 3 posts (2 public, 1 premium) +- 3 comments +- 3 likes +- 4 notifications + +All users have the password: `password123` + +The script is idempotent and can be run multiple times safely. + ## Run tests ```bash diff --git a/backend/package.json b/backend/package.json index 20014dba..ebf98308 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,6 +12,7 @@ "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", + "seed": "ts-node scripts/seed.ts", "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\" --fix", "test": "jest", "test:watch": "jest --watch", diff --git a/backend/scripts/seed.ts b/backend/scripts/seed.ts new file mode 100644 index 00000000..c486d1d3 --- /dev/null +++ b/backend/scripts/seed.ts @@ -0,0 +1,324 @@ +#!/usr/bin/env ts-node +import 'dotenv/config'; +/** + * Database seed script for development/testing. + * Inserts deterministic mock data for creators, fans, posts, comments, likes, and notifications. + * Idempotent — safe to run multiple times. + */ +import { DataSource } from 'typeorm'; +import { User, UserRole } from '../src/users/entities/user.entity'; +import { Creator } from '../src/creators/entities/creator.entity'; +import { Post } from '../src/posts/entities/post.entity'; +import { Comment } from '../src/comments/entities/comment.entity'; +import { Like } from '../src/likes/entities/like.entity'; +import { Notification, NotificationType } from '../src/notifications/entities/notification.entity'; +import * as bcrypt from 'bcrypt'; + +async function createDataSource(): Promise { + const host = process.env.DB_HOST ?? 'localhost'; + const port = parseInt(process.env.DB_PORT ?? '5432', 10); + const username = process.env.DB_USER ?? 'myfans'; + const password = process.env.DB_PASSWORD ?? 'password'; + const database = process.env.DB_NAME ?? 'myfans'; + + console.log('🌍 DB connection:', { host, port, username, database }); + + const dataSource = new DataSource({ + type: 'postgres', + host, + port, + username, + password, + database, + entities: [User, Creator, Post, Comment, Like, Notification], + synchronize: true, // create schema for local dev + logging: false, + }); + + await dataSource.initialize(); + return dataSource; +} + +async function seedDatabase() { + console.log('🌱 Starting database seed...'); + + const dataSource = await createDataSource(); + + try { + // Check if already seeded + const userRepo = dataSource.getRepository(User); + const existingUser = await userRepo.findOne({ where: { email: 'creator1@example.com' } }); + + if (existingUser) { + console.log('✅ Database already seeded. Skipping...'); + return; + } + + console.log('📝 Seeding users...'); + + // Hash password for all users + const hashedPassword = await bcrypt.hash('password123', 10); + + // Create fans + const fan1 = await userRepo.save({ + email: 'fan1@example.com', + username: 'fan1', + password_hash: hashedPassword, + display_name: 'John Fan', + role: UserRole.USER, + is_creator: false, + email_notifications: true, + push_notifications: false, + marketing_emails: false, + email_new_subscriber: true, + email_subscription_renewal: true, + email_new_comment: true, + email_new_like: false, + email_new_message: true, + email_payout: true, + push_new_subscriber: true, + push_subscription_renewal: true, + push_new_comment: true, + push_new_like: true, + push_new_message: true, + push_payout: false, + }); + + const fan2 = await userRepo.save({ + email: 'fan2@example.com', + username: 'fan2', + password_hash: hashedPassword, + display_name: 'Jane Fan', + role: UserRole.USER, + is_creator: false, + email_notifications: true, + push_notifications: true, + marketing_emails: true, + email_new_subscriber: true, + email_subscription_renewal: true, + email_new_comment: true, + email_new_like: true, + email_new_message: true, + email_payout: true, + push_new_subscriber: true, + push_subscription_renewal: true, + push_new_comment: true, + push_new_like: true, + push_new_message: true, + push_payout: false, + }); + + // Create creators + const creatorUser1 = await userRepo.save({ + email: 'creator1@example.com', + username: 'creator1', + password_hash: hashedPassword, + display_name: 'Alice Creator', + role: UserRole.USER, + is_creator: true, + email_notifications: true, + push_notifications: true, + marketing_emails: false, + email_new_subscriber: true, + email_subscription_renewal: true, + email_new_comment: true, + email_new_like: true, + email_new_message: true, + email_payout: true, + push_new_subscriber: true, + push_subscription_renewal: true, + push_new_comment: true, + push_new_like: true, + push_new_message: true, + push_payout: true, + }); + + const creatorUser2 = await userRepo.save({ + email: 'creator2@example.com', + username: 'creator2', + password_hash: hashedPassword, + display_name: 'Bob Creator', + role: UserRole.USER, + is_creator: true, + email_notifications: true, + push_notifications: false, + marketing_emails: true, + email_new_subscriber: true, + email_subscription_renewal: true, + email_new_comment: true, + email_new_like: false, + email_new_message: true, + email_payout: true, + push_new_subscriber: true, + push_subscription_renewal: true, + push_new_comment: true, + push_new_like: true, + push_new_message: true, + push_payout: true, + }); + + console.log('📝 Seeding creators...'); + + const creatorRepo = dataSource.getRepository(Creator); + + const creator1 = await creatorRepo.save({ + user_id: creatorUser1.id, + bio: 'I create amazing content about technology and programming!', + subscription_price: '10.0000000', + currency: 'XLM', + is_verified: true, + followers_count: 150, + }); + + const creator2 = await creatorRepo.save({ + user_id: creatorUser2.id, + bio: 'Art and creativity enthusiast sharing my journey.', + subscription_price: '5.0000000', + currency: 'XLM', + is_verified: false, + followers_count: 75, + }); + + console.log('📝 Seeding posts...'); + + const postRepo = dataSource.getRepository(Post); + + const post1 = await postRepo.save({ + title: 'My First Post', + content: 'This is my first post on the platform! Excited to share my journey with you all.', + authorId: creatorUser1.id, + isPublished: true, + isPremium: false, + likesCount: 0, + }); + + const post2 = await postRepo.save({ + title: 'Premium Content: Advanced Tutorial', + content: 'This premium tutorial covers advanced techniques that only subscribers can access.', + authorId: creatorUser1.id, + isPublished: true, + isPremium: true, + likesCount: 0, + }); + + const post3 = await postRepo.save({ + title: 'Creative Process Behind My Art', + content: 'Let me walk you through how I create my artwork from concept to completion.', + authorId: creatorUser2.id, + isPublished: true, + isPremium: false, + likesCount: 0, + }); + + console.log('📝 Seeding comments...'); + + const commentRepo = dataSource.getRepository(Comment); + + const comment1 = await commentRepo.save({ + content: 'Great post! Looking forward to more content.', + authorId: fan1.id, + postId: post1.id, + }); + + const comment2 = await commentRepo.save({ + content: 'This tutorial is exactly what I needed. Thank you!', + authorId: fan2.id, + postId: post2.id, + }); + + const comment3 = await commentRepo.save({ + content: 'Your art style is amazing. How long did this piece take?', + authorId: fan1.id, + postId: post3.id, + }); + + console.log('📝 Seeding likes...'); + + const likeRepo = dataSource.getRepository(Like); + + await likeRepo.save({ + userId: fan1.id, + postId: post1.id, + }); + + await likeRepo.save({ + userId: fan2.id, + postId: post1.id, + }); + + await likeRepo.save({ + userId: fan1.id, + postId: post3.id, + }); + + // Update likes count + await postRepo.update(post1.id, { likesCount: 2 }); + await postRepo.update(post3.id, { likesCount: 1 }); + + console.log('📝 Seeding notifications...'); + + const notificationRepo = dataSource.getRepository(Notification); + + await notificationRepo.save({ + user_id: creatorUser1.id, + type: NotificationType.NEW_COMMENT, + title: 'New Comment', + body: 'fan1 commented on your post "My First Post"', + is_read: false, + metadata: { postId: post1.id, commentId: comment1.id }, + }); + + await notificationRepo.save({ + user_id: creatorUser1.id, + type: NotificationType.NEW_LIKE, + title: 'New Like', + body: 'fan1 liked your post "My First Post"', + is_read: false, + metadata: { postId: post1.id }, + }); + + await notificationRepo.save({ + user_id: creatorUser1.id, + type: NotificationType.NEW_LIKE, + title: 'New Like', + body: 'fan2 liked your post "My First Post"', + is_read: false, + metadata: { postId: post1.id }, + }); + + await notificationRepo.save({ + user_id: creatorUser2.id, + type: NotificationType.NEW_COMMENT, + title: 'New Comment', + body: 'fan1 commented on your post "Creative Process Behind My Art"', + is_read: false, + metadata: { postId: post3.id, commentId: comment3.id }, + }); + + console.log('✅ Database seeded successfully!'); + console.log(''); + console.log('Created data:'); + console.log('- 2 fans (fan1@example.com, fan2@example.com)'); + console.log('- 2 creators (creator1@example.com, creator2@example.com)'); + console.log('- 3 posts (2 public, 1 premium)'); + console.log('- 3 comments'); + console.log('- 3 likes'); + console.log('- 4 notifications'); + console.log(''); + console.log('All users have password: password123'); + + } finally { + await dataSource.destroy(); + } +} + +async function main() { + try { + await seedDatabase(); + } catch (error) { + console.error('❌ Seeding failed:', error); + process.exit(1); + } +} + +main(); \ No newline at end of file