Browse Source

feat(api): add user invite api

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/4134/head
Pranav C 2 years ago
parent
commit
79ecbbcd33
  1. 98
      packages/nocodb/src/lib/meta/api/orgUserApis.ts
  2. 2
      packages/nocodb/src/lib/meta/api/projectUserApis.ts

98
packages/nocodb/src/lib/meta/api/orgUserApis.ts

@ -1,11 +1,17 @@
import { Router } from 'express';
import { v4 as uuidv4 } from 'uuid';
import validator from 'validator';
import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import Audit from '../../models/Audit';
import User from '../../models/User';
import { metaApiMetrics } from '../helpers/apiMetrics';
import { NcError } from '../helpers/catchError';
import { extractProps } from '../helpers/extractProps';
import ncMetaAclMw from '../helpers/ncMetaAclMw';
import { PagedResponseImpl } from '../helpers/PagedResponse';
import { randomTokenString } from '../helpers/stringHelpers';
import { Tele } from 'nc-help';
import { sendInviteEmail } from './projectUserApis';
async function userList(req, res) {
res.json(
@ -38,8 +44,90 @@ async function userDelete(req, res) {
res.json(await User.delete(req.params.userId));
}
async function userAdd(_req, _res) {
NcError.notImplemented();
async function userAdd(req, res, next) {
// allow only viewer or creator role
if (
req.body.roles &&
![OrgUserRoles.VIEWER, OrgUserRoles.CREATOR].includes(req.body.roles)
) {
NcError.badRequest('Invalid role');
}
// extract emails from request body
const emails = (req.body.email || '')
.toLowerCase()
.split(/\s*,\s*/)
.map((v) => v.trim());
// check for invalid emails
const invalidEmails = emails.filter((v) => !validator.isEmail(v));
if (!emails.length) {
return NcError.badRequest('Invalid email address');
}
if (invalidEmails.length) {
NcError.badRequest('Invalid email address : ' + invalidEmails.join(', '));
}
const invite_token = uuidv4();
const error = [];
for (const email of emails) {
// add user to project if user already exist
const user = await User.getByEmail(email);
if (user) {
NcError.badRequest('User already exist');
} else {
try {
// create new user with invite token
await User.insert({
invite_token,
invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000),
email,
roles: OrgUserRoles.VIEWER,
token_version: randomTokenString(),
});
const count = await User.count();
Tele.emit('evt', { evt_type: 'org:user:invite', count });
await Audit.insert({
project_id: req.params.projectId,
op_type: 'AUTHENTICATION',
op_sub_type: 'INVITE',
user: req.user.email,
description: `invited ${email} to ${req.params.projectId} project `,
ip: req.clientIp,
});
// in case of single user check for smtp failure
// and send back token if failed
if (
emails.length === 1 &&
!(await sendInviteEmail(email, invite_token, req))
) {
return res.json({ invite_token, email });
} else {
sendInviteEmail(email, invite_token, req);
}
} catch (e) {
console.log(e);
if (emails.length === 1) {
return next(e);
} else {
error.push({ email, error: e.message });
}
}
}
}
if (emails.length === 1) {
res.json({
msg: 'success',
});
} else {
return res.json({ invite_token, emails, error });
}
}
const router = Router({ mergeParams: true });
@ -56,11 +144,11 @@ router.patch(
router.delete(
'/api/v1/users/:userId',
metaApiMetrics,
ncMetaAclMw(userAdd, 'userAdd', [OrgUserRoles.SUPER])
ncMetaAclMw(userDelete, 'userAdd', [OrgUserRoles.SUPER])
);
router.post(
'/api/v1/users/:userId',
'/api/v1/users',
metaApiMetrics,
ncMetaAclMw(userDelete, 'userDelete', [OrgUserRoles.SUPER])
ncMetaAclMw(userAdd, 'userDelete', [OrgUserRoles.SUPER])
);
export default router;

2
packages/nocodb/src/lib/meta/api/projectUserApis.ts

@ -268,7 +268,7 @@ async function projectUserInviteResend(req, res): Promise<any> {
res.json({ msg: 'success' });
}
async function sendInviteEmail(
export async function sendInviteEmail(
email: string,
token: string,
req: any

Loading…
Cancel
Save