Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions prisma/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,19 @@ Populate the database with mock data for development:
npm run seed
```

This will create:
- 2 test users
- 4 sample modules with different difficulties
- 3 module completions
- 2 credentials
- 3 transactions
This will create deterministic local-only fixtures:
- 6 test users (including admin, verified learner, unverified learner, disabled learner, instructor, employer)
- 8 sample modules with different difficulties
- Module completions and credentials for testing success/empty/pending states
- Transactions for rewards, referrals, and adjustments
- 1 webhook endpoint

> **Note**: All fixture identities are strictly local-only and idempotent. Running `npm run seed` multiple times will not duplicate records.
> To explicitly reset the database before seeding, use:
> ```bash
> npm run seed:reset
> ```

### 5. Verify Setup

Open Prisma Studio to view and manage your data:
Expand Down
2 changes: 2 additions & 0 deletions prisma/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './users'
export * from './modules'
75 changes: 75 additions & 0 deletions prisma/fixtures/modules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
export type SeedModule = {
id: string
title: string
description: string
category: string
difficulty: string
reward: number
}

export const seedModuleFixtures: SeedModule[] = [
{
id: 'seed-module-blockchain-101',
title: 'Stellar Fundamentals',
description: 'Core ledger concepts, accounts, trustlines, and transaction flow on Stellar.',
category: 'blockchain',
difficulty: 'beginner',
reward: 10,
},
{
id: 'seed-module-finance-101',
title: 'Understanding Stablecoins',
description: 'How fiat-backed and crypto-backed stablecoins work across global payment rails.',
category: 'finance',
difficulty: 'beginner',
reward: 12,
},
{
id: 'seed-module-security-201',
title: 'Wallet Security & Key Management',
description: 'Threat modeling, custody approaches, and secure key handling in production systems.',
category: 'security',
difficulty: 'intermediate',
reward: 18,
},
{
id: 'seed-module-development-301',
title: 'Build with Soroban',
description: 'Develop and test smart contracts using practical Soroban development workflows.',
category: 'development',
difficulty: 'advanced',
reward: 30,
},
{
id: 'seed-module-compliance-201',
title: 'AML/KYC for Digital Finance',
description: 'Compliance basics, sanctions screening, and regulated onboarding for fintech teams.',
category: 'compliance',
difficulty: 'intermediate',
reward: 20,
},
{
id: 'seed-module-identity-301',
title: 'Decentralized Identity in Practice',
description: 'Verifiable credentials, selective disclosure, and identity portability patterns.',
category: 'identity',
difficulty: 'advanced',
reward: 28,
},
{
id: 'seed-module-development-401',
title: 'Production API Hardening',
description: 'Rate limiting, auth patterns, observability, and safe rollout practices.',
category: 'development',
difficulty: 'expert',
reward: 40,
},
{
id: 'seed-module-blockchain-202',
title: 'Stellar Asset Issuance',
description: 'Issue and manage custom assets with issuer/distributor architecture.',
category: 'blockchain',
difficulty: 'intermediate',
reward: 22,
},
]
75 changes: 75 additions & 0 deletions prisma/fixtures/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Role } from '@prisma/client'

export type SeedUser = {
id: string
email: string
username: string
name: string
role: Role
status: string
isVerified: boolean
walletAddress: string | null
}

export const seedUserFixtures: SeedUser[] = [
{
id: 'seed-user-admin-ada',
email: 'ada.admin+seed@learnault.local',
username: 'ada_admin_seed',
name: 'Ada Admin',
role: 'ADMIN',
status: 'ACTIVE',
isVerified: true,
walletAddress: 'GD6QK3H5MYYXQUDMVDGLDZ4E2IWXQ7N5SLW7PZBLW4D5YV3V2NFH8A1A',
},
{
id: 'seed-user-instructor-deepak',
email: 'deepak.instructor+seed@learnault.local',
username: 'deepak_inst_seed',
name: 'Deepak Instructor',
role: 'INSTRUCTOR',
status: 'ACTIVE',
isVerified: true,
walletAddress: 'GDVXG4D7WQ3WWT3S3Q6L2J5KLC2TSGBYHYQ4M4U7QWEK3J75VYLPQ9U2',
},
{
id: 'seed-user-employer-acme',
email: 'acme.employer+seed@learnault.local',
username: 'acme_emp_seed',
name: 'Acme Talent Team',
role: 'LEARNER',
status: 'ACTIVE',
isVerified: true,
walletAddress: null,
},
{
id: 'seed-user-learner-alice',
email: 'alice.learner+seed@learnault.local',
username: 'alice_learner_seed',
name: 'Alice Learner',
role: 'LEARNER',
status: 'ACTIVE',
isVerified: true,
walletAddress: 'GBUQWP3BOUZX34ULNQG23RQ6F4BFSRJ4HBSNF63YLIXLQ5EDLF274Y6C',
},
{
id: 'seed-user-learner-bob',
email: 'bob.learner+seed@learnault.local',
username: 'bob_learner_seed',
name: 'Bob Learner',
role: 'LEARNER',
status: 'ACTIVE',
isVerified: false,
walletAddress: 'GB7YLLICSMNYWJ46NWYCBTGXD54FPPWFY3YQYBFJFTQ6B2N3WHAL5V4K',
},
{
id: 'seed-user-learner-carla',
email: 'carla.learner+seed@learnault.local',
username: 'carla_learner_seed',
name: 'Carla Learner',
role: 'LEARNER',
status: 'DEACTIVATED',
isVerified: true,
walletAddress: 'GA5N2IBQ2J5KVSBBR6K7D6JQ3Y7L46BP3I7WNIPXQ2XH2WQOTJXK5C2Z',
},
]
147 changes: 14 additions & 133 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,14 @@ import { PrismaPg } from '@prisma/adapter-pg'
import { Pool } from 'pg'
import { hash } from 'bcryptjs'

import { seedUserFixtures } from './fixtures/users'
import { seedModuleFixtures } from './fixtures/modules'
import type { SeedModule } from './fixtures/modules'

const pool = new Pool({ connectionString: process.env.DATABASE_URL })
const adapter = new PrismaPg(pool)
const prisma = new PrismaClient({ adapter })

type SeedUser = {
id: string
email: string
name: string
walletAddress: string | null
}

type SeedModule = {
id: string
title: string
description: string
category: string
difficulty: string
reward: number
}

const MS_PER_DAY = 24 * 60 * 60 * 1000
const DIFFICULTY_MULTIPLIER: Record<string, number> = {
beginner: 1,
Expand All @@ -32,118 +20,6 @@ const DIFFICULTY_MULTIPLIER: Record<string, number> = {
expert: 2.5,
}

const seedUserFixtures: SeedUser[] = [
{
id: 'seed-user-admin-ada',
email: 'ada.admin+seed@learnault.dev',
name: 'Ada Admin',
walletAddress: 'GD6QK3H5MYYXQUDMVDGLDZ4E2IWXQ7N5SLW7PZBLW4D5YV3V2NFH8A1A',
},
{
id: 'seed-user-learner-alice',
email: 'alice.learner+seed@learnault.dev',
name: 'Alice Learner',
walletAddress: 'GBUQWP3BOUZX34ULNQG23RQ6F4BFSRJ4HBSNF63YLIXLQ5EDLF274Y6C',
},
{
id: 'seed-user-learner-bob',
email: 'bob.learner+seed@learnault.dev',
name: 'Bob Learner',
walletAddress: 'GB7YLLICSMNYWJ46NWYCBTGXD54FPPWFY3YQYBFJFTQ6B2N3WHAL5V4K',
},
{
id: 'seed-user-learner-carla',
email: 'carla.learner+seed@learnault.dev',
name: 'Carla Learner',
walletAddress: 'GA5N2IBQ2J5KVSBBR6K7D6JQ3Y7L46BP3I7WNIPXQ2XH2WQOTJXK5C2Z',
},
{
id: 'seed-user-learner-deepak',
email: 'deepak.learner+seed@learnault.dev',
name: 'Deepak Learner',
walletAddress: 'GDVXG4D7WQ3WWT3S3Q6L2J5KLC2TSGBYHYQ4M4U7QWEK3J75VYLPQ9U2',
},
{
id: 'seed-user-employer-acme',
email: 'acme.employer+seed@learnault.dev',
name: 'Acme Talent Team',
walletAddress: null,
},
{
id: 'seed-user-employer-globex',
email: 'globex.employer+seed@learnault.dev',
name: 'Globex Hiring Ops',
walletAddress: null,
},
]

const seedModuleFixtures: SeedModule[] = [
{
id: 'seed-module-blockchain-101',
title: 'Stellar Fundamentals',
description: 'Core ledger concepts, accounts, trustlines, and transaction flow on Stellar.',
category: 'blockchain',
difficulty: 'beginner',
reward: 10,
},
{
id: 'seed-module-finance-101',
title: 'Understanding Stablecoins',
description: 'How fiat-backed and crypto-backed stablecoins work across global payment rails.',
category: 'finance',
difficulty: 'beginner',
reward: 12,
},
{
id: 'seed-module-security-201',
title: 'Wallet Security & Key Management',
description: 'Threat modeling, custody approaches, and secure key handling in production systems.',
category: 'security',
difficulty: 'intermediate',
reward: 18,
},
{
id: 'seed-module-development-301',
title: 'Build with Soroban',
description: 'Develop and test smart contracts using practical Soroban development workflows.',
category: 'development',
difficulty: 'advanced',
reward: 30,
},
{
id: 'seed-module-compliance-201',
title: 'AML/KYC for Digital Finance',
description: 'Compliance basics, sanctions screening, and regulated onboarding for fintech teams.',
category: 'compliance',
difficulty: 'intermediate',
reward: 20,
},
{
id: 'seed-module-identity-301',
title: 'Decentralized Identity in Practice',
description: 'Verifiable credentials, selective disclosure, and identity portability patterns.',
category: 'identity',
difficulty: 'advanced',
reward: 28,
},
{
id: 'seed-module-development-401',
title: 'Production API Hardening',
description: 'Rate limiting, auth patterns, observability, and safe rollout practices.',
category: 'development',
difficulty: 'expert',
reward: 40,
},
{
id: 'seed-module-blockchain-202',
title: 'Stellar Asset Issuance',
description: 'Issue and manage custom assets with issuer/distributor architecture.',
category: 'blockchain',
difficulty: 'intermediate',
reward: 22,
},
]

function createPrng(seed: number) {
let state = seed >>> 0

Expand All @@ -162,7 +38,6 @@ function scoreFor(module: SeedModule, rand: () => number) {
}

function xlmAmount(module: SeedModule, rand: () => number) {

return Number((module.reward * (1 + rand() * 0.3)).toFixed(2))
}

Expand All @@ -184,16 +59,22 @@ async function upsertUsers(passwordHash: string) {
where: { id: user.id },
update: {
email: user.email,
name: user.name,
username: user.username,
role: user.role,
status: user.status,
isVerified: user.isVerified,
walletAddress: user.walletAddress,
passwordHash,
password: passwordHash,
},
create: {
id: user.id,
email: user.email,
name: user.name,
username: user.username,
role: user.role,
status: user.status,
isVerified: user.isVerified,
walletAddress: user.walletAddress,
passwordHash,
password: passwordHash,
},
})
}
Expand Down
Loading