docs(website): document account deletion core and MODULE_API 1.20.0
ACCOUNT_DELETION.md §6 step 2 is built (website feature/account-deletion). - MODULE_API.md: 1.20.0, api.onUserDeleting (§1.1, §1.1 history, §2.4). - BACKEND_DESIGN.md: reserved_usernames, DELETE /auth/me/account, the admin delete now running the same procedure, the demo lock row, and §6.6 on the procedure, the display rule and what goes and what stays. - TEAMS.md: the forum's deleted-author rule is now "[deleted player]" for members and the stored name, marked, for staff. - ACCOUNT_DELETION.md: AD12 (wrong password or code is 400, not 401) and AD13 (a deleted account drops off Forum guests), both asked of the org lead while building; §4.1 refusals carry codes; step 2 marked built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -26,13 +26,33 @@ here extends the contract first, in this file, before the module is written agai
|
||||
Core exports a single integer-major semver string from `server/src/modules/version.js`:
|
||||
|
||||
```js
|
||||
const MODULE_API_VERSION = '1.19.0'
|
||||
const MODULE_API_VERSION = '1.20.0'
|
||||
```
|
||||
|
||||
The client half carries the same number (`client/src/modules/version.js`) and a test asserts the two
|
||||
agree. Duplicated rather than fetched because the value has to be on `window.__rg` before the first
|
||||
module chunk evaluates, which is earlier than any network round trip could answer.
|
||||
|
||||
**1.20.0 — a module hears that an account is about to be deleted** (`website/ACCOUNT_DELETION.md`
|
||||
§4.1, AD10, AD11). One optional addition and no removal, so minor.
|
||||
|
||||
- **`api.onUserDeleting(fn)`** (§2.4): `fn({ userId, deletedBy })`, where `deletedBy` is `'self'` (the
|
||||
member, from `DELETE /auth/me/account`) or `'staff'` (`DELETE /admin/users/:id`). Called **before**
|
||||
the `users` row is deleted, while the module can still read the person's links.
|
||||
- **Its job is to undo in the game what the site's rows are about to stop saying.** Unlink each game
|
||||
link through the module's own unlink, so the game sees exactly what a player's own unlink produces,
|
||||
and clean up anything the module keeps with no foreign key to `users`. A cascade is not an unlink:
|
||||
for UO the link lives on the shard, and dropping the site's mirror alone would leave the shard
|
||||
attributing the game account to a user who no longer exists.
|
||||
- **It cannot veto.** A hook that throws, or runs past core's **10 s**, is logged and the deletion goes
|
||||
ahead: a member's deletion request must be honoured, and a game server that is down must not hold it
|
||||
open. A module that cannot finish keeps its own record of what is left (UO's
|
||||
`shard_pending_unlinks`) and retries.
|
||||
- **`started`** modules only, like the identity hooks; registered once. The payload is frozen.
|
||||
- Module-uo's `^1.10.0` still resolves and registers none until its own PR: with no hook, a deletion
|
||||
is core's cascade alone, as the admin route's delete always was. `website/USER_SAFETY.md`'s
|
||||
`reportTargets` moves to 1.21.0.
|
||||
|
||||
**1.19.0 — the public demo refuses a step at save** (`website/DEMO_MODE.md` §3.4, DM18). One optional
|
||||
member on an event action, so minor.
|
||||
|
||||
@@ -552,6 +572,11 @@ branches lacks — so it is the only place `permits` is true between two values
|
||||
between `admin` and `owner`, `members` or `subscribers`. `permits`, `meet` and `meetAll` are otherwise
|
||||
unchanged, and so is every rule about composition narrowing rather than widening.
|
||||
|
||||
**1.20.0 — account deletion** (`website/ACCOUNT_DELETION.md` §4.1, AD10, AD11). `api.onUserDeleting`,
|
||||
called before an account is deleted so the module unlinks its game links; it cannot veto, and a hook
|
||||
past 10 s is logged and skipped. One optional addition, so minor; see §1.1. Module-uo's `^1.10.0`
|
||||
still resolves.
|
||||
|
||||
**1.19.0 — a step refused at save on the demo** (`website/DEMO_MODE.md` §3.4, DM18). An event action's
|
||||
optional `demoRefuses(params)`, asked at create, save and publish with `DEMO_MODE` on; one optional
|
||||
member, so minor; see §1.1. Module-uo's `^1.10.0` still resolves.
|
||||
@@ -1009,6 +1034,7 @@ api.onShutdown(async () => {})
|
||||
api.onIdentityLinking(async ({ userId, provider, subject }) => ({ ok: true })) // 1.17.0
|
||||
api.onIdentityLinked(async ({ userId, provider, subject, displayName }) => {}) // 1.17.0
|
||||
api.onDemoSeed(async (data, { accounts }) => {}) // 1.18.0
|
||||
api.onUserDeleting(async ({ userId, deletedBy }) => {}) // 1.20.0
|
||||
```
|
||||
|
||||
**Every call STAGES; nothing is committed until the module as a whole is known good.** A claim's
|
||||
@@ -1528,6 +1554,25 @@ api.onDemoSeed(async ({ servers = [], links = [] }, { accounts }) => {
|
||||
seed's own process. A fixture section for a module that registered no hook fails the seed. So does a
|
||||
hook that throws.
|
||||
|
||||
**`onUserDeleting(fn)`** (1.20.0) — the module's part in deleting an account (`ACCOUNT_DELETION.md`
|
||||
§4.1, AD10, AD11). Core calls `fn({ userId, deletedBy })` **before** the `users` row goes, from the one
|
||||
deletion procedure both routes share (`auth/accountDeletion.js`), with `deletedBy` `'self'` or
|
||||
`'staff'`.
|
||||
|
||||
```js
|
||||
api.onUserDeleting(async ({ userId }) => {
|
||||
for (const link of await links.forUser(userId)) await links.unlinkOwned(link.steamId, userId, { reason: 'account deleted' })
|
||||
})
|
||||
```
|
||||
|
||||
- **Unlink through the module's own unlink**, for every link the person holds, so the game sees what a
|
||||
player's own unlink produces, with its audit entry and any re-sync. Then clean up rows kept with no
|
||||
foreign key to `users`; rows that cascade need nothing.
|
||||
- **It cannot stop the deletion.** A throw, or a hook still running after **10 s**, is logged and core
|
||||
deletes the account anyway. Anything the module could not finish (a game server that is offline) is
|
||||
the module's to record and retry: core never calls the hook twice for one deletion.
|
||||
- `started` modules only, in registration order; registered once; the payload is frozen.
|
||||
|
||||
### 2.5 Lifecycle
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user