mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-30 02:25:55 +01:00 
			
		
		
		
	feat(writeapi): added POST /api/v1/groups
This commit is contained in:
		| @@ -77,7 +77,7 @@ GroupFullObject: | |||||||
|   nullable: true |   nullable: true | ||||||
| GroupDataObject: | GroupDataObject: | ||||||
|   type: object |   type: object | ||||||
|   description: The response from an internal call to `Groups.getGroupData(<groupname>, [])` with **explicitly** no fields passed in |   description: The response from an internal call to `Groups.getGroupsFields(<groupname>, [])` with **explicitly** no fields passed in | ||||||
|   properties: |   properties: | ||||||
|     name: |     name: | ||||||
|       type: string |       type: string | ||||||
|   | |||||||
| @@ -364,6 +364,61 @@ paths: | |||||||
|                     $ref: '#/components/schemas/Status' |                     $ref: '#/components/schemas/Status' | ||||||
|                   response: |                   response: | ||||||
|                     $ref: '#/components/schemas/CategoryObj' |                     $ref: '#/components/schemas/CategoryObj' | ||||||
|  |   /groups/: | ||||||
|  |     post: | ||||||
|  |       tags: | ||||||
|  |         - groups | ||||||
|  |       summary: Create a new group | ||||||
|  |       description: This operation creates a new group | ||||||
|  |       requestBody: | ||||||
|  |         required: true | ||||||
|  |         content: | ||||||
|  |           application/json: | ||||||
|  |             schema: | ||||||
|  |               type: object | ||||||
|  |               properties: | ||||||
|  |                 name: | ||||||
|  |                   type: string | ||||||
|  |                 timestamp: | ||||||
|  |                   type: number | ||||||
|  |                 disableJoinRequests: | ||||||
|  |                   type: number | ||||||
|  |                   enum: [0, 1] | ||||||
|  |                 disableLeave: | ||||||
|  |                   type: number | ||||||
|  |                   enum: [0, 1] | ||||||
|  |                 hidden: | ||||||
|  |                   type: number | ||||||
|  |                   enum: [0, 1] | ||||||
|  |                 ownerUid: | ||||||
|  |                   type: number | ||||||
|  |                 private: | ||||||
|  |                   type: number | ||||||
|  |                   enum: [0, 1] | ||||||
|  |                 description: | ||||||
|  |                   type: string | ||||||
|  |                 userTitleEnabled: | ||||||
|  |                   type: number | ||||||
|  |                   enum: [0, 1] | ||||||
|  |                 createtime: | ||||||
|  |                   type: number | ||||||
|  |               required: | ||||||
|  |                 - name | ||||||
|  |             example: | ||||||
|  |               name: 'My Test Group' | ||||||
|  |               hidden: 1 | ||||||
|  |       responses: | ||||||
|  |         '200': | ||||||
|  |           description: group successfully created | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 type: object | ||||||
|  |                 properties: | ||||||
|  |                   status: | ||||||
|  |                     $ref: '#/components/schemas/Status' | ||||||
|  |                   response: | ||||||
|  |                     $ref: components/schemas/GroupObject.yaml#/GroupDataObject | ||||||
| components: | components: | ||||||
|   schemas: |   schemas: | ||||||
|     Status: |     Status: | ||||||
|   | |||||||
| @@ -37,20 +37,22 @@ define('admin/manage/groups', [ | |||||||
| 				hidden: $('#create-group-hidden').is(':checked') ? 1 : 0, | 				hidden: $('#create-group-hidden').is(':checked') ? 1 : 0, | ||||||
| 			}; | 			}; | ||||||
|  |  | ||||||
| 			socket.emit('admin.groups.create', submitObj, function (err, groupData) { | 			$.ajax({ | ||||||
| 				if (err) { | 				url: config.relative_path + '/api/v1/groups', | ||||||
| 					if (err.hasOwnProperty('message') && utils.hasLanguageKey(err.message)) { | 				method: 'post', | ||||||
| 						err = '[[admin/manage/groups:alerts.create-failure]]'; | 				data: submitObj, | ||||||
| 					} | 			}).done(function (res) { | ||||||
| 					createModalError.translateHtml(err).removeClass('hide'); | 				createModalError.addClass('hide'); | ||||||
| 				} else { | 				createGroupName.val(''); | ||||||
| 					createModalError.addClass('hide'); | 				createModal.on('hidden.bs.modal', function () { | ||||||
| 					createGroupName.val(''); | 					ajaxify.go('admin/manage/groups/' + res.response.name); | ||||||
| 					createModal.on('hidden.bs.modal', function () { | 				}); | ||||||
| 						ajaxify.go('admin/manage/groups/' + groupData.name); | 				createModal.modal('hide'); | ||||||
| 					}); | 			}).fail(function (ev) { | ||||||
| 					createModal.modal('hide'); | 				if (utils.hasLanguageKey(ev.responseJSON.status.message)) { | ||||||
|  | 					ev.responseJSON.status.message = '[[admin/manage/groups:alerts.create-failure]]'; | ||||||
| 				} | 				} | ||||||
|  | 				createModalError.translateHtml(ev.responseJSON.status.message).removeClass('hide'); | ||||||
| 			}); | 			}); | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,14 +11,16 @@ define('forum/groups/list', ['forum/infinitescroll', 'benchpress'], function (in | |||||||
| 		$('button[data-action="new"]').on('click', function () { | 		$('button[data-action="new"]').on('click', function () { | ||||||
| 			bootbox.prompt('[[groups:new-group.group_name]]', function (name) { | 			bootbox.prompt('[[groups:new-group.group_name]]', function (name) { | ||||||
| 				if (name && name.length) { | 				if (name && name.length) { | ||||||
| 					socket.emit('groups.create', { | 					$.ajax({ | ||||||
| 						name: name, | 						url: config.relative_path + '/api/v1/groups', | ||||||
| 					}, function (err) { | 						method: 'post', | ||||||
| 						if (!err) { | 						data: { | ||||||
| 							ajaxify.go('groups/' + utils.slugify(name)); | 							name: name, | ||||||
| 						} else { | 						}, | ||||||
| 							app.alertError(err.message); | 					}).done(function (res) { | ||||||
| 						} | 						ajaxify.go('groups/' + res.response.slug); | ||||||
|  | 					}).fail(function (ev) { | ||||||
|  | 						app.alertError(ev.responseJSON.status.message); | ||||||
| 					}); | 					}); | ||||||
| 				} | 				} | ||||||
| 			}); | 			}); | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								src/controllers/write/groups.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/controllers/write/groups.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const groups = require('../../groups'); | ||||||
|  | const events = require('../../events'); | ||||||
|  |  | ||||||
|  | const helpers = require('../helpers'); | ||||||
|  |  | ||||||
|  | const Groups = module.exports; | ||||||
|  |  | ||||||
|  | Groups.create = async (req, res) => { | ||||||
|  | 	if (typeof req.body.name !== 'string' || groups.isPrivilegeGroup(req.body.name)) { | ||||||
|  | 		throw new Error('[[error:invalid-group-name]]'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!res.locals.privileges['group:create']) { | ||||||
|  | 		throw new Error('[[error:no-privileges]]'); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	req.body.ownerUid = req.user.uid; | ||||||
|  | 	req.body.system = false; | ||||||
|  |  | ||||||
|  | 	const groupObj = await groups.create(req.body); | ||||||
|  | 	helpers.formatApiResponse(200, res, groupObj); | ||||||
|  | 	logGroupEvent(req, 'group-create', { | ||||||
|  | 		groupName: req.body.name, | ||||||
|  | 	}); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | function logGroupEvent(req, event, additional) { | ||||||
|  | 	events.log({ | ||||||
|  | 		type: event, | ||||||
|  | 		uid: req.user.uid, | ||||||
|  | 		ip: req.ip, | ||||||
|  | 		...additional, | ||||||
|  | 	}); | ||||||
|  | } | ||||||
| @@ -25,7 +25,7 @@ module.exports = function (Groups) { | |||||||
|  |  | ||||||
| 		const memberCount = data.hasOwnProperty('ownerUid') ? 1 : 0; | 		const memberCount = data.hasOwnProperty('ownerUid') ? 1 : 0; | ||||||
| 		const isPrivate = data.hasOwnProperty('private') && data.private !== undefined ? parseInt(data.private, 10) === 1 : true; | 		const isPrivate = data.hasOwnProperty('private') && data.private !== undefined ? parseInt(data.private, 10) === 1 : true; | ||||||
| 		const groupData = { | 		let groupData = { | ||||||
| 			name: data.name, | 			name: data.name, | ||||||
| 			slug: utils.slugify(data.name), | 			slug: utils.slugify(data.name), | ||||||
| 			createtime: timestamp, | 			createtime: timestamp, | ||||||
| @@ -48,8 +48,6 @@ module.exports = function (Groups) { | |||||||
| 		if (data.hasOwnProperty('ownerUid')) { | 		if (data.hasOwnProperty('ownerUid')) { | ||||||
| 			await db.setAdd('group:' + groupData.name + ':owners', data.ownerUid); | 			await db.setAdd('group:' + groupData.name + ':owners', data.ownerUid); | ||||||
| 			await db.sortedSetAdd('group:' + groupData.name + ':members', timestamp, data.ownerUid); | 			await db.sortedSetAdd('group:' + groupData.name + ':members', timestamp, data.ownerUid); | ||||||
|  |  | ||||||
| 			groupData.ownerUid = data.ownerUid; |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (!isHidden && !isSystem) { | 		if (!isHidden && !isSystem) { | ||||||
| @@ -62,6 +60,7 @@ module.exports = function (Groups) { | |||||||
|  |  | ||||||
| 		await db.setObjectField('groupslug:groupname', groupData.slug, groupData.name); | 		await db.setObjectField('groupslug:groupname', groupData.slug, groupData.name); | ||||||
|  |  | ||||||
|  | 		groupData = await Groups.getGroupData(groupData.name); | ||||||
| 		plugins.fireHook('action:group.create', { group: groupData }); | 		plugins.fireHook('action:group.create', { group: groupData }); | ||||||
| 		return groupData; | 		return groupData; | ||||||
| 	}; | 	}; | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| const user = require('../user'); | const user = require('../user'); | ||||||
|  | const privileges = require('../privileges'); | ||||||
| const utils = require('../utils'); | const utils = require('../utils'); | ||||||
|  |  | ||||||
| module.exports = function (middleware) { | module.exports = function (middleware) { | ||||||
| @@ -37,4 +38,10 @@ module.exports = function (middleware) { | |||||||
| 		res.locals.privileges = hash; | 		res.locals.privileges = hash; | ||||||
| 		return next(); | 		return next(); | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
|  | 	middleware.exposePrivilegeSet = async (req, res, next) => { | ||||||
|  | 		// Exposes a user's global privilege set | ||||||
|  | 		res.locals.privileges = await privileges.global.get(req.user.uid); | ||||||
|  | 		return next(); | ||||||
|  | 	}; | ||||||
| }; | }; | ||||||
|   | |||||||
							
								
								
									
										72
									
								
								src/routes/write/groups.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/routes/write/groups.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const router = require('express').Router(); | ||||||
|  | const middleware = require('../../middleware'); | ||||||
|  | const controllers = require('../../controllers'); | ||||||
|  | const routeHelpers = require('../helpers'); | ||||||
|  |  | ||||||
|  | const setupApiRoute = routeHelpers.setupApiRoute; | ||||||
|  |  | ||||||
|  | module.exports = function () { | ||||||
|  | 	const middlewares = [middleware.authenticate]; | ||||||
|  |  | ||||||
|  | 	setupApiRoute(router, '/', middleware, [...middlewares, middleware.checkRequired.bind(null, ['name']), middleware.exposePrivilegeSet], 'post', controllers.write.groups.create); | ||||||
|  |  | ||||||
|  | 	// app.delete('/:slug', apiMiddleware.requireUser, middleware.exposeGroupName, apiMiddleware.validateGroup, apiMiddleware.requireGroupOwner, function(req, res) { | ||||||
|  | 	// 	Groups.destroy(res.locals.groupName, function(err) { | ||||||
|  | 	// 		errorHandler.handle(err, res); | ||||||
|  | 	// 	}); | ||||||
|  | 	// }); | ||||||
|  |  | ||||||
|  | 	// app.put('/:slug/membership', apiMiddleware.requireUser, middleware.exposeGroupName, apiMiddleware.validateGroup, function(req, res) { | ||||||
|  | 	// 	if (Meta.config.allowPrivateGroups !== '0') { | ||||||
|  | 	// 		Groups.isPrivate(res.locals.groupName, function(err, isPrivate) { | ||||||
|  | 	// 			if (isPrivate) { | ||||||
|  | 	// 				Groups.requestMembership(res.locals.groupName, req.user.uid, function(err) { | ||||||
|  | 	// 					errorHandler.handle(err, res); | ||||||
|  | 	// 				}); | ||||||
|  | 	// 			} else { | ||||||
|  | 	// 				Groups.join(res.locals.groupName, req.user.uid, function(err) { | ||||||
|  | 	// 					errorHandler.handle(err, res); | ||||||
|  | 	// 				}); | ||||||
|  | 	// 			} | ||||||
|  | 	// 		}); | ||||||
|  | 	// 	} else { | ||||||
|  | 	// 		Groups.join(res.locals.groupName, req.user.uid, function(err) { | ||||||
|  | 	// 			errorHandler.handle(err, res); | ||||||
|  | 	// 		}); | ||||||
|  | 	// 	} | ||||||
|  | 	// }); | ||||||
|  |  | ||||||
|  | 	// app.put('/:slug/membership/:uid', middleware.exposeGroupName, apiMiddleware.validateGroup, apiMiddleware.requireUser, apiMiddleware.requireAdmin, function(req, res) { | ||||||
|  | 	// 	Groups.join(res.locals.groupName, req.params.uid, function(err) { | ||||||
|  | 	// 		errorHandler.handle(err, res); | ||||||
|  | 	// 	}); | ||||||
|  | 	// }); | ||||||
|  |  | ||||||
|  | 	// app.delete('/:slug/membership', apiMiddleware.requireUser, middleware.exposeGroupName, apiMiddleware.validateGroup, function(req, res) { | ||||||
|  | 	// 	Groups.isMember(req.user.uid, res.locals.groupName, function(err, isMember) { | ||||||
|  | 	// 		if (isMember) { | ||||||
|  | 	// 			Groups.leave(res.locals.groupName, req.user.uid, function(err) { | ||||||
|  | 	// 				errorHandler.handle(err, res); | ||||||
|  | 	// 			}); | ||||||
|  | 	// 		} else { | ||||||
|  | 	// 			errorHandler.respond(400, res); | ||||||
|  | 	// 		} | ||||||
|  | 	// 	}); | ||||||
|  | 	// }); | ||||||
|  |  | ||||||
|  | 	// app.delete('/:slug/membership/:uid', middleware.exposeGroupName, apiMiddleware.validateGroup, apiMiddleware.requireUser, apiMiddleware.requireAdmin, function(req, res) { | ||||||
|  | 	//     Groups.isMember(req.params.uid, res.locals.groupName, function(err, isMember) { | ||||||
|  | 	//         if (isMember) { | ||||||
|  | 	//             Groups.leave(res.locals.groupName, req.params.uid, function(err) { | ||||||
|  | 	//                 errorHandler.handle(err, res); | ||||||
|  | 	//             }); | ||||||
|  | 	//         } else { | ||||||
|  | 	//             errorHandler.respond(400, res); | ||||||
|  | 	//         } | ||||||
|  | 	//     }); | ||||||
|  | 	// }); | ||||||
|  |  | ||||||
|  | 	return router; | ||||||
|  | }; | ||||||
| @@ -1,10 +1,13 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| const groups = require('../../groups'); | const groups = require('../../groups'); | ||||||
|  | const sockets = require('..'); | ||||||
|  |  | ||||||
| const Groups = module.exports; | const Groups = module.exports; | ||||||
|  |  | ||||||
| Groups.create = async function (socket, data) { | Groups.create = async function (socket, data) { | ||||||
|  | 	sockets.warnDeprecated(socket, 'POST /api/v1/groups'); | ||||||
|  |  | ||||||
| 	if (!data) { | 	if (!data) { | ||||||
| 		throw new Error('[[error:invalid-data]]'); | 		throw new Error('[[error:invalid-data]]'); | ||||||
| 	} else if (groups.isPrivilegeGroup(data.name)) { | 	} else if (groups.isPrivilegeGroup(data.name)) { | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ const utils = require('../utils'); | |||||||
| const events = require('../events'); | const events = require('../events'); | ||||||
| const privileges = require('../privileges'); | const privileges = require('../privileges'); | ||||||
| const notifications = require('../notifications'); | const notifications = require('../notifications'); | ||||||
|  | const sockets = require('.'); | ||||||
|  |  | ||||||
| const SocketGroups = module.exports; | const SocketGroups = module.exports; | ||||||
|  |  | ||||||
| @@ -277,6 +278,8 @@ SocketGroups.kick = async (socket, data) => { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| SocketGroups.create = async (socket, data) => { | SocketGroups.create = async (socket, data) => { | ||||||
|  | 	sockets.warnDeprecated(socket, 'POST /api/v1/groups'); | ||||||
|  |  | ||||||
| 	if (!socket.uid) { | 	if (!socket.uid) { | ||||||
| 		throw new Error('[[error:no-privileges]]'); | 		throw new Error('[[error:no-privileges]]'); | ||||||
| 	} else if (typeof data.name !== 'string' || groups.isPrivilegeGroup(data.name)) { | 	} else if (typeof data.name !== 'string' || groups.isPrivilegeGroup(data.name)) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user