feat: add test accounts and secure event slugs
Security improvements: - Add @default(cuid()) to Event.slug for auto-generated random slugs - Prevent ID enumeration attacks (no more predictable slugs like "warsaw-dance-festival-2025") - Event slugs now generated as secure cuid strings (e.g., "cmhz3lcgb00018vbn34v4phoi") Test accounts: - Add 3 test users to seed (john_dancer, sarah_swings, mike_blues) - All users checked in to Warsaw Dance Festival 2025 - Pre-configured heats for testing matchmaking system - Full profiles with WSDC IDs, social media, and locations Seed improvements: - Add bcryptjs for password hashing - Add Prisma seed configuration to package.json - Use worldsdcId for event upsert (instead of slug) - Auto-generate event slugs via Prisma default Documentation: - Add test account credentials to SESSION_CONTEXT.md - Document event slug security model - Include sample heats for each test user
This commit is contained in:
@@ -54,5 +54,8 @@
|
||||
"testMatch": [
|
||||
"**/__tests__/**/*.test.js"
|
||||
]
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "node prisma/seed.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ model User {
|
||||
// Events table (dance events from worldsdc.com)
|
||||
model Event {
|
||||
id Int @id @default(autoincrement())
|
||||
slug String @unique @db.VarChar(50)
|
||||
slug String @unique @default(cuid()) @db.VarChar(50)
|
||||
name String @db.VarChar(255)
|
||||
location String @db.VarChar(255)
|
||||
startDate DateTime @map("start_date") @db.Date
|
||||
|
||||
@@ -1,10 +1,70 @@
|
||||
const { PrismaClient } = require('@prisma/client');
|
||||
const bcrypt = require('bcryptjs');
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
console.log('🌱 Seeding database...');
|
||||
|
||||
// Create test users
|
||||
const testUsers = await Promise.all([
|
||||
prisma.user.upsert({
|
||||
where: { email: 'john@example.com' },
|
||||
update: {},
|
||||
create: {
|
||||
username: 'john_dancer',
|
||||
email: 'john@example.com',
|
||||
passwordHash: await bcrypt.hash('Dance123!', 10),
|
||||
firstName: 'John',
|
||||
lastName: 'Smith',
|
||||
wsdcId: '12345',
|
||||
emailVerified: true,
|
||||
country: 'United States',
|
||||
city: 'Los Angeles',
|
||||
instagramUrl: 'https://instagram.com/johndancer',
|
||||
youtubeUrl: 'https://youtube.com/@johndancer',
|
||||
},
|
||||
}),
|
||||
prisma.user.upsert({
|
||||
where: { email: 'sarah@example.com' },
|
||||
update: {},
|
||||
create: {
|
||||
username: 'sarah_swings',
|
||||
email: 'sarah@example.com',
|
||||
passwordHash: await bcrypt.hash('Swing456!', 10),
|
||||
firstName: 'Sarah',
|
||||
lastName: 'Johnson',
|
||||
wsdcId: '23456',
|
||||
emailVerified: true,
|
||||
country: 'United Kingdom',
|
||||
city: 'London',
|
||||
tiktokUrl: 'https://tiktok.com/@sarahswings',
|
||||
facebookUrl: 'https://facebook.com/sarah.swings',
|
||||
},
|
||||
}),
|
||||
prisma.user.upsert({
|
||||
where: { email: 'mike@example.com' },
|
||||
update: {},
|
||||
create: {
|
||||
username: 'mike_blues',
|
||||
email: 'mike@example.com',
|
||||
passwordHash: await bcrypt.hash('Blues789!', 10),
|
||||
firstName: 'Mike',
|
||||
lastName: 'Williams',
|
||||
wsdcId: '34567',
|
||||
emailVerified: true,
|
||||
country: 'Sweden',
|
||||
city: 'Stockholm',
|
||||
instagramUrl: 'https://instagram.com/mikeblues',
|
||||
youtubeUrl: 'https://youtube.com/@mikeblues',
|
||||
tiktokUrl: 'https://tiktok.com/@mikeblues',
|
||||
facebookUrl: 'https://facebook.com/mike.blues',
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
console.log(`✅ Created ${testUsers.length} test users`);
|
||||
|
||||
// Create divisions
|
||||
const divisions = await Promise.all([
|
||||
prisma.division.upsert({
|
||||
@@ -57,13 +117,12 @@ async function main() {
|
||||
|
||||
console.log(`✅ Created ${competitionTypes.length} competition types`);
|
||||
|
||||
// Create events
|
||||
// Create events (slug will be auto-generated as cuid for security)
|
||||
const events = await Promise.all([
|
||||
prisma.event.upsert({
|
||||
where: { slug: 'warsaw-dance-festival-2025' },
|
||||
where: { worldsdcId: 'wdf-2025' },
|
||||
update: {},
|
||||
create: {
|
||||
slug: 'warsaw-dance-festival-2025',
|
||||
name: 'Warsaw Dance Festival 2025',
|
||||
location: 'Warsaw, Poland',
|
||||
startDate: new Date('2025-03-15'),
|
||||
@@ -74,10 +133,9 @@ async function main() {
|
||||
},
|
||||
}),
|
||||
prisma.event.upsert({
|
||||
where: { slug: 'swing-camp-barcelona-2025' },
|
||||
where: { worldsdcId: 'scb-2025' },
|
||||
update: {},
|
||||
create: {
|
||||
slug: 'swing-camp-barcelona-2025',
|
||||
name: 'Swing Camp Barcelona 2025',
|
||||
location: 'Barcelona, Spain',
|
||||
startDate: new Date('2025-04-20'),
|
||||
@@ -88,10 +146,9 @@ async function main() {
|
||||
},
|
||||
}),
|
||||
prisma.event.upsert({
|
||||
where: { slug: 'blues-week-herrang-2025' },
|
||||
where: { worldsdcId: 'bwh-2025' },
|
||||
update: {},
|
||||
create: {
|
||||
slug: 'blues-week-herrang-2025',
|
||||
name: 'Blues Week Herräng 2025',
|
||||
location: 'Herräng, Sweden',
|
||||
startDate: new Date('2025-07-14'),
|
||||
@@ -102,10 +159,9 @@ async function main() {
|
||||
},
|
||||
}),
|
||||
prisma.event.upsert({
|
||||
where: { slug: 'krakow-swing-connection-2025' },
|
||||
where: { worldsdcId: 'ksc-2025' },
|
||||
update: {},
|
||||
create: {
|
||||
slug: 'krakow-swing-connection-2025',
|
||||
name: 'Krakow Swing Connection 2025',
|
||||
location: 'Krakow, Poland',
|
||||
startDate: new Date('2025-05-10'),
|
||||
@@ -133,13 +189,161 @@ async function main() {
|
||||
|
||||
console.log(`✅ Created ${chatRooms.length} event chat rooms`);
|
||||
|
||||
// Add test users as event participants (check-in)
|
||||
const warsawEvent = events[0]; // Warsaw Dance Festival
|
||||
const barcelonaEvent = events[1]; // Swing Camp Barcelona
|
||||
|
||||
const eventParticipants = await Promise.all([
|
||||
// John participates in Warsaw
|
||||
prisma.eventParticipant.upsert({
|
||||
where: {
|
||||
userId_eventId: {
|
||||
userId: testUsers[0].id,
|
||||
eventId: warsawEvent.id,
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
userId: testUsers[0].id,
|
||||
eventId: warsawEvent.id,
|
||||
},
|
||||
}),
|
||||
// Sarah participates in Warsaw
|
||||
prisma.eventParticipant.upsert({
|
||||
where: {
|
||||
userId_eventId: {
|
||||
userId: testUsers[1].id,
|
||||
eventId: warsawEvent.id,
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
userId: testUsers[1].id,
|
||||
eventId: warsawEvent.id,
|
||||
},
|
||||
}),
|
||||
// Mike participates in Warsaw
|
||||
prisma.eventParticipant.upsert({
|
||||
where: {
|
||||
userId_eventId: {
|
||||
userId: testUsers[2].id,
|
||||
eventId: warsawEvent.id,
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
userId: testUsers[2].id,
|
||||
eventId: warsawEvent.id,
|
||||
},
|
||||
}),
|
||||
// John also in Barcelona
|
||||
prisma.eventParticipant.upsert({
|
||||
where: {
|
||||
userId_eventId: {
|
||||
userId: testUsers[0].id,
|
||||
eventId: barcelonaEvent.id,
|
||||
},
|
||||
},
|
||||
update: {},
|
||||
create: {
|
||||
userId: testUsers[0].id,
|
||||
eventId: barcelonaEvent.id,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
console.log(`✅ Added ${eventParticipants.length} event participants`);
|
||||
|
||||
// Create sample heats for test users in Warsaw event
|
||||
const jjType = competitionTypes[0]; // Jack & Jill
|
||||
const strType = competitionTypes[1]; // Strictly
|
||||
const novDiv = divisions[1]; // Novice
|
||||
const intDiv = divisions[2]; // Intermediate
|
||||
const advDiv = divisions[3]; // Advanced
|
||||
|
||||
const heats = await Promise.all([
|
||||
// John: J&J Novice Heat 1 (Leader), Strictly Intermediate Heat 2 (Leader)
|
||||
prisma.eventUserHeat.create({
|
||||
data: {
|
||||
userId: testUsers[0].id,
|
||||
eventId: warsawEvent.id,
|
||||
divisionId: novDiv.id,
|
||||
competitionTypeId: jjType.id,
|
||||
heatNumber: 1,
|
||||
role: 'Leader',
|
||||
},
|
||||
}),
|
||||
prisma.eventUserHeat.create({
|
||||
data: {
|
||||
userId: testUsers[0].id,
|
||||
eventId: warsawEvent.id,
|
||||
divisionId: intDiv.id,
|
||||
competitionTypeId: strType.id,
|
||||
heatNumber: 2,
|
||||
role: 'Leader',
|
||||
},
|
||||
}),
|
||||
// Sarah: J&J Novice Heat 1 (Follower), J&J Advanced Heat 3
|
||||
prisma.eventUserHeat.create({
|
||||
data: {
|
||||
userId: testUsers[1].id,
|
||||
eventId: warsawEvent.id,
|
||||
divisionId: novDiv.id,
|
||||
competitionTypeId: jjType.id,
|
||||
heatNumber: 1,
|
||||
role: 'Follower',
|
||||
},
|
||||
}),
|
||||
prisma.eventUserHeat.create({
|
||||
data: {
|
||||
userId: testUsers[1].id,
|
||||
eventId: warsawEvent.id,
|
||||
divisionId: advDiv.id,
|
||||
competitionTypeId: jjType.id,
|
||||
heatNumber: 3,
|
||||
role: null,
|
||||
},
|
||||
}),
|
||||
// Mike: J&J Intermediate Heat 5 (Leader), Strictly Advanced Heat 1 (Follower)
|
||||
prisma.eventUserHeat.create({
|
||||
data: {
|
||||
userId: testUsers[2].id,
|
||||
eventId: warsawEvent.id,
|
||||
divisionId: intDiv.id,
|
||||
competitionTypeId: jjType.id,
|
||||
heatNumber: 5,
|
||||
role: 'Leader',
|
||||
},
|
||||
}),
|
||||
prisma.eventUserHeat.create({
|
||||
data: {
|
||||
userId: testUsers[2].id,
|
||||
eventId: warsawEvent.id,
|
||||
divisionId: advDiv.id,
|
||||
competitionTypeId: strType.id,
|
||||
heatNumber: 1,
|
||||
role: 'Follower',
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
console.log(`✅ Created ${heats.length} sample heats`);
|
||||
|
||||
console.log('🎉 Seeding completed successfully!');
|
||||
console.log('');
|
||||
console.log('Created:');
|
||||
console.log(` - ${testUsers.length} test users`);
|
||||
console.log(` - ${divisions.length} divisions`);
|
||||
console.log(` - ${competitionTypes.length} competition types`);
|
||||
console.log(` - ${events.length} events`);
|
||||
console.log(` - ${chatRooms.length} chat rooms`);
|
||||
console.log(` - ${eventParticipants.length} event participants`);
|
||||
console.log(` - ${heats.length} sample heats`);
|
||||
console.log('');
|
||||
console.log('Test accounts:');
|
||||
console.log(' 1. john_dancer / Dance123!');
|
||||
console.log(' 2. sarah_swings / Swing456!');
|
||||
console.log(' 3. mike_blues / Blues789!');
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
@@ -339,17 +339,47 @@ RUN apk add --no-cache openssl
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference - Mock Data
|
||||
## Test Accounts
|
||||
|
||||
**Mock User (logged in):**
|
||||
- ID: 1
|
||||
- Username: john_doe
|
||||
- Email: john@example.com
|
||||
**Test users for manual testing (seeded in database):**
|
||||
|
||||
**Mock Events:**
|
||||
- ID: 1 - Warsaw Dance Festival 2025
|
||||
- ID: 2 - Swing Camp Barcelona 2025
|
||||
- ID: 3 - Blues Week Herräng 2025
|
||||
1. **john_dancer**
|
||||
- Email: `john@example.com`
|
||||
- Password: `Dance123!`
|
||||
- Profile: John Smith (WSDC #12345), Los Angeles, USA
|
||||
- Social: Instagram, YouTube
|
||||
- Events: Warsaw Dance Festival (checked in), Swing Camp Barcelona (checked in)
|
||||
- Heats in Warsaw: J&J NOV 1 L, STR INT 2 L
|
||||
|
||||
2. **sarah_swings**
|
||||
- Email: `sarah@example.com`
|
||||
- Password: `Swing456!`
|
||||
- Profile: Sarah Johnson (WSDC #23456), London, UK
|
||||
- Social: TikTok, Facebook
|
||||
- Events: Warsaw Dance Festival (checked in)
|
||||
- Heats in Warsaw: J&J NOV 1 F, J&J ADV 3
|
||||
|
||||
3. **mike_blues**
|
||||
- Email: `mike@example.com`
|
||||
- Password: `Blues789!`
|
||||
- Profile: Mike Williams (WSDC #34567), Stockholm, Sweden
|
||||
- Social: Instagram, YouTube, TikTok, Facebook
|
||||
- Events: Warsaw Dance Festival (checked in)
|
||||
- Heats in Warsaw: J&J INT 5 L, STR ADV 1 F
|
||||
|
||||
**Note:** All test users are verified and checked in to Warsaw Dance Festival 2025 event. They have pre-configured heats to test the matchmaking system.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference - Events
|
||||
|
||||
**Seeded Events (slugs are auto-generated cuid for security):**
|
||||
- Warsaw Dance Festival 2025 (slug: auto-generated, e.g. `cmhz3lcgb00018vbn34v4phoi`)
|
||||
- Swing Camp Barcelona 2025 (slug: auto-generated)
|
||||
- Blues Week Herräng 2025 (slug: auto-generated)
|
||||
- Krakow Swing Connection 2025 (slug: auto-generated)
|
||||
|
||||
**Note:** Event slugs are randomly generated cuid strings to prevent ID enumeration attacks. To find the actual slug, log in and check the Events page URL.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user