ACCOUNT_DELETION.md phase 2 (core): a member deletes their own account on
the website, and the admin route runs the same procedure.
- DELETE /api/v1/auth/me/account: typed username, password always (AD7),
and a 2FA or recovery code when 2FA is on. A wrong password or code is
400, not 401 (AD12), so the app's refresh-on-401 never replays it.
409 password_required and last_admin; the demo's shared logins are
locked.
- auth/accountDeletion.js: module hooks first, then one transaction that
reserves a staff username (reserved_usernames, AD9) and deletes the row,
then a Teams reconcile, the audit entry (no username) and a notice to
the old address (new protected template auth.account-deleted).
- MODULE_API 1.20.0: api.onUserDeleting({ userId, deletedBy }), before the
delete, 10 s limit, cannot veto (AD10, AD11).
- utils/displayAuthor.js: a deleted account's username snapshots read
"[deleted player]" to members and the stored name, marked, to staff
(AD5, AD6). Used by the forum, the Reports queue, the guest list and
the Team digest. A deleted account drops off Forum guests (AD13).
- Reserved names refuse at every username write, answered as "taken".
- Web: Delete my account on the player and staff Account pages; staff
views mark "(deleted account)".
- OpenAPI regenerated; route manifest and engagement manifest regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
52 lines
1.7 KiB
JavaScript
52 lines
1.7 KiB
JavaScript
import { useEffect, useState } from 'react'
|
|
import { onDemoLocked } from '../lib/demoNotice.js'
|
|
import { onNotice } from '../lib/notice.js'
|
|
|
|
// The one toast for a write the public demo refused (DEMO_MODE.md §3.1). Fed
|
|
// by api/client.js through lib/demoNotice.js, so every screen — and every
|
|
// installed module's screens — gets it without handling the code itself. On a
|
|
// real shard nothing is ever refused that way, so it never appears.
|
|
const SHOW_MS = 5000
|
|
|
|
export default function DemoToast() {
|
|
const [message, setMessage] = useState(null)
|
|
|
|
useEffect(() => onDemoLocked((text) => setMessage(text)), [])
|
|
// Also the site's one passing notice (lib/notice.js), on any deployment: the
|
|
// account page's "Your account has been deleted." after it has navigated home.
|
|
useEffect(() => onNotice((text) => setMessage(text)), [])
|
|
|
|
useEffect(() => {
|
|
if (!message) return undefined
|
|
const t = setTimeout(() => setMessage(null), SHOW_MS)
|
|
return () => clearTimeout(t)
|
|
}, [message])
|
|
|
|
return (
|
|
<div
|
|
role="status"
|
|
aria-live="polite"
|
|
style={{ position: 'fixed', left: 16, right: 16, bottom: 20, zIndex: 1000, display: 'flex', justifyContent: 'center', pointerEvents: 'none' }}
|
|
>
|
|
{message && (
|
|
<div
|
|
className="sans"
|
|
style={{
|
|
maxWidth: 480,
|
|
padding: '11px 16px',
|
|
borderRadius: 10,
|
|
fontSize: '0.88rem',
|
|
color: 'var(--head)',
|
|
background: 'var(--bg-deep)',
|
|
border: '1px solid var(--accent)',
|
|
boxShadow: '0 12px 32px rgba(0,0,0,0.45)',
|
|
pointerEvents: 'auto',
|
|
}}
|
|
>
|
|
{message}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|