Let staff link their own characters + share the game-accounts UI
- Backend: /admin/shard/{link,accounts,roster/:account,vendors/:account} —
staff self-service, reusing the player/shard controller (it keys off
req.user.id, so the same handlers serve any logged-in role). Swagger under
Admin · Account; spec regenerated.
- components/GameAccounts.jsx: the link-prompt + character-roster UI extracted
into one reusable component parametrized by an api scope and a charTo(serial)
route builder.
- PlayerCharacters now renders it (player scope → /player/char/:serial).
- Admin: "My Characters" nav item + /admin/characters (AdminCharacters) and
/admin/characters/:serial (AdminCharacter, in-shell sheet), using the admin
self-service scope. api.admin.shard.* added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qPmpmVH1xGCiZoz9m9vW3
This commit is contained in:
@@ -1886,6 +1886,207 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/shard/link": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Admin · Account"
|
||||
],
|
||||
"summary": "Link an in-game account with a one-time code (self)",
|
||||
"description": "",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Linked",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ShardLinkResult"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Unknown or expired code",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error"
|
||||
},
|
||||
"502": {
|
||||
"description": "Bad Gateway"
|
||||
},
|
||||
"503": {
|
||||
"description": "Service Unavailable"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
},
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ShardLinkRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/shard/accounts": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Admin · Account"
|
||||
],
|
||||
"summary": "List the caller’s linked game accounts (self)",
|
||||
"description": "",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Linked accounts",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ShardLink"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
},
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/shard/roster/{account}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Admin · Account"
|
||||
],
|
||||
"summary": "Character roster for a linked account (self)",
|
||||
"description": "",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "account",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "A game account linked to the caller."
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Account roster",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request"
|
||||
},
|
||||
"403": {
|
||||
"description": "Account not linked to the caller",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
},
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/shard/vendors/{account}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Admin · Account"
|
||||
],
|
||||
"summary": "Player vendors for a linked account (self)",
|
||||
"description": "",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "account",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "A game account linked to the caller."
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Vendor snapshot",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request"
|
||||
},
|
||||
"403": {
|
||||
"description": "Account not linked to the caller",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Error"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"cookieAuth": []
|
||||
},
|
||||
{
|
||||
"bearerAuth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/admin/dashboard": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
Reference in New Issue
Block a user