diplomail (Stage B): admin/owner sends + lifecycle hooks
Item 7 of the spec wants game-state and membership-state changes to land as durable inbox entries the affected players can re-read after the fact — push alone times out of the 5-minute ring buffer. Stage B adds the admin-kind send matrix (owner-driven via /user, site-admin driven via /admin) plus the lobby lifecycle hooks: paused / cancelled emit a broadcast system mail to active members, kick / ban emit a single-recipient system mail to the affected user (which they keep read access to even after the membership row is revoked, per item 8). Migration relaxes diplomail_messages_kind_sender_chk so an owner sending kind=admin keeps sender_kind=player; the new LifecyclePublisher dep on lobby.Service is wired through a thin adapter in cmd/backend/main, mirroring how lobby's notification publisher is plumbed today. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1183,6 +1183,47 @@ paths:
|
||||
$ref: "#/components/responses/NotImplementedError"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/user/games/{game_id}/mail/admin:
|
||||
post:
|
||||
tags: [User]
|
||||
operationId: userMailSendAdmin
|
||||
summary: Send a non-replyable admin notification (owner only)
|
||||
description: |
|
||||
Owner-only: the caller must be the owner of the private game.
|
||||
`target="user"` requires `recipient_user_id`; `target="all"`
|
||||
accepts an optional `recipients` scope (`active` by default,
|
||||
plus `active_and_removed` and `all_members`). The message
|
||||
carries `kind="admin"` and is therefore non-replyable.
|
||||
security:
|
||||
- UserHeader: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/XUserID"
|
||||
- $ref: "#/components/parameters/GameID"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserMailSendAdminRequest"
|
||||
responses:
|
||||
"201":
|
||||
description: Admin message persisted; broadcasts return a fan-out receipt.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/UserMailMessageDetail"
|
||||
- $ref: "#/components/schemas/UserMailBroadcastReceipt"
|
||||
"400":
|
||||
$ref: "#/components/responses/InvalidRequestError"
|
||||
"403":
|
||||
$ref: "#/components/responses/ForbiddenError"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFoundError"
|
||||
"501":
|
||||
$ref: "#/components/responses/NotImplementedError"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/user/games/{game_id}/mail/messages/{message_id}:
|
||||
get:
|
||||
tags: [User]
|
||||
@@ -1913,6 +1954,49 @@ paths:
|
||||
$ref: "#/components/responses/NotImplementedError"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/admin/games/{game_id}/mail:
|
||||
post:
|
||||
tags: [Admin]
|
||||
operationId: adminDiplomailSend
|
||||
summary: Send a diplomatic-mail admin notification to one game
|
||||
description: |
|
||||
Site-admin send for the diplomatic-mail subsystem. Body shape
|
||||
mirrors the owner-only `POST /api/v1/user/games/{game_id}/mail/admin`
|
||||
endpoint. `target="user"` requires `recipient_user_id`;
|
||||
`target="all"` accepts an optional `recipients` scope
|
||||
(`active` / `active_and_removed` / `all_members`). The
|
||||
authenticated admin username is persisted as `sender_username`.
|
||||
security:
|
||||
- AdminBasicAuth: []
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/GameID"
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/UserMailSendAdminRequest"
|
||||
responses:
|
||||
"201":
|
||||
description: Admin message persisted; broadcasts return a fan-out receipt.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/UserMailMessageDetail"
|
||||
- $ref: "#/components/schemas/UserMailBroadcastReceipt"
|
||||
"400":
|
||||
$ref: "#/components/responses/InvalidRequestError"
|
||||
"401":
|
||||
$ref: "#/components/responses/UnauthorizedError"
|
||||
"403":
|
||||
$ref: "#/components/responses/ForbiddenError"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFoundError"
|
||||
"501":
|
||||
$ref: "#/components/responses/NotImplementedError"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
/api/v1/admin/runtimes/{game_id}:
|
||||
get:
|
||||
tags: [Admin]
|
||||
@@ -3831,6 +3915,74 @@ components:
|
||||
body:
|
||||
type: string
|
||||
description: Plain UTF-8 body. HTML is not parsed on the server.
|
||||
UserMailSendAdminRequest:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required: [target, body]
|
||||
properties:
|
||||
target:
|
||||
type: string
|
||||
enum: [user, all]
|
||||
recipient_user_id:
|
||||
type: string
|
||||
format: uuid
|
||||
description: |
|
||||
Required when `target="user"`. Identifies the recipient
|
||||
of the personal admin message; the recipient may be in
|
||||
any membership status (admin notifications can reach
|
||||
kicked players).
|
||||
recipients:
|
||||
type: string
|
||||
enum: [active, active_and_removed, all_members]
|
||||
description: |
|
||||
Optional scope when `target="all"`. Defaults to `active`.
|
||||
subject:
|
||||
type: string
|
||||
body:
|
||||
type: string
|
||||
UserMailBroadcastReceipt:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- message_id
|
||||
- game_id
|
||||
- kind
|
||||
- sender_kind
|
||||
- body
|
||||
- body_lang
|
||||
- broadcast_scope
|
||||
- created_at
|
||||
- recipient_count
|
||||
properties:
|
||||
message_id:
|
||||
type: string
|
||||
format: uuid
|
||||
game_id:
|
||||
type: string
|
||||
format: uuid
|
||||
game_name:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
enum: [personal, admin]
|
||||
sender_kind:
|
||||
type: string
|
||||
enum: [player, admin, system]
|
||||
subject:
|
||||
type: string
|
||||
body:
|
||||
type: string
|
||||
body_lang:
|
||||
type: string
|
||||
broadcast_scope:
|
||||
type: string
|
||||
enum: [single, game_broadcast, multi_game_broadcast]
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
recipient_count:
|
||||
type: integer
|
||||
minimum: 0
|
||||
UserMailMessageDetail:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
Reference in New Issue
Block a user