- Seed now resets the database and seeds the Aldenfell campaign from the campaign book: admin DM user, default weapons, locations, bestiary, pre-built encounters, and DM/shared notes - Default weapon/spell/monster library, character profile fields, locations and dice mode (new migration) - Party overview page, pagination, info tips, build preview calc - Remove stale demo-login hint; notes preserve line breaks Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
111 lines
3.8 KiB
JavaScript
111 lines
3.8 KiB
JavaScript
// Resets the database and seeds the Aldenfell campaign (see
|
|
// docs/Aldenfell - Full Campaign Book.docx and seed-data/aldenfell.js).
|
|
//
|
|
// DESTRUCTIVE: every user, campaign, character, note, etc. is deleted first.
|
|
//
|
|
// Species/classes/skills are NOT seeded as rows — they're fixed game-rule
|
|
// tables ported into src/game/data/*.js, as is the default weapon/spell/monster
|
|
// library DMs pull from. The default weapons (seed-data/weapons.json) ARE
|
|
// seeded, into the campaign, since weapons are per-campaign in the schema.
|
|
const path = require('path');
|
|
const bcrypt = require('bcrypt');
|
|
const { PrismaClient } = require('@prisma/client');
|
|
const { generateInviteCode } = require('../src/utils/inviteCode');
|
|
|
|
const prisma = new PrismaClient();
|
|
const weapons = require(path.join(__dirname, '..', 'seed-data', 'weapons.json'));
|
|
const aldenfell = require(path.join(__dirname, '..', 'seed-data', 'aldenfell.js'));
|
|
|
|
const ADMIN_EMAIL = 'joshua@crosscode.co.za';
|
|
const ADMIN_PASSWORD = '12345678';
|
|
|
|
async function resetDatabase() {
|
|
// Truncating the two roots cascades through every table that references them.
|
|
await prisma.$executeRawUnsafe('TRUNCATE TABLE "users", "campaigns" RESTART IDENTITY CASCADE');
|
|
}
|
|
|
|
async function main() {
|
|
await resetDatabase();
|
|
|
|
const admin = await prisma.user.create({
|
|
data: {
|
|
email: ADMIN_EMAIL,
|
|
passwordHash: await bcrypt.hash(ADMIN_PASSWORD, 12),
|
|
displayName: 'Joshua',
|
|
},
|
|
});
|
|
|
|
const campaign = await prisma.campaign.create({
|
|
data: {
|
|
...aldenfell.campaign,
|
|
inviteCode: generateInviteCode(),
|
|
ownerId: admin.id,
|
|
members: { create: { userId: admin.id, role: 'DM' } },
|
|
},
|
|
});
|
|
|
|
await prisma.weapon.createMany({ data: weapons.map((w) => ({ ...w, campaignId: campaign.id })) });
|
|
|
|
await prisma.location.createMany({
|
|
data: aldenfell.locations.map((l) => ({ ...l, campaignId: campaign.id })),
|
|
});
|
|
|
|
const monsterByName = {};
|
|
for (const monster of aldenfell.monsters) {
|
|
const created = await prisma.monster.create({ data: { ...monster, campaignId: campaign.id } });
|
|
monsterByName[created.name] = created;
|
|
}
|
|
|
|
for (const enc of aldenfell.encounters) {
|
|
const participants = [];
|
|
for (const [name, count] of enc.monsters) {
|
|
const monster = monsterByName[name];
|
|
if (!monster) throw new Error(`Encounter "${enc.name}" references unknown monster "${name}"`);
|
|
for (let i = 1; i <= count; i++) {
|
|
participants.push({
|
|
monsterId: monster.id,
|
|
displayName: count > 1 ? `${monster.name} #${i}` : monster.name,
|
|
currentHp: monster.maxHp,
|
|
maxHp: monster.maxHp,
|
|
isMonster: true,
|
|
});
|
|
}
|
|
}
|
|
await prisma.encounter.create({
|
|
data: { campaignId: campaign.id, name: enc.name, participants: { create: participants } },
|
|
});
|
|
}
|
|
|
|
// Notes list newest-first, so stamp them in reverse to keep book order on screen.
|
|
const allNotes = [
|
|
...aldenfell.sharedNotes.map((body) => ({ body, visibility: 'SHARED' })),
|
|
...aldenfell.dmNotes.map((body) => ({ body, visibility: 'DM_ONLY' })),
|
|
];
|
|
const now = Date.now();
|
|
await prisma.note.createMany({
|
|
data: allNotes.map((n, i) => ({
|
|
...n,
|
|
campaignId: campaign.id,
|
|
authorId: admin.id,
|
|
createdAt: new Date(now - i * 1000),
|
|
})),
|
|
});
|
|
|
|
console.log('Seed complete.');
|
|
console.log(` Admin/DM login: ${ADMIN_EMAIL} / ${ADMIN_PASSWORD}`);
|
|
console.log(` Campaign: ${campaign.name} — invite code ${campaign.inviteCode}`);
|
|
console.log(
|
|
` ${weapons.length} weapons, ${aldenfell.locations.length} locations, ${aldenfell.monsters.length} monsters, ` +
|
|
`${aldenfell.encounters.length} encounters, ${allNotes.length} notes`,
|
|
);
|
|
}
|
|
|
|
main()
|
|
.catch((err) => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|