feat(rust): permissions as event rewards — lend or give any registered permission (PLAN_REDESIGNS §12)
D342-D346 (docs#342). Two steps over the permission mirror's own tables, so no plugin, sidecar or protocol change: - rust.perm.lend, "Permissions until the event ends": a kit reward's lease, one rust_perm_run_grants row per recipient per permission, the message on one row each; change/ledger with rust.kit.entitle's revert and reconcile; budget rust.permissions. - rust.perm.give, "Permissions for good": a standing rust_perm_grants row per website user per permission, scoped to the step's server, source `event`, note "event run <run>, step <step>"; a grant already there is left alone, and a retry's own grants are counted as given; the message rides a notice-only run-grant row; irreversible/none; budget rust.permissions.kept. - Both: several permissions through core's `multiple` param (MODULE_API 1.14.0, at most 10); a required "I am sure" toggle refused unticked in the dry run and at run time, naming the permissions (D343); a permission the server's catalogue lacks refused by name; a blank message tells nobody. - rust.options.permissions: the enabled servers' catalogues, grouped by the registering plugin (by prefix when none), sorted plugin then permission without case, a row noted "Only on X" when not on every server. - Permission manager: an event-given grant shows "given by event run N, step M"; staff turn it off like any grant. - coreApi ^1.14.0; ci/core-ref.json pinned to website#213's head until it merges; engagement-triggers.json regenerated; routes.manifest.json unchanged (no new routes). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -322,6 +322,33 @@ async function insertGrant({ userId, permission, scope, source, note, grantedBy
|
||||
return { inserted: affected(result) > 0, id: result.insertId }
|
||||
}
|
||||
|
||||
/**
|
||||
* The grants some users already hold at one scope, with their source and note
|
||||
* (PLAN_REDESIGNS §12.5): `rust.perm.give` leaves a grant that is there alone,
|
||||
* and tells its own first attempt's grants from a staff member's by the note.
|
||||
*/
|
||||
async function listGrantsForUsers(userIds, scope) {
|
||||
const ids = [...new Set((userIds || []).map(Number).filter(Boolean))]
|
||||
if (!ids.length) return []
|
||||
return core.query(
|
||||
`SELECT user_id AS userId, permission, source, note
|
||||
FROM ${GRANTS}
|
||||
WHERE scope = ? AND user_id IN (${ids.map(() => '?').join(', ')})`,
|
||||
[scope, ...ids],
|
||||
)
|
||||
}
|
||||
|
||||
/** Add many grants at once, leaving any already there (§12.5). Returns how many were added. */
|
||||
async function insertGrants(rows) {
|
||||
if (!rows.length) return 0
|
||||
const result = await core.query(
|
||||
`INSERT IGNORE INTO ${GRANTS} (user_id, permission, scope, source, note, granted_by)
|
||||
VALUES ${placeholders(rows, 6)}`,
|
||||
rows.flatMap((r) => [r.userId, r.permission, r.scope, r.source, r.note || null, r.grantedBy || null]),
|
||||
)
|
||||
return affected(result)
|
||||
}
|
||||
|
||||
/** Delete a grant and the exceptions it carried, which no foreign key can reach. */
|
||||
async function deleteGrant(id) {
|
||||
const removed = affected(await core.query(`DELETE FROM ${GRANTS} WHERE id = ?`, [id])) > 0
|
||||
@@ -873,6 +900,8 @@ module.exports = {
|
||||
listGrants,
|
||||
getGrant,
|
||||
insertGrant,
|
||||
insertGrants,
|
||||
listGrantsForUsers,
|
||||
deleteGrant,
|
||||
listSteamGrants,
|
||||
getSteamGrant,
|
||||
|
||||
@@ -342,8 +342,11 @@ function buildDesired(serverId, authored) {
|
||||
const permission = normaliseName(row.permission)
|
||||
if (!permission) continue
|
||||
|
||||
// A grant an event gave for good (PLAN_REDESIGNS.md §12.5) says so, with its
|
||||
// note, so the screen can show where it came from; staff delete it like any.
|
||||
const given = row.source === 'event' ? { origin: 'event', note: row.note || '' } : {}
|
||||
for (const steamId of steamIdsByUser.get(row.userId) || []) {
|
||||
addGrant(steamId, permission, { type: 'userGrant', id: row.id, userId: row.userId, scope: row.scope })
|
||||
addGrant(steamId, permission, { type: 'userGrant', id: row.id, userId: row.userId, scope: row.scope, ...given })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user