You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
6.2 KiB
212 lines
6.2 KiB
/**
|
|
* Database Seed Script
|
|
*
|
|
* Seeds the database with initial mock product data for development and testing.
|
|
* Run with: pnpm db:seed
|
|
*/
|
|
|
|
import 'dotenv/config'
|
|
import { drizzle } from 'drizzle-orm/postgres-js'
|
|
import { and, eq } from 'drizzle-orm'
|
|
import postgres from 'postgres'
|
|
import * as schema from './schema'
|
|
import { products, roles, productRoleVisibility } from './schema'
|
|
|
|
/**
|
|
* Standard roles for the system
|
|
* MVP: All roles are created, but assignment is manual
|
|
* Phase 2/3: educator and company roles require approval workflow
|
|
*/
|
|
const standardRoles = [
|
|
{
|
|
code: 'private' as const,
|
|
displayName: 'Privatperson',
|
|
description: 'Für private Besucher und Einzelpersonen',
|
|
requiresApproval: false,
|
|
sortOrder: 1,
|
|
active: true,
|
|
},
|
|
{
|
|
code: 'educator' as const,
|
|
displayName: 'Pädagoge',
|
|
description: 'Für Lehrer, Erzieher und pädagogische Fachkräfte',
|
|
requiresApproval: true,
|
|
sortOrder: 2,
|
|
active: true,
|
|
},
|
|
{
|
|
code: 'company' as const,
|
|
displayName: 'Unternehmen',
|
|
description: 'Für Firmenkunden und B2B-Partner',
|
|
requiresApproval: true,
|
|
sortOrder: 3,
|
|
active: true,
|
|
},
|
|
]
|
|
|
|
/**
|
|
* Sample annual pass products for experimenta
|
|
*/
|
|
const mockProducts = [
|
|
{
|
|
navProductId: 'MSPACE-JK-2025',
|
|
name: 'Makerspace Jahreskarte',
|
|
description:
|
|
'Unbegrenzter Zugang zum Makerspace für 365 Tage. Nutze modernste Werkzeuge, 3D-Drucker, Lasercutter und vieles mehr. Perfekt für Maker, Tüftler und kreative Köpfe.',
|
|
price: '120.00',
|
|
stockQuantity: 100,
|
|
category: 'makerspace-annual-pass',
|
|
active: true,
|
|
},
|
|
{
|
|
navProductId: 'EXPERIMENTA-JK-2025',
|
|
name: 'experimenta Jahreskarte',
|
|
description:
|
|
'Erlebe die Ausstellungswelt der experimenta ein ganzes Jahr lang. Mit freiem Eintritt zu allen Ausstellungen, Science Dome Shows und Sonderausstellungen.',
|
|
price: '85.00',
|
|
stockQuantity: 200,
|
|
category: 'annual-pass',
|
|
active: true,
|
|
},
|
|
{
|
|
navProductId: 'PAEDAGOGEN-JK-2025',
|
|
name: 'Pädagogen Jahreskarte',
|
|
description:
|
|
'Speziell für Lehrkräfte und Pädagogen. Mit exklusiven Fortbildungsangeboten, didaktischen Materialien und freiem Zugang zu allen Ausstellungen.',
|
|
price: '60.00',
|
|
stockQuantity: 50,
|
|
category: 'educator-annual-pass',
|
|
active: true,
|
|
},
|
|
]
|
|
|
|
async function seed() {
|
|
// Get database connection from environment
|
|
const connectionString = process.env.DATABASE_URL
|
|
if (!connectionString) {
|
|
throw new Error('DATABASE_URL environment variable is not set')
|
|
}
|
|
|
|
console.log('🌱 Starting database seed...')
|
|
|
|
// Create database connection with schema
|
|
const client = postgres(connectionString)
|
|
const db = drizzle(client, { schema })
|
|
|
|
try {
|
|
// 1. Insert/Update Roles
|
|
console.log(`👥 Inserting ${standardRoles.length} roles...`)
|
|
const insertedRoles = []
|
|
for (const roleData of standardRoles) {
|
|
const [role] = await db
|
|
.insert(roles)
|
|
.values(roleData)
|
|
.onConflictDoUpdate({
|
|
target: roles.code,
|
|
set: {
|
|
displayName: roleData.displayName,
|
|
description: roleData.description,
|
|
requiresApproval: roleData.requiresApproval,
|
|
sortOrder: roleData.sortOrder,
|
|
active: roleData.active,
|
|
updatedAt: new Date(),
|
|
},
|
|
})
|
|
.returning()
|
|
insertedRoles.push(role)
|
|
}
|
|
|
|
console.log(`✅ Successfully inserted/updated ${insertedRoles.length} roles:`)
|
|
insertedRoles.forEach((role) => {
|
|
console.log(
|
|
` - ${role.displayName} (${role.code}) ${role.requiresApproval ? '[requires approval]' : '[auto-approved]'}`
|
|
)
|
|
})
|
|
|
|
// 2. Insert/Update Products
|
|
console.log(`\n📦 Inserting ${mockProducts.length} products...`)
|
|
const insertedProducts = []
|
|
for (const productData of mockProducts) {
|
|
const [product] = await db
|
|
.insert(products)
|
|
.values(productData)
|
|
.onConflictDoUpdate({
|
|
target: products.navProductId,
|
|
set: {
|
|
name: productData.name,
|
|
description: productData.description,
|
|
price: productData.price,
|
|
stockQuantity: productData.stockQuantity,
|
|
category: productData.category,
|
|
active: productData.active,
|
|
updatedAt: new Date(),
|
|
},
|
|
})
|
|
.returning()
|
|
insertedProducts.push(product)
|
|
}
|
|
|
|
console.log(`✅ Successfully inserted/updated ${insertedProducts.length} products:`)
|
|
insertedProducts.forEach((product) => {
|
|
console.log(` - ${product.name} (${product.navProductId}) - €${product.price}`)
|
|
})
|
|
|
|
// 3. Assign Roles to Products (Category-based mapping)
|
|
console.log(`\n🔗 Assigning roles to products based on category...`)
|
|
|
|
// Category to role mapping
|
|
const categoryRoleMapping: Record<string, string[]> = {
|
|
'makerspace-annual-pass': ['private', 'educator'],
|
|
'annual-pass': ['private'],
|
|
'educator-annual-pass': ['educator'],
|
|
}
|
|
|
|
let assignmentCount = 0
|
|
for (const product of insertedProducts) {
|
|
const roleCodes = categoryRoleMapping[product.category] || []
|
|
|
|
for (const roleCode of roleCodes) {
|
|
// Find role by code
|
|
const role = insertedRoles.find((r) => r.code === roleCode)
|
|
if (!role) continue
|
|
|
|
// Check if assignment already exists
|
|
const existing = await db.query.productRoleVisibility.findFirst({
|
|
where: and(
|
|
eq(productRoleVisibility.productId, product.id),
|
|
eq(productRoleVisibility.roleId, role.id)
|
|
),
|
|
})
|
|
|
|
if (!existing) {
|
|
await db.insert(productRoleVisibility).values({
|
|
productId: product.id,
|
|
roleId: role.id,
|
|
})
|
|
assignmentCount++
|
|
console.log(` - ${product.name} → ${role.displayName}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`✅ Created ${assignmentCount} product-role assignments`)
|
|
|
|
console.log('\n✨ Database seed completed successfully!')
|
|
} catch (error) {
|
|
console.error('❌ Error seeding database:', error)
|
|
throw error
|
|
} finally {
|
|
// Close database connection
|
|
await client.end()
|
|
}
|
|
}
|
|
|
|
// Run seed function
|
|
seed()
|
|
.then(() => {
|
|
process.exit(0)
|
|
})
|
|
.catch((error) => {
|
|
console.error('Fatal error:', error)
|
|
process.exit(1)
|
|
})
|
|
|