UNPKG

@crowdin/app-project-module

Version:

Module that generates for you all common endpoints for serving standalone Crowdin App

280 lines (279 loc) 12.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const util_1 = require("../../../util"); const types_1 = require("../../../types"); const storage_1 = require("../../../storage"); const users_1 = require("./users"); const CONSTANTS = { MAX_EMAIL_LENGTH: 76, MANAGER_ROLES: ['owner', 'manager'], PROJECT_INTEGRATIONS_MODULE_TYPE: 'project-integrations', }; function handle() { return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () { const onlyCheck = req.body.onlyCheck; const usersToInvite = req.body.users; if (!Array.isArray(usersToInvite)) { return res.status(400).send('Invalid request'); } const client = req.crowdinApiClient; const projectId = req.crowdinContext.jwtPayload.context.project_id; const userId = req.crowdinContext.jwtPayload.context.user_id; const { projectMembers, organizationMembers } = yield (0, users_1.getUsers)({ client, projectId }); const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(req.crowdinContext.clientId); let applicationInstallation = null; try { applicationInstallation = yield client.applicationsApi.getApplicationInstallation(req.crowdinContext.appIdentifier); } catch (error) { if (error.code !== 403) { console.error('Failed to get application installation', error); } } const isAdmin = organizationMembers.some((member) => member.id === +userId && member.isAdmin) || projectMembers.some((member) => member.id === +userId && 'role' in member && member.role === 'owner'); const hasManagerAccess = isAdmin || projectMembers.some((member) => member.id === +userId && ('role' in member ? CONSTANTS.MANAGER_ROLES.includes(member.role) : member.isManager)); if (!hasManagerAccess) { return res.status(403).send({ error: 'Access denied' }); } const inviteRestricted = !isAdmin && 'invite_restrict_enabled' in req.crowdinContext.jwtPayload.context && !!req.crowdinContext.jwtPayload.context.invite_restrict_enabled; const usersWhoWillBeInvitedToOrganization = filterOrganizationUsers(usersToInvite); const usersWhoWillBeInvitedToProject = filterProjectUsers({ inviteRestricted, usersToInvite, projectMembers, organizationMembers, }); const usersWhoNotIssetInApplicationInstallation = filterNotIssetApplicationUsers({ inviteRestricted, usersToInvite, applicationInstallation, projectMembers, organizationMembers, }); const usersWhoNotIssetInIntegration = filterNotIssetIntegrationUsers({ inviteRestricted, usersToInvite, integrationCredentials, projectMembers, organizationMembers, }); if (onlyCheck) { return res.send({ data: { isAdmin, inviteRestricted, editApplicationAvailable: applicationInstallation !== null, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, usersWhoNotIssetInApplicationInstallation, usersWhoNotIssetInIntegration, }, }); } const response = yield inviteUsers({ req, projectId, isAdmin, inviteRestricted, usersToInvite, applicationInstallation, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, usersWhoNotIssetInApplicationInstallation, }); return res.send(response); })); } exports.default = handle; function filterOrganizationUsers(usersToInvite) { return usersToInvite.filter((identifier) => (0, util_1.validateEmail)(identifier)).map((name) => ({ name: `${name}` })); } function filterProjectUsers({ inviteRestricted, usersToInvite, projectMembers, organizationMembers, }) { return usersToInvite .map((identifier) => { if ((0, util_1.validateEmail)(identifier) && !inviteRestricted) { return { name: `${identifier}` }; } const user = projectMembers.find((member) => member.id === +identifier); if (!user) { const organizationUser = organizationMembers.find((member) => member.id === +identifier); return organizationUser && !organizationUser.isAdmin ? { id: organizationUser.id, name: (0, users_1.getUserFullName)(organizationUser) } : null; } return ('role' in user ? !CONSTANTS.MANAGER_ROLES.includes(user.role) : !user.isManager) ? { id: user.id, name: (0, users_1.getUserFullName)(user) } : null; }) .filter(Boolean); } function filterNotIssetApplicationUsers({ inviteRestricted, usersToInvite, applicationInstallation, projectMembers, organizationMembers, }) { let userIdentifiers = []; if (!applicationInstallation) { return []; } const projectIntegrationModules = applicationInstallation.data.modules.filter((module) => module.type === CONSTANTS.PROJECT_INTEGRATIONS_MODULE_TYPE); if (!projectIntegrationModules.length) { return []; } if (projectIntegrationModules.some((module) => module.permissions.user.value === types_1.UserPermissions.ALL_MEMBERS)) { return []; } else { userIdentifiers = usersToInvite.filter((userId) => projectIntegrationModules.every((module) => !module.permissions.user.ids.includes(+userId))); } return userIdentifiers .map((identifier) => { if ((0, util_1.validateEmail)(identifier) && !inviteRestricted) { return { name: `${identifier}` }; } let user; user = projectMembers.find((member) => member.id === +identifier); if (!user) { user = organizationMembers.find((member) => member.id === +identifier); } return user ? { id: user.id, name: (0, users_1.getUserFullName)(user) } : null; }) .filter(Boolean); } function filterNotIssetIntegrationUsers({ inviteRestricted, usersToInvite, integrationCredentials, projectMembers, organizationMembers, }) { let integrationManagers = []; if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.managers) { integrationManagers = JSON.parse(integrationCredentials.managers); } return usersToInvite .map((identifier) => { if (integrationManagers.includes(`${identifier}`)) { return null; } if ((0, util_1.validateEmail)(identifier) && !inviteRestricted) { return { name: `${identifier}` }; } let user; user = projectMembers.find((member) => member.id === +identifier); if (!user) { user = organizationMembers.find((member) => member.id === +identifier); } return user ? { id: user.id, name: (0, users_1.getUserFullName)(user) } : null; }) .filter(Boolean); } function inviteUsers({ req, projectId, isAdmin, inviteRestricted, usersToInvite, applicationInstallation, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, usersWhoNotIssetInApplicationInstallation, }) { return __awaiter(this, void 0, void 0, function* () { const client = req.crowdinApiClient; if (inviteRestricted) { usersWhoWillBeInvitedToOrganization = []; } const alreadyAddedUserIds = yield inviteUsersToProject({ client, projectId, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, }); if (isAdmin) { yield addUsersToApplicationInstallation({ client, ownerId: req.integrationCredentials.ownerId, applicationInstallation, alreadyAddedUserIds, usersWhoNotIssetInApplicationInstallation, }); } yield addUsersToIntegration({ clientId: req.crowdinContext.clientId, alreadyAddedUserIds, usersToInvite, }); return { success: true }; }); } function inviteUsersToProject({ client, projectId, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, }) { return __awaiter(this, void 0, void 0, function* () { let addedIds = []; const emailInvites = usersWhoWillBeInvitedToOrganization.map((user) => user.name); const userIdInvites = usersWhoWillBeInvitedToProject.map((user) => user.id).filter(Boolean); try { if (emailInvites.length) { const inviteResponse = (yield client.usersApi.addProjectMember(projectId, { managerAccess: true, emails: emailInvites, })); // TODO: fix typings in the @crowdin/crowdin-api-client addedIds = inviteResponse.data.added.map((member) => member.id); } } catch (error) { console.error('Failed to invite users', error); } try { if (userIdInvites.length) { yield client.usersApi.addProjectMember(projectId, { managerAccess: true, userIds: userIdInvites, }); // TODO: fix typings in the @crowdin/crowdin-api-client } } catch (error) { console.error('Failed to grant project manager access', error); } return addedIds; }); } function addUsersToApplicationInstallation({ client, ownerId, applicationInstallation, alreadyAddedUserIds, usersWhoNotIssetInApplicationInstallation, }) { return __awaiter(this, void 0, void 0, function* () { if (!applicationInstallation || !usersWhoNotIssetInApplicationInstallation.length) { return; } const applicationInvites = [ ownerId, ...alreadyAddedUserIds, ...usersWhoNotIssetInApplicationInstallation.map((user) => user.id).filter(Boolean), ]; const permissions = applicationInstallation.data.modules.filter((module) => module.type === CONSTANTS.PROJECT_INTEGRATIONS_MODULE_TYPE); if (!permissions.length) { return; } try { for (const module of permissions) { if (module.permissions.user.value === types_1.UserPermissions.ALL_MEMBERS) { continue; } const userIds = [...new Set([...module.permissions.user.ids, ...applicationInvites])]; client.applicationsApi.editApplicationInstallation(applicationInstallation.data.identifier, [ { op: 'replace', path: `/modules/${module.key}/permissions`, value: { user: { value: types_1.UserPermissions.RESTRICTED, ids: userIds, }, }, }, ]); } } catch (error) { console.error('Failed to update application permissions', error); } }); } function addUsersToIntegration({ clientId, alreadyAddedUserIds, usersToInvite, }) { return __awaiter(this, void 0, void 0, function* () { const integrationInvites = [...alreadyAddedUserIds, ...usersToInvite.filter((identifier) => +identifier)]; yield (0, storage_1.getStorage)().updateIntegrationManagers(clientId, JSON.stringify(integrationInvites.map((id) => `${id}`))); }); }