Files
website/server/db/seed.js
wtclaude 0c4eacfa4a
All checks were successful
PR Checks / bot-install (pull_request) Successful in 18s
PR Checks / client-build (pull_request) Successful in 27s
PR Checks / server-tests (pull_request) Successful in 31s
refactor(modules)!: de-UO core's copy, and enforce it (phase 3, slice 4)
Phase 3's acceptance criterion 1, made real. Three things, one review:

**The dead bindings.** `client/src/api/client.js` still carried ~190 lines of UO
namespaces — `shard`, `atlas`, the two SSE URLs, `admin.shard/shardOps/atlas/
userShard`, the uo-link and town-crier calls, `player.shard` — with zero core
consumers since slice 3 deleted the views. module-uo vendors its own bindings.
The five assertions core's `apiClient.test.js` made about those URLs moved with
them (Module-uo#5); the encoding test that used `governorHistory` now uses a
core route.

**The copy.** Core is the platform, not one game's site, so its words are
game-neutral now: `About`, `Screenshots`, `Website`'s cards, `Status` (which was
never about a game server at all — it reports site mode), `Wiki`, `SiteFooter`,
the default hero, `brand.js`'s tagline and description, the seeded wiki
categories, and two user-visible NavEditor strings that named a module's admin
screen by its proper name. Which game an instance is for is the operator's to
say — BRAND_* vars, the hero editor, CMS pages — and every real instance already
does: `.env.uomysticmoon.example` sets both brand strings explicitly, so nothing
live changes wording. Wiki page SLUGS are untouched: `seedDefault*` only inserts
what is absent, so renaming one adds a duplicate page to every install.

Also gone: an orphan comment block in `schema.sql` describing the spawn-atlas
tables slice 1 took away, and the two settings rows core seeded for a module
(`game_account_signup`, `uo_link_protocol_3_migrated`). The second was a live
defect — see Module-uo#5, which takes ownership of both and repairs the
one-shot migration core's ordering had disabled.

**The check.** `scripts/checkModuleIdentifiers.js` + `npm run check:modules`,
first step of the server-tests job because it needs no dependencies. It reads
CODE, not prose — file names, import specifiers, route path literals, declared
identifiers and property names — per §5.2, so core's English may still say
"shard" where saying it is worth more than the word costs.

Two things it gets right only because getting them wrong was tried first: it
matches WHOLE WORDS (a substring pass flags `defaultImage`, which contains
"ultIma", four times in this repo), and it strips comments and string bodies in
one character walk (a comment contains quotes, a string contains `//`) — the
`checkImports.js` lesson. It has its own 17-test suite, because a boundary check
that silently stops checking is worse than none. The three §6.5 grandfathering
allowlists are exempt by name, and an exemption that stops matching fails the
build rather than lingering.

BREAKING CHANGE: core no longer seeds `game_account_signup` or
`uo_link_protocol_3_migrated`; module-uo's schema fragment does. An install
running core without module-uo keeps whatever rows it already has and gains no
new ones — nothing in core reads either key.

Deferred to slice 5, deliberately: README.md's 48 UO mentions, including a
`## Shard integration (uo-link)` section and the architecture diagram. That is
documentation, which §5.2 does not cover, and it belongs with the phase-closing
docs pass rather than half-done here.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-11 21:41:18 -05:00

95 lines
4.0 KiB
JavaScript

require('dotenv').config()
const settingsDb = require('../src/model/settings/settings.db')
const wikiDb = require('../src/model/wiki/wiki.db')
const users = require('../src/model/users/users.model')
const { ensureSchema, close } = require('../src/utils/db')
const brand = require('../src/config/brand')
const log = require('../src/utils/logger')('seed')
// Default settings — only inserted if the key does not already exist.
const DEFAULT_SETTINGS = {
site_mode: 'maintenance', // start safe: site is in maintenance until set live
site_mode_changed_at: '',
site_mode_changed_by: '',
maintenance_message:
`${brand.shortName} is being shaped beneath a midnight sky. The site will return soon.`,
status_message: 'In progress.',
homepage_teaser:
`${brand.shortName} is still being shaped beneath a midnight sky. A quiet preview for ` +
'future news, screenshots, guides, and community notes as the world comes online.',
contact_email: brand.contactEmail,
site_title: brand.name,
// Android App Links opt-in — off until an admin enables it (docs/android/APP_LINKS.md).
mobile_app_links_enabled: 'false',
}
// Starter wiki sections (editable later via the admin panel).
//
// The SLUGS are deliberately untouched by the de-UO pass: `seedDefault*` only
// inserts a row that is not already there, so renaming one adds a duplicate page
// to every existing install rather than renaming anything.
// [slug, title, description, sort_order]
const WIKI_CATEGORIES = [
['guides', 'Guides', 'Getting started and how-to guides.', 10],
['world', 'World & Lore', `Regions, maps, and the story of ${brand.shortName}.`, 20],
['gameplay', 'Systems & Gameplay', 'Mechanics, items, monsters, and crafting.', 30],
['community', 'Community & Rules', 'Player conduct and server policies.', 40],
]
// The 8 starter pages, each mapped to a section. [slug, title, body, categorySlug]
const WIKI_PAGES = [
['new-player-guide', 'New Player Guide', 'First steps, basic survival, and early goals.', 'guides'],
['maps-atlas', 'Maps & Atlas', 'Regions, towns, routes, and travel notes.', 'world'],
['lore', 'Lore', 'Stories, places, factions, and mysteries.', 'world'],
['systems', 'Server Systems', 'Server mechanics and custom features.', 'gameplay'],
['items', 'Items & Rewards', 'Equipment, treasures, rewards, and curiosities.', 'gameplay'],
['monsters', 'Monsters & Encounters', 'Creatures, bosses, spawns, and dangers.', 'gameplay'],
['crafting', 'Crafting', 'Professions, materials, recipes, and tools.', 'gameplay'],
['rules', 'Rules', 'Player conduct, server expectations, and policies.', 'community'],
]
async function seedDefaults() {
for (const [key, value] of Object.entries(DEFAULT_SETTINGS)) {
await settingsDb.seedDefault(key, value)
}
for (const [slug, title, description, sortOrder] of WIKI_CATEGORIES) {
await wikiDb.seedDefaultCategory(slug, title, description, sortOrder)
}
for (const [slug, title, body, categorySlug] of WIKI_PAGES) {
await wikiDb.seedDefault(slug, title, body)
// Attach to its section (only if not already categorized — safe re-run /
// migration of pages seeded before the wiki upgrade).
await wikiDb.assignCategoryBySlug(slug, categorySlug)
}
log.info('settings and wiki defaults ensured')
}
// Create the first admin from env vars, only when no users exist yet.
async function createInitialAdminFromEnv() {
const { ADMIN_USERNAME, ADMIN_PASSWORD } = process.env
if (!ADMIN_USERNAME || !ADMIN_PASSWORD) return
if ((await users.count()) > 0) return
await users.createUser({ username: ADMIN_USERNAME, password: ADMIN_PASSWORD, role: 'admin' })
log.info(`created initial admin "${ADMIN_USERNAME}"`)
}
// Allow running standalone: `npm run seed`
if (require.main === module) {
;(async () => {
try {
await ensureSchema()
await seedDefaults()
await createInitialAdminFromEnv()
} catch (err) {
log.error('seed failed', err)
process.exitCode = 1
} finally {
await close()
}
})()
}
module.exports = { seedDefaults, createInitialAdminFromEnv }