feat(account): unlink game accounts on the shard before an account is deleted (MODULE_API 1.20.0)
All checks were successful
PR Checks / server-tests (pull_request) Successful in 1m0s
PR Checks / client-build (pull_request) Successful in 1m0s
PR Checks / frozen-manifest (pull_request) Successful in 1m20s

ACCOUNT_DELETION.md §4.4 (AD10, AD11). api.onUserDeleting queues every linked
account in the new shard_pending_unlinks table, then calls the sidecar's
DELETE /link/{account} (actor web:account-deletion) and drops the mirror on 200/404.
A shard that is away leaves the row pending, retried on WS reconnect and on a live
server.hello; a protected account (403) is refused and listed on Admin -> Shard as
still linked on the shard. A live mob.login without the deleted user's webId clears
the row. coreApi ^1.20.0; core-ref pinned to website#232's merge (7bd93b8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-29 20:38:33 -05:00
parent 6246383962
commit 582a55dde5
16 changed files with 630 additions and 9 deletions

View File

@@ -27,6 +27,10 @@
-- marker, and deleting it would re-arm a protocol bump against tables this
-- file has just dropped.
-- Account deletion's queue (MODULE_API 1.20.0), the newest table. No foreign key:
-- its rows outlive the users they name, by design.
DROP TABLE IF EXISTS `shard_pending_unlinks`;
-- The Asset Bridge's three (phase 3). No foreign keys of their own, so they lead:
-- `shard_creature_bodies.slug` mirrors an atlas slug and `shard_assets.body` a body
-- id, but neither is declared as a constraint — the atlas tables are rebuilt from

View File

@@ -974,3 +974,27 @@ INSERT IGNORE INTO settings (`key`, value) VALUES ('uo_link_protocol_8_migrated'
-- applied to an empty database. That is the same blind spot the protocol-pin block
-- above records, two phases running.
ALTER TABLE shard_spawn_points ADD COLUMN IF NOT EXISTS unique_id VARCHAR(64) NULL;
-- Game-account unlinks a website account's deletion could not finish
-- (MODULE_API 1.20.0; ACCOUNT_DELETION.md §4.4, AD11). `utils/accountDeletion.js`
-- queues every linked account BEFORE it asks the shard, and drops the row when the
-- shard answers 200 or 404.
-- pending the shard was away (503, no sidecar, a timeout): retried when the
-- bridge reconnects and on a live server.hello
-- refused a protected staff account (403): never retried; Admin → Shard lists
-- it as still linked on the shard, for the shard's staff to clear
-- Either goes once a live mob.login on the account carries no webId, or another.
--
-- **No foreign key, deliberately.** `web_user_id` names a user who is gone by the
-- time the row matters; it is the id the shard still reports, kept so a login can
-- show the tag has gone.
CREATE TABLE IF NOT EXISTS shard_pending_unlinks (
account VARCHAR(120) NOT NULL PRIMARY KEY,
web_user_id INT NOT NULL,
state VARCHAR(16) NOT NULL DEFAULT 'pending',
queued_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
attempts INT NOT NULL DEFAULT 0,
last_attempt_at DATETIME NULL,
last_error VARCHAR(255) NULL,
INDEX idx_shard_pending_unlinks_state (state)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;