mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 02:55:58 +01:00 
			
		
		
		
	feat: POST /api/v3/chats, chat room creation, plus openAPI docs update
				
					
				
			This commit is contained in:
		| @@ -1,6 +1,7 @@ | |||||||
| { | { | ||||||
| 	"invalid-data": "Invalid Data", | 	"invalid-data": "Invalid Data", | ||||||
| 	"invalid-json": "Invalid JSON", | 	"invalid-json": "Invalid JSON", | ||||||
|  | 	"array-expected": "A value of type Array was expected for property `%1`, but %2 was received instead", | ||||||
|  |  | ||||||
| 	"not-logged-in": "You don't seem to be logged in.", | 	"not-logged-in": "You don't seem to be logged in.", | ||||||
| 	"account-locked": "Your account has been locked temporarily", | 	"account-locked": "Your account has been locked temporarily", | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								public/openapi/components/schemas/Chats.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								public/openapi/components/schemas/Chats.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | RoomObject: | ||||||
|  |   type: object | ||||||
|  |   properties: | ||||||
|  |     owner: | ||||||
|  |       type: number | ||||||
|  |       description: the uid of the chat room owner (usually the user who created the room initially) | ||||||
|  |     roomId: | ||||||
|  |       type: string | ||||||
|  |       description: unique identifier for the chat room | ||||||
|  |     roomName: | ||||||
|  |       type: string | ||||||
|  |     groupChat: | ||||||
|  |       type: boolean | ||||||
|  |       description: whether the chat room is a group chat or not | ||||||
| @@ -19,7 +19,7 @@ info: | |||||||
|     # Authentication |     # Authentication | ||||||
|  |  | ||||||
|     Please see the ["Authentication" section under the Read API](../read/#section/Overview/Authentication) for more information on how to authenticate against this API in order to make calls. |     Please see the ["Authentication" section under the Read API](../read/#section/Overview/Authentication) for more information on how to authenticate against this API in order to make calls. | ||||||
|   version: 1.15.0 |   version: 1.19.0 | ||||||
|   contact: |   contact: | ||||||
|     email: support@nodebb.org |     email: support@nodebb.org | ||||||
|   license: |   license: | ||||||
| @@ -39,6 +39,8 @@ tags: | |||||||
|     description: Topic-based calls (create, modify, delete, etc.) |     description: Topic-based calls (create, modify, delete, etc.) | ||||||
|   - name: posts |   - name: posts | ||||||
|     description: Individual post-related calls (create, modify, delete, etc.) |     description: Individual post-related calls (create, modify, delete, etc.) | ||||||
|  |   - name: chats | ||||||
|  |     description: Calls related to the user private messaging system | ||||||
|   - name: admin |   - name: admin | ||||||
|     description: Administrative calls |     description: Administrative calls | ||||||
|   - name: files |   - name: files | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								public/openapi/write/chats.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								public/openapi/write/chats.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | post: | ||||||
|  |   tags: | ||||||
|  |     - chats | ||||||
|  |   summary: create a chat room | ||||||
|  |   description: This operation creates a new chat room and adds users to the room, if provided. | ||||||
|  |   requestBody: | ||||||
|  |     required: true | ||||||
|  |     content: | ||||||
|  |       application/json: | ||||||
|  |         schema: | ||||||
|  |           type: object | ||||||
|  |           properties: | ||||||
|  |             uids: | ||||||
|  |               type: array | ||||||
|  |               example: [2, 3] | ||||||
|  |           required: | ||||||
|  |             - uids | ||||||
|  |   responses: | ||||||
|  |     '200': | ||||||
|  |       description: chat room successfully created | ||||||
|  |       content: | ||||||
|  |         application/json: | ||||||
|  |           schema: | ||||||
|  |             type: object | ||||||
|  |             properties: | ||||||
|  |               status: | ||||||
|  |                 $ref: ../components/schemas/Status.yaml#/Status | ||||||
|  |               response: | ||||||
|  |                 $ref: ../components/schemas/Chats.yaml#/RoomObject | ||||||
							
								
								
									
										37
									
								
								src/api/chats.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/api/chats.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const meta = require('../meta'); | ||||||
|  | const privileges = require('../privileges'); | ||||||
|  | const messaging = require('../messaging'); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | const websockets = require('../socket.io'); | ||||||
|  | const socketHelpers = require('../socket.io/helpers'); | ||||||
|  |  | ||||||
|  | const chatsAPI = module.exports; | ||||||
|  |  | ||||||
|  | function rateLimitExceeded(caller) { | ||||||
|  | 	const session = caller.request ? caller.request.session : caller.session;	// socket vs req | ||||||
|  | 	const now = Date.now(); | ||||||
|  | 	session.lastChatMessageTime = session.lastChatMessageTime || 0; | ||||||
|  | 	if (now - session.lastChatMessageTime < meta.config.chatMessageDelay) { | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 	session.lastChatMessageTime = now; | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | chatsAPI.create = async function (caller, data) { | ||||||
|  | 	if (rateLimitExceeded(caller)) { | ||||||
|  | 		throw new Error('[[error:too-many-messages]]'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!data.uids || !Array.isArray(data.uids)) { | ||||||
|  | 		throw new Error(`[[error:array-expected, uids, ${typeof data.uids}]]`); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	await Promise.all(data.uids.map(async uid => messaging.canMessageUser(caller.uid, uid))); | ||||||
|  | 	const roomId = await messaging.newRoom(caller.uid, data.uids); | ||||||
|  |  | ||||||
|  | 	return await messaging.getRoomData(roomId); | ||||||
|  | }; | ||||||
| @@ -5,6 +5,7 @@ module.exports = { | |||||||
| 	groups: require('./groups'), | 	groups: require('./groups'), | ||||||
| 	topics: require('./topics'), | 	topics: require('./topics'), | ||||||
| 	posts: require('./posts'), | 	posts: require('./posts'), | ||||||
|  | 	chats: require('./chats'), | ||||||
| 	categories: require('./categories'), | 	categories: require('./categories'), | ||||||
| 	flags: require('./flags'), | 	flags: require('./flags'), | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -18,7 +18,8 @@ Chats.list = async (req, res) => { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| Chats.create = async (req, res) => { | Chats.create = async (req, res) => { | ||||||
| 	// ... | 	const roomObj = await api.chats.create(req, req.body); | ||||||
|  | 	helpers.formatApiResponse(200, res, roomObj); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| Chats.exists = async (req, res) => { | Chats.exists = async (req, res) => { | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ module.exports = function () { | |||||||
| 	const middlewares = [middleware.ensureLoggedIn, middleware.canChat]; | 	const middlewares = [middleware.ensureLoggedIn, middleware.canChat]; | ||||||
|  |  | ||||||
| 	setupApiRoute(router, 'get', '/', [...middlewares], controllers.write.chats.list); | 	setupApiRoute(router, 'get', '/', [...middlewares], controllers.write.chats.list); | ||||||
| 	// setupApiRoute(router, 'post', '/', [...middlewares, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.create); | 	setupApiRoute(router, 'post', '/', [...middlewares, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.create); | ||||||
|  |  | ||||||
| 	setupApiRoute(router, 'head', '/:roomId', [...middlewares, middleware.assert.room], controllers.write.chats.exists); | 	setupApiRoute(router, 'head', '/:roomId', [...middlewares, middleware.assert.room], controllers.write.chats.exists); | ||||||
| 	// setupApiRoute(router, 'get', '/:roomId', [...middlewares, middleware.assert.room], controllers.write.chats.get); | 	// setupApiRoute(router, 'get', '/:roomId', [...middlewares, middleware.assert.room], controllers.write.chats.get); | ||||||
|   | |||||||
| @@ -12,6 +12,9 @@ const server = require('./index'); | |||||||
| const user = require('../user'); | const user = require('../user'); | ||||||
| const privileges = require('../privileges'); | const privileges = require('../privileges'); | ||||||
|  |  | ||||||
|  | const sockets = require('.'); | ||||||
|  | const api = require('../api'); | ||||||
|  |  | ||||||
| const SocketModules = module.exports; | const SocketModules = module.exports; | ||||||
|  |  | ||||||
| SocketModules.chats = {}; | SocketModules.chats = {}; | ||||||
| @@ -43,20 +46,16 @@ SocketModules.chats.isDnD = async function (socket, uid) { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| SocketModules.chats.newRoom = async function (socket, data) { | SocketModules.chats.newRoom = async function (socket, data) { | ||||||
|  | 	sockets.warnDeprecated(socket, 'POST /api/v3/chats'); | ||||||
|  |  | ||||||
| 	if (!data) { | 	if (!data) { | ||||||
| 		throw new Error('[[error:invalid-data]]'); | 		throw new Error('[[error:invalid-data]]'); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (rateLimitExceeded(socket)) { | 	const roomObj = await api.chats.create(socket, { | ||||||
| 		throw new Error('[[error:too-many-messages]]'); | 		uids: [data.touid], | ||||||
| 	} | 	}); | ||||||
|  | 	return roomObj.roomId; | ||||||
| 	const canChat = await privileges.global.can('chat', socket.uid); |  | ||||||
| 	if (!canChat) { |  | ||||||
| 		throw new Error('[[error:no-privileges]]'); |  | ||||||
| 	} |  | ||||||
| 	await Messaging.canMessageUser(socket.uid, data.touid); |  | ||||||
| 	return await Messaging.newRoom(socket.uid, [data.touid]); |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| SocketModules.chats.send = async function (socket, data) { | SocketModules.chats.send = async function (socket, data) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user