feat(pool): pin a user to one Claude/Codex pool account
An admin pins a server-mode user to one pool account per tool (account_pins; PUT /api/accounts/pins with the password step-up and audit, the Users page's Pin a user, agenthub accounts pin|unpin). The workspace owner's pin wins over the balancer whenever the pane may use the account; a pinned login still waits while another pool login holds the home.
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
* - POST /api/auth/logout (Auth)
|
||||
* - GET /api/accounts (Auth) — the Claude and Codex account pools, no keys or tokens
|
||||
* - POST /api/accounts, DELETE /api/accounts/{name} (Auth) — an admin adds/removes one
|
||||
* - PUT /api/accounts/pins (Auth) — an admin pins a user to one pool account
|
||||
* - GET /api/users (Auth) — every account's usage (admin/analyst)
|
||||
* - GET /api/users/audit (Auth) — staff reads of developers' sessions
|
||||
* - GET/PUT /api/me/prefs, POST /api/me/onboarded (Auth) — the account's own UI preferences
|
||||
@@ -30,6 +31,16 @@ const tags = [
|
||||
];
|
||||
|
||||
const schemas = {
|
||||
AccountPin: {
|
||||
type: "object",
|
||||
required: ["userId", "username", "account"],
|
||||
properties: {
|
||||
userId: { type: "integer" },
|
||||
username: { type: "string" },
|
||||
account: { type: "string", description: "The pool account's name" },
|
||||
pinnedAt: { type: "string" },
|
||||
},
|
||||
},
|
||||
UserPrefs: {
|
||||
type: "object",
|
||||
required: ["prefs", "onboardedAt"],
|
||||
@@ -633,6 +644,15 @@ const paths = {
|
||||
},
|
||||
},
|
||||
},
|
||||
pins: {
|
||||
type: "object",
|
||||
description:
|
||||
"Per tool, the users an admin pinned to one account (empty in local mode)",
|
||||
properties: {
|
||||
claude: { type: "array", items: { $ref: "#/components/schemas/AccountPin" } },
|
||||
codex: { type: "array", items: { $ref: "#/components/schemas/AccountPin" } },
|
||||
},
|
||||
},
|
||||
codex: {
|
||||
type: "array",
|
||||
description:
|
||||
@@ -706,6 +726,38 @@ const paths = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"/api/accounts/pins": {
|
||||
put: {
|
||||
tags: ["Auth"],
|
||||
operationId: "pinPoolAccount",
|
||||
summary:
|
||||
"Admin: pin one user's new sessions of a tool to one pool account (`account: null` unpins), with their own password again",
|
||||
requestBody: {
|
||||
required: true,
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["tool", "userId", "currentPassword"],
|
||||
properties: {
|
||||
tool: { type: "string", enum: ["claude", "codex"] },
|
||||
userId: { type: "integer" },
|
||||
account: { type: "string", nullable: true },
|
||||
currentPassword: { type: "string" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: { description: "`{pin: {userId, username, account}}` — `account` null when unpinned" },
|
||||
400: err("EBADTOOL"),
|
||||
403: err("EREAUTH — wrong password; EADMIN — not an admin; ELOCALMODE"),
|
||||
404: err("ENOUSER, ENOACCOUNT"),
|
||||
429: err("ETOOMANY — too many wrong passwords"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"/api/accounts/profiles": {
|
||||
get: {
|
||||
tags: ["Auth"],
|
||||
|
||||
Reference in New Issue
Block a user