// Game-account unlinks that a website account's deletion could not finish // (ACCOUNT_DELETION.md ยง4.4, AD11). See utils/accountDeletion.js for who writes // and who clears these rows. const { query } = require('../../core') const COLS = 'account, web_user_id, state, queued_at, attempts, last_attempt_at, last_error' // Queue an account before the first call to the shard, so a hook core abandons // at its time limit still leaves the account here. A re-queue (the same account // deleted twice is impossible, but a test or a retry is not) starts it over. const queue = ({ account, webUserId }) => query( `INSERT INTO shard_pending_unlinks (account, web_user_id, state) VALUES (?, ?, 'pending') ON DUPLICATE KEY UPDATE web_user_id = VALUES(web_user_id), state = 'pending', attempts = 0, last_attempt_at = NULL, last_error = NULL`, [account, webUserId], ) const remove = (account) => query('DELETE FROM shard_pending_unlinks WHERE account = ?', [account]) // One attempt that did not finish. `state` is `pending` (try again) or `refused` // (the shard said no for good: a protected staff account). const recordAttempt = ({ account, state, error }) => query( `UPDATE shard_pending_unlinks SET state = ?, attempts = attempts + 1, last_attempt_at = CURRENT_TIMESTAMP, last_error = ? WHERE account = ?`, [state, error ? String(error).slice(0, 255) : null, account], ) const listPending = () => query(`SELECT ${COLS} FROM shard_pending_unlinks WHERE state = 'pending' ORDER BY queued_at ASC`) const listAll = () => query(`SELECT ${COLS} FROM shard_pending_unlinks ORDER BY queued_at ASC`) // The shard just reported this account logging in with `webId` (or none). When // that is no longer the deleted user, the tag is gone and the row has nothing // left to wait for. Returns the number of rows dropped. async function clearIfNotAttributed(account, webId) { const result = await query( `DELETE FROM shard_pending_unlinks WHERE account = ? AND (? IS NULL OR web_user_id <> ?)`, [account, webId, webId], ) return Number(result && result.affectedRows) || 0 } module.exports = { queue, remove, recordAttempt, listPending, listAll, clearIfNotAttributed }