- Remove explicit ADMIN role checks in API handlers - Replace combined creator/member role checks with membership-only filters - Update seed data roles to reflect changed authorization logic - Add migration to insert hunt creators as members for consistent checks - Prevent removal of hunt creator as a member with new explicit check - Adjust related user role updates and queries accordingly - Upgrade dependencies including nuxt and zod for compatibility
398 lines
11 KiB
TypeScript
398 lines
11 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
import { hash } from 'argon2';
|
|
import { randomUUID } from 'node:crypto';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
await prisma.$transaction(async (tx) => {
|
|
// region Users
|
|
const password = await hash('12345678');
|
|
const admin = await tx.user.upsert({
|
|
where: { email: 'admin@mail.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'admin@mail.com',
|
|
name: 'Admin user',
|
|
role: 'CREATOR',
|
|
password,
|
|
emailConfirmedAt: new Date()
|
|
}
|
|
});
|
|
const huntMember = await tx.user.upsert({
|
|
where: { email: 'team@mail.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'team@mail.com',
|
|
name: 'Hunt member',
|
|
role: 'USER',
|
|
password,
|
|
emailConfirmedAt: new Date()
|
|
}
|
|
});
|
|
|
|
const user = await tx.user.upsert({
|
|
where: { email: 'user@mail.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'user@mail.com',
|
|
name: 'User',
|
|
role: 'USER',
|
|
password,
|
|
emailConfirmedAt: new Date()
|
|
}
|
|
});
|
|
const teamMember = await tx.user.upsert({
|
|
where: { email: 'member@mail.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'member@mail.com',
|
|
name: 'Team Member',
|
|
role: 'USER',
|
|
password,
|
|
emailConfirmedAt: new Date()
|
|
}
|
|
});
|
|
|
|
const user2 = await tx.user.upsert({
|
|
where: { email: 'user2@mail.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'user2@mail.com',
|
|
name: 'User2',
|
|
role: 'USER',
|
|
password,
|
|
emailConfirmedAt: new Date()
|
|
}
|
|
});
|
|
const teamMember2 = await tx.user.upsert({
|
|
where: { email: 'member2@mail.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'member2@mail.com',
|
|
name: 'Team Member2',
|
|
role: 'USER',
|
|
password,
|
|
emailConfirmedAt: new Date()
|
|
}
|
|
});
|
|
const user3 = await tx.user.upsert({
|
|
where: { email: 'user3@mail.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'user3@mail.com',
|
|
name: 'User3',
|
|
role: 'USER',
|
|
password,
|
|
emailConfirmedAt: new Date()
|
|
}
|
|
});
|
|
const teamMember3 = await tx.user.upsert({
|
|
where: { email: 'member3@mail.com' },
|
|
update: {},
|
|
create: {
|
|
email: 'member3@mail.com',
|
|
name: 'Team Member3',
|
|
role: 'USER',
|
|
password,
|
|
emailConfirmedAt: new Date()
|
|
}
|
|
});
|
|
// endregion
|
|
|
|
const hunt = await tx.hunt.create({
|
|
include: {
|
|
teams: true,
|
|
quests: {
|
|
orderBy: {
|
|
order: 'asc'
|
|
}
|
|
}
|
|
},
|
|
data: {
|
|
name_de: 'Bremer Stadtrallye!',
|
|
name_en: 'Bremer Stadtrallye!',
|
|
description_de: 'DIE Rallye durch Bremen!',
|
|
description_en: 'THE Rallye trough Bremen!',
|
|
start: new Date('2025-08-23T10:00:00.000Z'),
|
|
end: new Date('2025-08-30T10:00:00.000Z'),
|
|
allowJoin: true,
|
|
revealQuests: true,
|
|
revealAnswers: false,
|
|
quests: {
|
|
create: [
|
|
{
|
|
title_de: 'MOIN!',
|
|
title_en: 'MOIN!',
|
|
description_de: 'Finde das größte MOIN',
|
|
description_en: 'Find the largest MOIN',
|
|
question_de: 'Wo steht es?',
|
|
question_en: 'Where is it?',
|
|
order: 0,
|
|
optional: false,
|
|
pictureRequired: true,
|
|
textRequired: false,
|
|
textType: 'TEXT',
|
|
textSolution: 'Am Herdentor',
|
|
points: 10
|
|
},
|
|
{
|
|
title_de: 'Die Musikanten',
|
|
title_en: 'The Musikanten',
|
|
description_de:
|
|
'Dort wo die vier Bremer Tiere musizieren, müsst ihr ihre Beine anfassen!',
|
|
description_en:
|
|
'Where the four Bremen music animals are, you have to touch their legs!',
|
|
question_de: 'Wie viele Beine haben die Musikanten zusammen?',
|
|
question_en: 'How many legs do the musicians have altogether?',
|
|
extraText_de:
|
|
'Mehr Punkte, je mehr unterschiedliche Beine ihr berührt!',
|
|
extraText_en:
|
|
'The more different legs you touch, the more points you get!',
|
|
points: 10,
|
|
extraPoints: 5,
|
|
pictureRequired: true,
|
|
optional: false,
|
|
textRequired: true,
|
|
textType: 'INTEGER',
|
|
textSolution: '14',
|
|
order: 1
|
|
},
|
|
{
|
|
title_de: 'Tolle Aussicht',
|
|
title_en: 'Great view',
|
|
description_de:
|
|
'Mache ein Bild aus einem Gebäude der Hochschule, von so weit oben wie möglich!',
|
|
description_en:
|
|
'Take a picture of a university building from as high up as possible!',
|
|
points: 10,
|
|
extraPoints: 10,
|
|
extraText_de:
|
|
'Wenn ihr das einzige Team in diesem Gebäude/Standort oder die Höchsten wart!',
|
|
extraText_en:
|
|
'If you were the only team in that building/location or on the highest floor!',
|
|
optional: false,
|
|
pictureRequired: true,
|
|
textRequired: false,
|
|
question_de: 'In welchem Gebäude und Stockwerk wart ihr?',
|
|
question_en: 'Which building and floor were you in?',
|
|
textType: 'TEXT',
|
|
order: 2
|
|
},
|
|
{
|
|
title_de: 'Einfach mal abhauen',
|
|
title_en: 'Just get away',
|
|
description_de:
|
|
'Fahrt so weit ihr wollt und macht ein Foto vom Haltestellenschild!',
|
|
description_en:
|
|
'Drive as far as you want and take a photo of the bus stop sign!',
|
|
question_de: 'An welcher Haltestelle wart ihr?',
|
|
question_en: 'Which stop were you at?',
|
|
order: 3,
|
|
pictureRequired: true,
|
|
textRequired: true,
|
|
textType: 'TEXT',
|
|
points: 3,
|
|
extraPoints: 30,
|
|
extraText_de: 'Je weiter weg',
|
|
extraText_en: 'The further away'
|
|
}
|
|
]
|
|
},
|
|
creatorId: admin.id,
|
|
members: {
|
|
create: [
|
|
{
|
|
memberId: admin.id
|
|
},
|
|
{
|
|
memberId: huntMember.id
|
|
}
|
|
]
|
|
},
|
|
teams: {
|
|
create: [
|
|
{
|
|
ownerId: user.id,
|
|
name: 'Team 1',
|
|
description: 'Das beste Informatik Team!',
|
|
password: 'top secret',
|
|
members: {
|
|
create: [
|
|
{
|
|
memberId: teamMember.id
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
ownerId: user2.id,
|
|
name: 'Team 2',
|
|
description: 'Das schlechteste Informatik Team!',
|
|
password: 'top secret',
|
|
members: {
|
|
create: [
|
|
{
|
|
memberId: teamMember2.id
|
|
}
|
|
]
|
|
}
|
|
},
|
|
{
|
|
ownerId: user3.id,
|
|
name: 'Team 3',
|
|
description: 'Das Informatik Team!',
|
|
password: 'top secret',
|
|
members: {
|
|
create: [
|
|
{
|
|
memberId: teamMember3.id
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
});
|
|
|
|
const team = hunt.teams[0];
|
|
const team2 = hunt.teams[1];
|
|
const team3 = hunt.teams[2];
|
|
|
|
const images = [
|
|
'/img/dummy/aussicht1.jpeg',
|
|
'https://pichunt.s3.swarm.syma.dev/0197ff6a-ce49-7000-a5b0-f32f1ee1b4ee',
|
|
'https://pichunt.s3.swarm.syma.dev/0197ff1a-3c8e-7000-8ba6-fa9c06548f82',
|
|
'https://pichunt.s3.swarm.syma.dev/0197ff5b-f74d-7000-864c-20f531481fcc',
|
|
'/img/dummy/moin1.jpeg',
|
|
'/img/dummy/musikanten1.jpg'
|
|
];
|
|
const reviews = [
|
|
{
|
|
reviewerId: admin.id,
|
|
score: 10,
|
|
review: 'Schönes Bild!',
|
|
internalNote: 'Wow!'
|
|
},
|
|
{
|
|
score: 17,
|
|
reviewerId: huntMember.id,
|
|
review: 'Höchstes Team, aber leider gabs andere im AB!',
|
|
rankingValue: 10
|
|
},
|
|
{
|
|
score: 13,
|
|
reviewerId: admin.id,
|
|
review: 'Ihr wart 612m weit weg!',
|
|
rankingValue: 612,
|
|
internalNote: '612m'
|
|
},
|
|
{
|
|
score: 21,
|
|
reviewerId: admin.id,
|
|
review: 'Ihr wart 2110m weit weg!',
|
|
rankingValue: 2110,
|
|
internalNote: 'Bisher am weitesten'
|
|
},
|
|
{
|
|
score: 18,
|
|
reviewerId: huntMember.id,
|
|
review: 'Ihr wart 945m weit weg!',
|
|
rankingValue: 945,
|
|
showcase: true
|
|
}
|
|
];
|
|
const answers = await tx.questAnswer.createManyAndReturn({
|
|
data: [
|
|
{
|
|
teamId: team.id,
|
|
memberId: user.id,
|
|
questId: hunt.quests[0].id,
|
|
text: 'Am Herdentor',
|
|
location: 'An der Haltestelle Herdentor',
|
|
lat: 53.079138,
|
|
lon: 8.809734,
|
|
final: true
|
|
},
|
|
{
|
|
teamId: team.id,
|
|
memberId: teamMember.id,
|
|
questId: hunt.quests[2].id,
|
|
text: 'Neustadtswall AB-Gebäude Etage 10',
|
|
final: true
|
|
},
|
|
{
|
|
teamId: team.id,
|
|
memberId: teamMember.id,
|
|
questId: hunt.quests[3].id,
|
|
text: 'Theater am Leibnizplatz',
|
|
final: true
|
|
},
|
|
{
|
|
teamId: team2.id,
|
|
memberId: teamMember2.id,
|
|
questId: hunt.quests[3].id,
|
|
text: 'Bürgerpark',
|
|
final: true
|
|
},
|
|
{
|
|
teamId: team3.id,
|
|
memberId: teamMember3.id,
|
|
questId: hunt.quests[3].id,
|
|
text: 'Am Brill',
|
|
final: true
|
|
},
|
|
{
|
|
teamId: team.id,
|
|
memberId: teamMember.id,
|
|
questId: hunt.quests[1].id,
|
|
text: '14',
|
|
location: 'Marktplatz',
|
|
final: false
|
|
}
|
|
],
|
|
select: {
|
|
id: true,
|
|
memberId: true
|
|
}
|
|
});
|
|
|
|
await Promise.all(
|
|
answers.map((a, i) =>
|
|
tx.file.create({
|
|
data: {
|
|
url: images[i],
|
|
creatorId: a.memberId,
|
|
uuid: randomUUID(),
|
|
answers: { connect: { id: a.id } },
|
|
answerId: a.id
|
|
}
|
|
})
|
|
)
|
|
);
|
|
await Promise.all(
|
|
reviews.map((r, i) =>
|
|
tx.review.create({
|
|
data: {
|
|
answerId: answers[i].id,
|
|
...r
|
|
}
|
|
})
|
|
)
|
|
);
|
|
|
|
console.log(JSON.stringify(hunt, null, 2));
|
|
});
|
|
}
|
|
|
|
main()
|
|
.then(async () => {
|
|
await prisma.$disconnect();
|
|
})
|
|
.catch(async (e) => {
|
|
console.error(e);
|
|
await prisma.$disconnect();
|
|
process.exit(1);
|
|
});
|