@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
JavaScript
;
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}`)));
});
}