Player accounts frontend + Swagger + schema comment fix
- Player portal: RequirePlayer guard, /account routes (login, register, settings) with shared PlayerShell; register reads /public/settings derived flags; AuthContext.register; api.register + api.player.* namespace. - Admin UI: player role + status/email + reset-password hint in UserEditor, status column + badge-player in UsersAdmin, player_registration select in SettingsAdmin; 'disabled' SSO error copy. - Swagger: Player tag + RegisterRequest/ChangeUsername/ChangePassword/ PlayerAccount/OkFlag schemas; regenerated swagger-output.json. - Fix: remove a semicolon from a schema.sql inline comment that broke the statement splitter in ensureSchema. Verified against the live dev DB: schema migrations apply (player enum, nullable password_hash, email/status/last_login_ip, seeded setting); 21-check controller smoke (register gating, dup/reserved, null-hash rules, self change username/password with session re-issue surviving the cutoff, SSO-only initial password, banned-login refusal); case-insensitive uniqueness; public settings expose only derived registration flags. Client builds; 133 server tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019rao86n5cXpwAyjdBFEshV
This commit is contained in:
@@ -5,7 +5,12 @@ import { dateTime } from '../../../lib/format.js'
|
||||
import { api } from '../../../api/client.js'
|
||||
import UserEditor from './UserEditor.jsx'
|
||||
|
||||
const ROLE_BADGE = { admin: 'badge-admin', editor: 'badge-editor', moderator: 'badge-moderator' }
|
||||
const ROLE_BADGE = {
|
||||
admin: 'badge-admin',
|
||||
editor: 'badge-editor',
|
||||
moderator: 'badge-moderator',
|
||||
player: 'badge-player',
|
||||
}
|
||||
|
||||
export default function UsersAdmin() {
|
||||
const [tick, setTick] = useState(0)
|
||||
@@ -18,7 +23,7 @@ export default function UsersAdmin() {
|
||||
<section>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 18, flexWrap: 'wrap', gap: 12 }}>
|
||||
<p className="sans muted" style={{ margin: 0, fontSize: '0.9rem' }}>
|
||||
Manage admin, editor, and moderator accounts
|
||||
Manage admin, editor, moderator, and player accounts
|
||||
</p>
|
||||
<button onClick={() => setEditing('new')} className="btn btn-primary btn-sq">
|
||||
+ Add user
|
||||
@@ -35,6 +40,7 @@ export default function UsersAdmin() {
|
||||
<tr>
|
||||
<th className="adm-th">Username</th>
|
||||
<th className="adm-th">Role</th>
|
||||
<th className="adm-th">Status</th>
|
||||
<th className="adm-th">Last login</th>
|
||||
<th className="adm-th" />
|
||||
</tr>
|
||||
@@ -48,6 +54,14 @@ export default function UsersAdmin() {
|
||||
<td className="adm-td">
|
||||
<span className={`badge ${ROLE_BADGE[u.role] || 'badge-editor'}`}>{u.role}</span>
|
||||
</td>
|
||||
<td className="adm-td">
|
||||
<span
|
||||
className="sans"
|
||||
style={{ fontSize: '0.82rem', color: u.status && u.status !== 'active' ? '#d98b84' : 'var(--muted)' }}
|
||||
>
|
||||
{u.status || 'active'}
|
||||
</span>
|
||||
</td>
|
||||
<td className="adm-td dim">{u.last_login_at ? dateTime(u.last_login_at) : 'never'}</td>
|
||||
<td className="adm-td" style={{ textAlign: 'right' }}>
|
||||
<span className="link-accent" onClick={() => setEditing(u)}>
|
||||
|
||||
Reference in New Issue
Block a user