Files
website/server/src/config/brand.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

52 lines
2.6 KiB
JavaScript

// ── Branding (BRAND_*) ─────────────────────────────────────────────────────
//
// Single source of instance branding. Reads BRAND_* env vars once at startup,
// with Runic Gateway defaults, so any instance substitutes its own identity
// without a rebuild (the app ships as one prebuilt image).
//
// How it reaches the UI:
// • Text + colors + asset paths are surfaced to the SPA through the public
// settings API (settings.model.getPublic → SiteContext). The two fields the
// admin can edit (site title, contact email) override these defaults.
// • The static index.html shell (title/description/OG/favicon) is templated by
// Express at serve time (see src/app.js).
// • Server-side consumers (emails, TOTP issuer, API docs) read this directly.
//
// Image assets are delivered from the /brand mount (BRAND_LOGO/HERO/FAVICON), or
// any absolute URL. Runic Gateway ships neutral defaults baked into the image so
// an instance with no BRAND_* set still renders.
require('dotenv').config()
const name = process.env.BRAND_NAME || 'Runic Gateway'
const brand = {
name,
shortName: process.env.BRAND_SHORT_NAME || name,
// Game-neutral defaults. Core is the platform, not one game's site: which game
// this instance is for is the operator's to say, through these two vars or an
// installed module (MODULE_SYSTEM.md §2.7.1, slice 4). Every real instance
// overrides both — `.env.uomysticmoon.example` sets its own wording — so these
// are what an unconfigured instance shows, not what anyone ships.
tagline: process.env.BRAND_TAGLINE || 'an independent, privately-run game server',
description:
process.env.BRAND_DESCRIPTION ||
`${name} — an independent, privately-run game server. News, screenshots, guides, and community notes.`,
contactEmail: process.env.BRAND_CONTACT_EMAIL || process.env.CONTACT_TO || '',
url: process.env.BRAND_URL || '',
// Visual
accent: process.env.BRAND_ACCENT_COLOR || '#7f99bd',
logo: process.env.BRAND_LOGO || '', // empty → no logo image rendered
hero: process.env.BRAND_HERO || '/assets/img/runic-emblem.png',
favicon: process.env.BRAND_FAVICON || '/assets/img/favicon.ico', // Runic Gateway default emblem
}
// Discord embeds want an int (0xRRGGBB). Parse the accent hex once; fall back to
// the default accent if it's malformed.
brand.accentInt = (() => {
const hex = String(brand.accent).replace('#', '')
const n = parseInt(hex, 16)
return Number.isNaN(n) ? 0x7f99bd : n
})()
module.exports = brand