diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index 6b55e90783..3f90040ffe 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -142,6 +142,8 @@ paths: $ref: 'write/chats/roomId.yaml' /chats/{roomId}/users: $ref: 'write/chats/roomId/users.yaml' + /chats/{roomId}/users/{uid}: + $ref: 'write/chats/roomId/users/uid.yaml' /chats/{roomId}/{mid}: $ref: 'write/chats/roomId/mid.yaml' /flags/: diff --git a/public/openapi/write/chats/roomId/users.yaml b/public/openapi/write/chats/roomId/users.yaml index 2845338c57..150bccfab5 100644 --- a/public/openapi/write/chats/roomId/users.yaml +++ b/public/openapi/write/chats/roomId/users.yaml @@ -46,13 +46,52 @@ post: uids: type: array description: A list of valid uids - example: [2] + example: [2, 4] items: type: number description: A valid uid responses: '200': description: users successfully invited to chat room + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../components/schemas/Status.yaml#/Status + response: + $ref: ../../../components/schemas/Chats.yaml#/RoomUserList +delete: + tags: + - chats + summary: remove users from chat room + description: This operation removes (kicks) a user from a chat room + parameters: + - in: path + name: roomId + schema: + type: number + required: true + description: a valid chat room id + example: 1 + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + uids: + type: array + description: A list of valid uids + example: [2] + items: + type: number + description: A valid uid + responses: + '200': + description: users successfully removed from chat room content: application/json: schema: diff --git a/public/openapi/write/chats/roomId/users/uid.yaml b/public/openapi/write/chats/roomId/users/uid.yaml new file mode 100644 index 0000000000..96b31eb907 --- /dev/null +++ b/public/openapi/write/chats/roomId/users/uid.yaml @@ -0,0 +1,32 @@ +delete: + tags: + - chats + summary: remove one user from chat room + description: This operation removes (kicks) a single user from a chat room + parameters: + - in: path + name: roomId + schema: + type: number + required: true + description: a valid chat room id + example: 1 + - in: path + name: uid + schema: + type: number + required: true + description: a valid user id + example: 4 + responses: + '200': + description: user successfully removed from chat room + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../../components/schemas/Status.yaml#/Status + response: + $ref: ../../../../components/schemas/Chats.yaml#/RoomUserList \ No newline at end of file diff --git a/public/src/client/chats.js b/public/src/client/chats.js index 5ecee2bfdb..29e89aefe7 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -264,16 +264,9 @@ define('forum/chats', [ modal.on('click', '[data-action="kick"]', function () { const uid = parseInt(this.getAttribute('data-uid'), 10); - socket.emit('modules.chats.removeUserFromRoom', { - roomId: roomId, - uid: uid, - }, function (err) { - if (err) { - return alerts.error(err); - } - - Chats.refreshParticipantsList(roomId, modal); - }); + api.delete(`/chats/${roomId}/users/${uid}`, {}).then((body) => { + Chats.refreshParticipantsList(roomId, modal, body); + }).catch(alerts.error); }); }; diff --git a/src/api/chats.js b/src/api/chats.js index 81c79e2841..4f8660ea2a 100644 --- a/src/api/chats.js +++ b/src/api/chats.js @@ -101,3 +101,15 @@ chatsAPI.invite = async (caller, data) => { delete data.uids; return chatsAPI.users(caller, data); }; + +chatsAPI.kick = async (caller, data) => { + const uidsExist = await user.exists(data.uids); + if (!uidsExist.every(Boolean)) { + throw new Error('[[error:no-user]]'); + } + + await messaging.removeUsersFromRoom(caller.uid, data.uids, data.roomId); + + delete data.uids; + return chatsAPI.users(caller, data); +}; diff --git a/src/controllers/write/chats.js b/src/controllers/write/chats.js index 0f2b554ac7..999eccb220 100644 --- a/src/controllers/write/chats.js +++ b/src/controllers/write/chats.js @@ -70,7 +70,22 @@ Chats.invite = async (req, res) => { }; Chats.kick = async (req, res) => { - // ... + const users = await api.chats.kick(req, { + ...req.body, + roomId: req.params.roomId, + }); + + helpers.formatApiResponse(200, res, users); +}; + +Chats.kickUser = async (req, res) => { + req.body.uids = [req.params.uid]; + const users = await api.chats.kick(req, { + ...req.body, + roomId: req.params.roomId, + }); + + helpers.formatApiResponse(200, res, users); }; Chats.messages = {}; diff --git a/src/routes/write/chats.js b/src/routes/write/chats.js index b1b62417d5..a211270c71 100644 --- a/src/routes/write/chats.js +++ b/src/routes/write/chats.js @@ -21,7 +21,8 @@ module.exports = function () { setupApiRoute(router, 'get', '/:roomId/users', [...middlewares, middleware.assert.room], controllers.write.chats.users); setupApiRoute(router, 'post', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.invite); - // setupApiRoute(router, 'delete', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.kick); + setupApiRoute(router, 'delete', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.kick); + setupApiRoute(router, 'delete', '/:roomId/users/:uid', [...middlewares, middleware.assert.room, middleware.assert.user], controllers.write.chats.kickUser); setupApiRoute(router, 'get', '/:roomId/:mid', [...middlewares, middleware.assert.room, middleware.assert.message], controllers.write.chats.messages.get); setupApiRoute(router, 'put', '/:roomId/:mid', [...middlewares, middleware.assert.room, middleware.assert.message], controllers.write.chats.messages.edit); diff --git a/src/socket.io/modules.js b/src/socket.io/modules.js index a199069ea4..0b6be7e34b 100644 --- a/src/socket.io/modules.js +++ b/src/socket.io/modules.js @@ -117,15 +117,17 @@ SocketModules.chats.addUserToRoom = async function (socket, data) { }; SocketModules.chats.removeUserFromRoom = async function (socket, data) { + sockets.warnDeprecated(socket, 'DELETE /api/v3/chats/:roomId/users OR DELETE /api/v3/chats/:roomId/users/:uid'); + if (!data || !data.roomId) { throw new Error('[[error:invalid-data]]'); } - const exists = await user.exists(data.uid); - if (!exists) { - throw new Error('[[error:no-user]]'); - } - await Messaging.removeUsersFromRoom(socket.uid, [data.uid], data.roomId); + // Revised API can accept multiple uids now + data.uids = [data.uid]; + delete data.uid; + + await api.chats.kick(socket, data); }; SocketModules.chats.leave = async function (socket, roomid) { diff --git a/test/api.js b/test/api.js index 64857041f2..97d19470f6 100644 --- a/test/api.js +++ b/test/api.js @@ -390,6 +390,7 @@ describe('API', async () => { try { if (type === 'json') { + console.log(`calling ${url}`); response = await request(url, { method: method, jar: !unauthenticatedRoutes.includes(path) ? jar : undefined, @@ -418,7 +419,12 @@ describe('API', async () => { it('response status code should match one of the schema defined responses', () => { // HACK: allow HTTP 418 I am a teapot, for now 👇 - assert(context[method].responses.hasOwnProperty('418') || Object.keys(context[method].responses).includes(String(response.statusCode)), `${method.toUpperCase()} ${path} sent back unexpected HTTP status code: ${response.statusCode}`); + try { + assert(context[method].responses.hasOwnProperty('418') || Object.keys(context[method].responses).includes(String(response.statusCode)), `${method.toUpperCase()} ${path} sent back unexpected HTTP status code: ${response.statusCode}`); + } catch (e) { + console.log(response.body); + throw e; + } }); // Recursively iterate through schema properties, comparing type