- A DM can start a campaign from a ready-made world by typing its six-character code: "+ New Campaign" on the Campaigns page, then "Or start from a ready-made world". The whole world is built in one go with them as its DM: locations, weapons, items, spells, monsters, NPCs, encounters and notes. ALDENF is Aldenfell, HOLLOW is Hollowcrown. - A world is one file in /backend/seeds. Drop another in and its code works straight away, with no restart. "npm run seeds:check" lists every world, or what is wrong with one: a misspelt field, a name used twice, an encounter pointing at something the file does not have. A file with a mistake in it is never offered. docs/SEEDS.md has the format. - A seed can now hold everything a campaign can: gear effects, an NPC's wares and exchange rate, things for sale at an encounter, and rewards in silver, copper and cash. - The database seed creates the admin account and nothing else. The Aldenfell and Hollowcrown seed commands are gone: worlds come from codes. - Hollowcrown follows the V3 book: 4 starting items and 3 things equipped, the book's effects on every weapon and item, armor as items (library, modern and magic), worn gear at the book's levels, damage types on monsters, ten shop scenes with prices and stock, five money changers, Meridian loot as cash and mixed hoards as gold, silver and copper. - Aldenfell: the Shield and Leather Armor change AC, Mireille and the Founding Fair stalls sell things, and the book's missing scenes are in (E5, E7, E15, E16, E18, E23-24, E26, the Drowned Shrine, Sevrisk's Hollow, a wyvern pass). The locket is found with Investigation, as the book has it. - Packages are up to date: Prisma 7 (with the pg driver adapter and a prisma.config.js that holds the database URL), Express 5, zod 4, dotenv 18, jest 30, bcrypt 6 and TypeScript 7. "npm audit" finds nothing. Install scripts are approved for Prisma and bcrypt. On a server: "npm install", then "npm run deploy". - The frontend's five lint warnings are fixed; useAuth and usePagination have files of their own. - All packages at version 1.12.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
55 lines
2.4 KiB
JavaScript
55 lines
2.4 KiB
JavaScript
// The database seed: makes sure the admin account exists, and nothing else.
|
|
//
|
|
// npx prisma db seed create the admin account if it is missing
|
|
// npx prisma db seed -- --reset DESTRUCTIVE: delete every user, campaign,
|
|
// character, note, etc. first
|
|
//
|
|
// It only ever adds, so it is safe against a live database: an existing
|
|
// account, and its password, is left alone. SEED_ADMIN_EMAIL /
|
|
// SEED_ADMIN_PASSWORD / SEED_ADMIN_NAME override the account that is created.
|
|
//
|
|
// Worlds are not seeded here. A campaign is created in the app, blank or from
|
|
// one of the world seeds in /backend/seeds by its code (docs/SEEDS.md).
|
|
//
|
|
// Species/classes/skills are not rows either: they are fixed game-rule tables
|
|
// in src/game/data/*.js, as is the library DMs pull weapons, items, spells
|
|
// and monsters from.
|
|
const bcrypt = require('bcrypt');
|
|
const prisma = require('../src/db');
|
|
const { loadSeeds } = require('../src/seeds');
|
|
|
|
const ADMIN_EMAIL = process.env.SEED_ADMIN_EMAIL || 'joshua@crosscode.co.za';
|
|
const ADMIN_PASSWORD = process.env.SEED_ADMIN_PASSWORD || '12345678';
|
|
const ADMIN_NAME = process.env.SEED_ADMIN_NAME || 'Joshua';
|
|
|
|
async function seed({ reset = false } = {}) {
|
|
if (reset) {
|
|
// Truncating the two roots cascades through every table that references them.
|
|
await prisma.$executeRawUnsafe('TRUNCATE TABLE "users", "campaigns" RESTART IDENTITY CASCADE');
|
|
console.log('Database reset: every user and campaign was deleted.');
|
|
}
|
|
|
|
if (await prisma.user.findUnique({ where: { email: ADMIN_EMAIL } })) {
|
|
console.log(`Admin account ${ADMIN_EMAIL} already exists — left as it is.`);
|
|
} else {
|
|
await prisma.user.create({
|
|
data: { email: ADMIN_EMAIL, passwordHash: await bcrypt.hash(ADMIN_PASSWORD, 12), displayName: ADMIN_NAME },
|
|
});
|
|
console.log(`Admin/DM login created: ${ADMIN_EMAIL} / ${ADMIN_PASSWORD}`);
|
|
console.log(' Change that password from the account page ("Account" in the navigation) once you are logged in.');
|
|
}
|
|
|
|
const worlds = loadSeeds().filter((world) => !world.error);
|
|
if (worlds.length) {
|
|
console.log('Worlds to start a campaign from (Campaigns → New Campaign → world code):');
|
|
for (const { code, seed: world } of worlds) console.log(` ${code} ${world.campaign.name}`);
|
|
}
|
|
}
|
|
|
|
seed({ reset: process.argv.includes('--reset') })
|
|
.catch((err) => {
|
|
console.error(err);
|
|
process.exitCode = 1;
|
|
})
|
|
.finally(() => prisma.$disconnect());
|