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

@@ -10,6 +10,7 @@ const uoLinkConfig = require('../../model/uoLinkConfig/uoLinkConfig.model')
const uoLinkClient = require('../../utils/uoLinkClient')
const uoLinkSocket = require('../../utils/uoLinkSocket')
const shardBroadcast = require('../../utils/shardBroadcast')
const gameSignup = require('../../utils/gameSignup')
const { activity } = require('../../core')
const log = require('../../core').logger('admin-uolink')
@@ -75,6 +76,34 @@ async function saveConfig(req, res) {
}
}
// GET /admin/uo-link/signup-mode — whether this site creates game accounts.
//
// Core's Site Settings carried this field until slice 3, with help text naming
// Bridge.cfg. It reads as UO policy because it is: the site's mode and the
// shard's own SignupMode have to agree, and only one of those two is core's.
async function getSignupMode(req, res) {
try {
return res.json({ mode: await gameSignup.getMode(), modes: gameSignup.MODES })
} catch (err) {
log.error('uoLink.getSignupMode', err)
return res.status(500).json({ message: 'Internal Server Error' })
}
}
// PUT /admin/uo-link/signup-mode
async function saveSignupMode(req, res) {
const { mode } = req.body
try {
await gameSignup.setMode(mode, req.user.id)
await activity.log({ req, action: 'uoLink.signupMode.update', detail: { mode } })
log.info('game-signup mode updated', { by: req.user.username, mode })
return res.json({ mode })
} catch (err) {
log.error('uoLink.saveSignupMode', err)
return res.status(500).json({ message: 'Internal Server Error' })
}
}
// POST /admin/uo-link/towncrier — publish/replace a town-crier message.
async function postTownCrier(req, res) {
const { id, lines, durationSec } = req.body
@@ -120,4 +149,4 @@ function stream(req, res) {
shardBroadcast.subscribe(req, res, 'admin')
}
module.exports = { getConfig, saveConfig, postTownCrier, deleteTownCrier, stream }
module.exports = { getConfig, saveConfig, getSignupMode, saveSignupMode, postTownCrier, deleteTownCrier, stream }