| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- const loggerFactory = require('@alias/logger');
- const logger = loggerFactory('growi:routes:apiv3:user-group'); // eslint-disable-line no-unused-vars
- const express = require('express');
- const router = express.Router();
- const { body, param, query } = require('express-validator');
- const { sanitizeQuery } = require('express-validator');
- const mongoose = require('mongoose');
- const ErrorV3 = require('../../models/vo/error-apiv3');
- const { toPagingLimit, toPagingOffset } = require('../../util/express-validator/sanitizer');
- const validator = {};
- const { ObjectId } = mongoose.Types;
- /**
- * @swagger
- * tags:
- * name: UserGroup
- */
- module.exports = (crowi) => {
- const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
- const adminRequired = require('../../middlewares/admin-required')(crowi);
- const csrf = require('../../middlewares/csrf')(crowi);
- const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
- const {
- UserGroup,
- UserGroupRelation,
- User,
- Page,
- } = crowi.models;
- /**
- * @swagger
- *
- * paths:
- * /user-groups:
- * get:
- * tags: [UserGroup]
- * operationId: getUserGroup
- * summary: /user-groups
- * description: Get usergroups
- * responses:
- * 200:
- * description: usergroups are fetched
- * content:
- * application/json:
- * schema:
- * properties:
- * userGroups:
- * type: object
- * description: a result of `UserGroup.find`
- */
- router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
- // TODO: filter with querystring
- try {
- const page = parseInt(req.query.page) || 1;
- const result = await UserGroup.findUserGroupsWithPagination({ page });
- const { docs: userGroups, totalDocs: totalUserGroups, limit: pagingLimit } = result;
- return res.apiv3({ userGroups, totalUserGroups, pagingLimit });
- }
- catch (err) {
- const msg = 'Error occurred in fetching user group list';
- logger.error('Error', err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-list-fetch-failed'));
- }
- });
- validator.create = [
- body('name', 'Group name is required').trim().exists({ checkFalsy: true }),
- ];
- /**
- * @swagger
- *
- * paths:
- * /user-groups:
- * post:
- * tags: [UserGroup]
- * operationId: createUserGroup
- * summary: /user-groups
- * description: Adds userGroup
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * properties:
- * name:
- * type: string
- * description: name of the userGroup trying to be added
- * responses:
- * 200:
- * description: userGroup is added
- * content:
- * application/json:
- * schema:
- * properties:
- * userGroup:
- * type: object
- * description: A result of `UserGroup.createGroupByName`
- */
- router.post('/', loginRequiredStrictly, adminRequired, csrf, validator.create, apiV3FormValidator, async(req, res) => {
- const { name } = req.body;
- try {
- const userGroupName = crowi.xss.process(name);
- const userGroup = await UserGroup.createGroupByName(userGroupName);
- return res.apiv3({ userGroup });
- }
- catch (err) {
- const msg = 'Error occurred in creating a user group';
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-create-failed'));
- }
- });
- validator.delete = [
- param('id').trim().exists({ checkFalsy: true }),
- query('actionName').trim().exists({ checkFalsy: true }),
- query('transferToUserGroupId').trim(),
- ];
- /**
- * @swagger
- *
- * paths:
- * /user-groups/{id}:
- * delete:
- * tags: [UserGroup]
- * operationId: deleteUserGroup
- * summary: /user-groups/{id}
- * description: Deletes userGroup
- * parameters:
- * - name: id
- * in: path
- * required: true
- * description: id of userGroup
- * schema:
- * type: string
- * - name: actionName
- * in: query
- * description: name of action
- * schema:
- * type: string
- * - name: transferToUserGroupId
- * in: query
- * description: userGroup id that will be transferred to
- * schema:
- * type: string
- * responses:
- * 200:
- * description: userGroup is removed
- * content:
- * application/json:
- * schema:
- * properties:
- * userGroups:
- * type: object
- * description: A result of `UserGroup.removeCompletelyById`
- */
- router.delete('/:id', loginRequiredStrictly, adminRequired, csrf, validator.delete, apiV3FormValidator, async(req, res) => {
- const { id: deleteGroupId } = req.params;
- const { actionName, transferToUserGroupId } = req.query;
- try {
- const userGroup = await UserGroup.removeCompletelyById(deleteGroupId, actionName, transferToUserGroupId);
- return res.apiv3({ userGroup });
- }
- catch (err) {
- const msg = 'Error occurred in deleting a user group';
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-delete-failed'));
- }
- });
- // return one group with the id
- // router.get('/:id', async(req, res) => {
- // });
- validator.update = [
- body('name', 'Group name is required').trim().exists({ checkFalsy: true }),
- ];
- /**
- * @swagger
- *
- * paths:
- * /user-groups/{id}:
- * put:
- * tags: [UserGroup]
- * operationId: updateUserGroups
- * summary: /user-groups/{id}
- * description: Update userGroup
- * parameters:
- * - name: id
- * in: path
- * required: true
- * description: id of userGroup
- * schema:
- * type: string
- * responses:
- * 200:
- * description: userGroup is updated
- * content:
- * application/json:
- * schema:
- * properties:
- * userGroup:
- * type: object
- * description: A result of `UserGroup.updateName`
- */
- router.put('/:id', loginRequiredStrictly, adminRequired, csrf, validator.update, apiV3FormValidator, async(req, res) => {
- const { id } = req.params;
- const { name } = req.body;
- try {
- const userGroup = await UserGroup.findById(id);
- if (userGroup == null) {
- throw new Error('The group does not exist');
- }
- // check if the new group name is available
- const isRegisterableName = await UserGroup.isRegisterableName(name);
- if (!isRegisterableName) {
- throw new Error('The group name is already taken');
- }
- await userGroup.updateName(name);
- res.apiv3({ userGroup });
- }
- catch (err) {
- const msg = 'Error occurred in updating a user group name';
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-update-failed'));
- }
- });
- validator.users = {};
- /**
- * @swagger
- *
- * paths:
- * /user-groups/{id}/users:
- * get:
- * tags: [UserGroup]
- * operationId: getUsersUserGroups
- * summary: /user-groups/{id}/users
- * description: Get users related to the userGroup
- * parameters:
- * - name: id
- * in: path
- * required: true
- * description: id of userGroup
- * schema:
- * type: string
- * responses:
- * 200:
- * description: users are fetched
- * content:
- * application/json:
- * schema:
- * properties:
- * users:
- * type: array
- * items:
- * type: object
- * description: user objects
- */
- router.get('/:id/users', loginRequiredStrictly, adminRequired, async(req, res) => {
- const { id } = req.params;
- try {
- const userGroup = await UserGroup.findById(id);
- const userGroupRelations = await UserGroupRelation.findAllRelationForUserGroup(userGroup);
- const users = userGroupRelations.map((userGroupRelation) => {
- return userGroupRelation.relatedUser;
- });
- return res.apiv3({ users });
- }
- catch (err) {
- const msg = `Error occurred in fetching users for group: ${id}`;
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-user-list-fetch-failed'));
- }
- });
- /**
- * @swagger
- *
- * paths:
- * /user-groups/{id}/unrelated-users:
- * get:
- * tags: [UserGroup]
- * operationId: getUnrelatedUsersUserGroups
- * summary: /user-groups/{id}/unrelated-users
- * description: Get users unrelated to the userGroup
- * parameters:
- * - name: id
- * in: path
- * required: true
- * description: id of userGroup
- * schema:
- * type: string
- * responses:
- * 200:
- * description: users are fetched
- * content:
- * application/json:
- * schema:
- * properties:
- * users:
- * type: array
- * items:
- * type: object
- * description: user objects
- */
- router.get('/:id/unrelated-users', loginRequiredStrictly, adminRequired, async(req, res) => {
- const { id } = req.params;
- const {
- searchWord, searchType, isAlsoNameSearched, isAlsoMailSearched,
- } = req.query;
- const queryOptions = {
- searchWord, searchType, isAlsoNameSearched, isAlsoMailSearched,
- };
- try {
- const userGroup = await UserGroup.findById(id);
- const users = await UserGroupRelation.findUserByNotRelatedGroup(userGroup, queryOptions);
- return res.apiv3({ users });
- }
- catch (err) {
- const msg = `Error occurred in fetching unrelated users for group: ${id}`;
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-unrelated-user-list-fetch-failed'));
- }
- });
- validator.users.post = [
- param('id').trim().exists({ checkFalsy: true }),
- param('username').trim().exists({ checkFalsy: true }),
- ];
- /**
- * @swagger
- *
- * paths:
- * /user-groups/{id}/users:
- * post:
- * tags: [UserGroup]
- * operationId: addUserUserGroups
- * summary: /user-groups/{id}/users
- * description: Add a user to the userGroup
- * parameters:
- * - name: id
- * in: path
- * required: true
- * description: id of userGroup
- * schema:
- * type: string
- * responses:
- * 200:
- * description: a user is added
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * user:
- * type: object
- * description: the user added to the group
- * userGroup:
- * type: object
- * description: the group to which a user was added
- * userGroupRelation:
- * type: object
- * description: the associative entity between user and userGroup
- */
- router.post('/:id/users/:username', loginRequiredStrictly, adminRequired, validator.users.post, apiV3FormValidator, async(req, res) => {
- const { id, username } = req.params;
- try {
- const [userGroup, user] = await Promise.all([
- UserGroup.findById(id),
- User.findUserByUsername(username),
- ]);
- // check for duplicate users in groups
- const isRelatedUserForGroup = await UserGroupRelation.isRelatedUserForGroup(userGroup, user);
- if (isRelatedUserForGroup) {
- logger.warn('The user is already joined');
- return res.apiv3();
- }
- const userGroupRelation = await UserGroupRelation.createRelation(userGroup, user);
- await userGroupRelation.populate('relatedUser', User.USER_PUBLIC_FIELDS).execPopulate();
- return res.apiv3({ user, userGroup, userGroupRelation });
- }
- catch (err) {
- const msg = `Error occurred in adding the user "${username}" to group "${id}"`;
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-add-user-failed'));
- }
- });
- validator.users.delete = [
- param('id').trim().exists({ checkFalsy: true }),
- param('username').trim().exists({ checkFalsy: true }),
- ];
- /**
- * @swagger
- *
- * paths:
- * /user-groups/{id}/users:
- * delete:
- * tags: [UserGroup]
- * operationId: deleteUsersUserGroups
- * summary: /user-groups/{id}/users
- * description: remove a user from the userGroup
- * parameters:
- * - name: id
- * in: path
- * required: true
- * description: id of userGroup
- * schema:
- * type: string
- * responses:
- * 200:
- * description: a user was removed
- * content:
- * application/json:
- * schema:
- * type: object
- * properties:
- * user:
- * type: object
- * description: the user removed from the group
- * userGroup:
- * type: object
- * description: the group from which a user was removed
- * userGroupRelation:
- * type: object
- * description: the associative entity between user and userGroup
- */
- router.delete('/:id/users/:username', loginRequiredStrictly, adminRequired, validator.users.delete, apiV3FormValidator, async(req, res) => {
- const { id, username } = req.params;
- try {
- const [userGroup, user] = await Promise.all([
- UserGroup.findById(id),
- User.findUserByUsername(username),
- ]);
- const userGroupRelation = await UserGroupRelation.findOne({ relatedUser: new ObjectId(user._id), relatedGroup: new ObjectId(userGroup._id) });
- if (userGroupRelation == null) {
- throw new Error(`Group "${id}" does not exist or user "${username}" does not belong to group "${id}"`);
- }
- await userGroupRelation.remove();
- return res.apiv3({ user, userGroup, userGroupRelation });
- }
- catch (err) {
- const msg = `Error occurred in removing the user "${username}" from group "${id}"`;
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-remove-user-failed'));
- }
- });
- validator.userGroupRelations = {};
- /**
- * @swagger
- *
- * paths:
- * /user-groups/{id}/user-group-relations:
- * get:
- * tags: [UserGroup]
- * operationId: getUserGroupRelationsUserGroups
- * summary: /user-groups/{id}/user-group-relations
- * description: Get the user group relations for the userGroup
- * parameters:
- * - name: id
- * in: path
- * required: true
- * description: id of userGroup
- * schema:
- * type: string
- * responses:
- * 200:
- * description: user group relations are fetched
- * content:
- * application/json:
- * schema:
- * properties:
- * userGroupRelations:
- * type: array
- * items:
- * type: object
- * description: userGroupRelation objects
- */
- router.get('/:id/user-group-relations', loginRequiredStrictly, adminRequired, async(req, res) => {
- const { id } = req.params;
- try {
- const userGroup = await UserGroup.findById(id);
- const userGroupRelations = await UserGroupRelation.findAllRelationForUserGroup(userGroup);
- return res.apiv3({ userGroupRelations });
- }
- catch (err) {
- const msg = `Error occurred in fetching user group relations for group: ${id}`;
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-user-group-relation-list-fetch-failed'));
- }
- });
- validator.pages = {};
- validator.pages.get = [
- param('id').trim().exists({ checkFalsy: true }),
- sanitizeQuery('limit').customSanitizer(toPagingLimit),
- sanitizeQuery('offset').customSanitizer(toPagingOffset),
- ];
- /**
- * @swagger
- *
- * paths:
- * /user-groups/{id}/pages:
- * get:
- * tags: [UserGroup]
- * operationId: getPagesUserGroups
- * summary: /user-groups/{id}/pages
- * description: Get closed pages for the userGroup
- * parameters:
- * - name: id
- * in: path
- * required: true
- * description: id of userGroup
- * schema:
- * type: string
- * responses:
- * 200:
- * description: pages are fetched
- * content:
- * application/json:
- * schema:
- * properties:
- * pages:
- * type: array
- * items:
- * type: object
- * description: page objects
- */
- router.get('/:id/pages', loginRequiredStrictly, adminRequired, validator.pages.get, apiV3FormValidator, async(req, res) => {
- const { id } = req.params;
- const { limit, offset } = req.query;
- try {
- const { docs, total } = await Page.paginate({
- grant: Page.GRANT_USER_GROUP,
- grantedGroup: { $in: [id] },
- }, {
- offset,
- limit,
- populate: {
- path: 'lastUpdateUser',
- select: User.USER_PUBLIC_FIELDS,
- },
- });
- const current = offset / limit + 1;
- // TODO: create a common moudule for paginated response
- return res.apiv3({ total, current, pages: docs });
- }
- catch (err) {
- const msg = `Error occurred in fetching pages for group: ${id}`;
- logger.error(msg, err);
- return res.apiv3Err(new ErrorV3(msg, 'user-group-page-list-fetch-failed'));
- }
- });
- return router;
- };
|