fix(server): own game-account signup, and repair the gate slice 1 broke

`POST /player/shard/account` and its staff twin have answered 500 for every
caller since slice 1: the ported controller called
`settings.isGameAccountSignupEnabled()`, which is a member of core's settings
model and not of `ctx.settings` — three functions, deliberately. The call was
`undefined(...)`, the TypeError landed in the catch, and no test reached the
branch.

The gate now lives on the side that uses it (`utils/gameSignup.js`), which is
also where the policy belongs: the setting's own help text names Bridge.cfg and
says the shard's SignupMode must agree, and core cannot own a sentence about a
UO shard. The admin field moves to this module's Shard page and the derived
flag onto `/public/shard/features`, beside the visibility flags the same
callers already read.

The setting KEY is unchanged. Renaming `game_account_signup` would silently
reset every configured instance to `disabled` on upgrade, with players
reporting broken signup as the only clue — the same grandfathering as
`spawn_atlas_servuo_path` and the seven stream ids.

Both regression tests were shown to fail against the bug before it was fixed.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 18:00:37 -05:00
parent 28f4b9afe2
commit 493cf296ab
5 changed files with 305 additions and 2 deletions

View File

@@ -15,6 +15,7 @@ const shardMarket = require('../../model/shardMarket/shardMarket.model')
const uoLinkConfig = require('../../model/uoLinkConfig/uoLinkConfig.model')
const broadcast = require('../../utils/shardBroadcast')
const visibility = require('../../utils/shardVisibility')
const gameSignup = require('../../utils/gameSignup')
const log = require('../../core').logger('public-shard')
@@ -386,7 +387,20 @@ async function getFeatures(req, res) {
try {
const config = await visibility.getConfig()
const level = await visibility.viewerLevel(req)
return res.json({ level, features: visibility.visibleFeatures(level, config) })
return res.json({
level,
features: visibility.visibleFeatures(level, config),
// Whether this site offers game-account creation. Not a visibility flag
// and deliberately carried here anyway: it is the same per-viewer,
// once-a-session answer, and the alternative is a second endpoint and a
// second round-trip for one boolean. It is NOT audience-gated — it says
// what the site offers, not what this caller may see, and the portal's
// create-account form is behind a session either way.
//
// Core's public settings carried this until slice 3. It is ours now
// (utils/gameSignup.js), because the setting is about a game server.
gameAccountSignup: await gameSignup.isEnabled(),
})
} catch (err) {
log.error('shard.getFeatures', err)
return res.status(500).json({ message: 'Internal Server Error' })