mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 14:41:28 -07:00
[api] msg delete
This commit is contained in:
@@ -213,6 +213,15 @@ export const SendMultipleMessagesRequest = z.object({
|
||||
Data: z.string().optional().describe('The message payload; often empty'),
|
||||
})
|
||||
|
||||
/**
|
||||
* `POST /api/messages/v3/delete` JSON body — the messages the client is dropping from
|
||||
* its inbox. Ids are the `Id` of a stored message, which this server has never issued:
|
||||
* with no message store the list is only ever echoed back as accepted.
|
||||
*/
|
||||
export const DeleteMessagesRequest = z.object({
|
||||
MessageIds: z.array(z.int()).describe('Ids of the messages to delete'),
|
||||
})
|
||||
|
||||
/**
|
||||
* `POST /api/messages/v1/friendOnlineStatus` — how many of the caller's friends are
|
||||
* online, wrapped in the client's `{ success, value }` envelope.
|
||||
|
||||
@@ -22,6 +22,7 @@ import { authedId, unauthorized } from '../http'
|
||||
import {
|
||||
AckResponse,
|
||||
AUTHED,
|
||||
DeleteMessagesRequest,
|
||||
ErrorResponse,
|
||||
form,
|
||||
FriendOnlineCountResponse,
|
||||
@@ -624,6 +625,28 @@ export const socialRoutes = new Hono<App>({ strict: false })
|
||||
}),
|
||||
(c) => c.json([])
|
||||
)
|
||||
// The inbox's delete button. A POST rather than a DELETE because the ids arrive as
|
||||
// a JSON array body — the client batches a multi-select into one call, and its HTTP
|
||||
// layer only ever sends a body on POST/PUT.
|
||||
.post(
|
||||
'/api/messages/v3/delete',
|
||||
describeRoute({
|
||||
tags: ['Social'],
|
||||
summary: 'Delete messages',
|
||||
description:
|
||||
'Drops the given messages from the caller’s inbox. Nothing happens: there is no ' +
|
||||
'message store behind `GET /api/messages/v2/get` (the `MessageReceived` ' +
|
||||
'notification is the whole delivery — see `POST /api/messages/v2/send`), so ' +
|
||||
'there are no ids to match and nothing to remove.\n\n' +
|
||||
'Accepted unconditionally and answered 200 with an empty body, which is what the ' +
|
||||
'client wants: it removes the rows locally and re-reads the empty list either ' +
|
||||
'way. Not auth-gated, for the same reason `GET /api/messages/v2/get` isn’t — ' +
|
||||
'the call reaches no state to protect.',
|
||||
requestBody: jsonBody(DeleteMessagesRequest, 'The messages to delete'),
|
||||
responses: { 200: { description: 'Accepted (empty body)' } },
|
||||
}),
|
||||
(c) => c.body(null, 200)
|
||||
)
|
||||
// How many of the caller's friends are online — the friends panel's header count.
|
||||
// Answered from the friend graph joined to live presence, so it agrees with the
|
||||
// friends the panel then lists. Auth-gated: the count is the CALLER's own.
|
||||
|
||||
@@ -3418,6 +3418,20 @@ describe('messages', () => {
|
||||
expect(res.status).toBe(401)
|
||||
expect(await pushed()).toEqual([])
|
||||
})
|
||||
|
||||
test('POST /api/messages/v3/delete accepts anything with an empty 200', async () => {
|
||||
// No message store, so no id can be real and nothing is gated — an unknown id, an
|
||||
// empty list and a missing body all land the same way.
|
||||
for (const body of [{ MessageIds: [1787377235629] }, { MessageIds: [] }, {}]) {
|
||||
const res = await exports.default.fetch(`${ORIGIN}/api/messages/v3/delete`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
expect(res.status).toBe(200)
|
||||
expect(await res.text()).toBe('')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('mutual friends', () => {
|
||||
@@ -4837,6 +4851,7 @@ describe('openapi', () => {
|
||||
'POST /api/messages/v1/friendOnlineStatus',
|
||||
'POST /api/messages/v1/sendMultiple',
|
||||
'POST /api/messages/v2/send',
|
||||
'POST /api/messages/v3/delete',
|
||||
'POST /api/playerReputation/v1/bulk',
|
||||
'POST /api/playerReputation/v2/bulk',
|
||||
'POST /api/playerevents/v1/bulkInvite',
|
||||
|
||||
Reference in New Issue
Block a user