ui(pool): pick an account's users from its own Edit

Each account row in the Account pool card has a pencil: tick the users
whose sessions run on it (one moves off any other account, unticking sends
one back to the balancer). PUT /api/accounts/:name/users saves the set in one
transaction, audits each change and switches a Claude login into each added
user's home at once. The card-level switch button and pinned list are gone.
This commit is contained in:
2026-10-19 10:45:54 +07:00
parent 776ad6466f
commit c042adc51b
11 changed files with 305 additions and 232 deletions
+38 -1
View File
@@ -11,6 +11,8 @@
* written into the user's home right away (`applyPin`), over the pool login
* their running panes use, so those move to it without a restart.
*
* `setAccountUsers` is the Users page's Edit of one account: who runs on it.
*
* Server mode only: in local mode there are no users and nothing is pinned.
* Removing the account drops its pins (`dropAccount`); deleting the user drops
* theirs (ON DELETE CASCADE, and `auth.deleteUser`).
@@ -81,6 +83,41 @@ function setPin(userId, tool, account) {
return { userId: user.id, username: user.username, account: name };
}
/**
* The Users page's Edit of one account: exactly `userIds` run on `account`
* afterwards. A user pinned to another account of the tool moves here; one
* pinned here but left out goes back to the balancer. One transaction.
* Returns `{added, removed}` (each `[{userId, username}]`).
*/
function setAccountUsers(tool, account, userIds) {
validTool(tool);
const name = String(account ?? "");
if (!poolOf(tool).hasAccount(name)) {
throw err("ENOACCOUNT", `no ${tool} account ${name} in the pool`);
}
if (!Array.isArray(userIds) || !userIds.every(Number.isInteger)) {
throw err("EBADUSER", "userIds: an array of user ids");
}
const want = [...new Set(userIds)];
const users = want.map((id) => {
const u = db().prepare("SELECT id, username FROM users WHERE id = ?").get(id);
if (!u) throw err("ENOUSER", `no user ${id}`);
return u;
});
const before = listPins(tool);
const here = new Set(before.filter((p) => p.account === name).map((p) => p.userId));
const added = users.filter((u) => !here.has(u.id));
const removed = before.filter((p) => p.account === name && !want.includes(p.userId));
db().transaction(() => {
for (const u of added) setPin(u.id, tool, name);
for (const p of removed) setPin(p.userId, tool, null);
})();
return {
added: added.map((u) => ({ userId: u.id, username: u.username })),
removed: removed.map((p) => ({ userId: p.userId, username: p.username })),
};
}
/**
* Make a pin take effect now where it can: a Claude login is written into
* the user's home at once (claude-pool `switchHome`), so their running panes
@@ -104,4 +141,4 @@ function dropAccount(tool, account) {
}
}
module.exports = { TOOLS, pinFor, listPins, setPin, applyPin, dropAccount };
module.exports = { TOOLS, pinFor, listPins, setPin, setAccountUsers, applyPin, dropAccount };