Browse Source

fix: Bulk add users

Enabled option to add bulk add user with comma separated emails

re #300

Signed-off-by: Pranav C <61551451+pranavxc@users.noreply.github.com>
pull/350/head
Pranav C 3 years ago
parent
commit
12ab1d2d8a
  1. 22
      packages/nc-gui/components/auth/userManagement.vue
  2. 96
      packages/nc-gui/components/project/spreadsheet/components/extras.vue
  3. 81
      packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue
  4. 1194
      packages/nc-gui/layouts/default.vue
  5. 1
      packages/nc-gui/nuxt.config.js
  6. 2
      packages/nc-gui/pages/projects/index.vue
  7. 9
      packages/nc-gui/plugins/globalEventBus.js
  8. 1
      packages/nc-gui/static/lang/en.json
  9. 1
      packages/nc-gui/static/lang/fr.json
  10. 1
      packages/nc-gui/static/lang/ja.json
  11. 2
      packages/nc-gui/static/lang/zh.json
  12. 2
      packages/nc-lib-gui/package.json
  13. 103
      packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts

22
packages/nc-gui/components/auth/userManagement.vue

@ -346,7 +346,7 @@
></dlg-label-submit-cancel> ></dlg-label-submit-cancel>
<v-dialog v-model="userEditDialog" :width="invite_token ? 700 :480" @close="invite_token = null"> <v-dialog v-model="userEditDialog" :width="invite_token ? 700 :700" @close="invite_token = null">
<v-card v-if="selectedUser" class="px-15 py-5 " style="min-height: 100%"> <v-card v-if="selectedUser" class="px-15 py-5 " style="min-height: 100%">
<h4 class="text-center text-capitalize mt-2 d-100 display-1"> <h4 class="text-center text-capitalize mt-2 d-100 display-1">
<template v-if="invite_token">Copy Invite Token</template> <template v-if="invite_token">Copy Invite Token</template>
@ -388,7 +388,10 @@
<p class="caption grey--text mt-3"> Looks like you have not configured mailer yet! <br>Please copy above <p class="caption grey--text mt-3"> Looks like you have not configured mailer yet! <br>Please copy above
invite invite
link and send it to {{ invite_token && invite_token.email }}.</p> link and send it to {{ invite_token && (invite_token.email || invite_token.emails && invite_token.emails.join(', ')) }}.</p>
<!-- todo: show error message if failed-->
</div> </div>
<template v-else> <template v-else>
<v-form v-model="valid" @submit.prevent="saveUser"> <v-form v-model="valid" @submit.prevent="saveUser">
@ -403,6 +406,8 @@
v-model="selectedUser.email" v-model="selectedUser.email"
:rules="emailRules" :rules="emailRules"
@input="edited=true" @input="edited=true"
validate-on-blur
hint="You can add multiple comma(,) separated emails"
label="Email"></v-text-field> label="Email"></v-text-field>
</v-col> </v-col>
<v-col cols="12"> <v-col cols="12">
@ -476,14 +481,21 @@ export default {
valid: null, valid: null,
emailRules: [ emailRules: [
v => !!v || 'E-mail is required', v => !!v || 'E-mail is required',
v => isEmail(v) || 'E-mail must be valid' v => {
const invalidEmails = (v||'').split(/\s*,\s*/).filter(e => !isEmail(e));
return !invalidEmails.length || `"${invalidEmails.join(', ')}" - invalid email`
}
], ],
userList: [] userList: []
}), }),
async created() { async created() {
this.$eventBus.$on('show-add-user', this.addUser);
await this.loadUsers(); await this.loadUsers();
await this.loadRoles(); await this.loadRoles();
}, },
beforeDestroy() {
this.$eventBus.$off('show-add-user', this.addUser);
},
methods: { methods: {
simpleAnim() { simpleAnim() {
var count = 30; var count = 30;
@ -619,7 +631,7 @@ export default {
addUser() { addUser() {
this.invite_token = null; this.invite_token = null;
this.selectedUser = { this.selectedUser = {
roles: 'creator,editor' roles: 'editor'
} }
this.userEditDialog = true this.userEditDialog = true
}, },
@ -733,7 +745,7 @@ export default {
this.selectedUser = this.users[i]; this.selectedUser = this.users[i];
} }
} }
} },
} }
</script> </script>

96
packages/nc-gui/components/project/spreadsheet/components/extras.vue

@ -0,0 +1,96 @@
<template>
<div class="wrapper">
<div class="d-flex justify-end">
<v-list
width="100%"
class="
flex-shrink-1
text-left
elevation-1
rounded-sm
community-card
item
"
:class="{ active: showCommunity }"
dense
>
<v-list-item
dense
target="_blank"
href="https://calendly.com/nocodb"
>
<!-- Book a Free DEMO -->
<v-list-item-title>
<v-icon class="mr-1" small :color="textColors[3]">mdi-calendar-month
</v-icon>
<span class="caption" :title="$t('projects.show_community_message_2')">{{
$t('projects.show_community_message_2')
}}</span></v-list-item-title>
</v-list-item>
<v-divider></v-divider>
<v-list-item dense href="https://discord.gg/5RgZmkW" target="_blank">
<!-- Get your questions answered -->
<v-list-item-title>
<v-icon class="mr-1" small :color="textColors[0]">mdi-discord</v-icon>
<span class="caption" :title="$t('projects.show_community_message_3_short')">{{
$t('projects.show_community_message_3_short')
}}</span>
</v-list-item-title>
</v-list-item>
<v-divider></v-divider>
<v-list-item dense href="https://twitter.com/NocoDB" target="_blank">
<!-- Follow NocoDB -->
<v-list-item-title>
<v-icon class="mr-1" small :color="textColors[1]">mdi-twitter</v-icon>
<span class="caption" title="$t('projects.show_community_message_4')"> {{
$t('projects.show_community_message_4')
}}</span></v-list-item-title>
</v-list-item>
</v-list>
</div>
<sponsor-mini
:class="{ active: !showCommunity }" class="item" :nav="true"></sponsor-mini>
</div>
</template>
<script>
import SponsorMini from "~/components/sponsorMini";
import colors from "~/mixins/colors";
export default {
name: "extras",
data: () => ({
showCommunity: true
}),
mixins: [colors],
components: {SponsorMini},
mounted() {
setInterval(() => this.showCommunity = !this.showCommunity, 60000)
}
}
</script>
<style scoped lang="scss">
.wrapper {
position: relative;
.item {
z-index: -1;
opacity: 0;
position: absolute;
transition: .6s opacity;
bottom: 0;
right: 0;
width: 100%;
&.active {
z-index: 1;
position: relative;
opacity: 1;
}
}
}
</style>

81
packages/nc-gui/components/project/spreadsheet/components/spreadsheetNavDrawer.vue

@ -29,7 +29,7 @@
x-small x-small
v-if="viewIcons[view.show_as]" v-if="viewIcons[view.show_as]"
:color="viewIcons[view.show_as].color" :color="viewIcons[view.show_as].color"
>{{ viewIcons[view.show_as].icon }} >{{ viewIcons[view.show_as].icon }}
</v-icon> </v-icon>
<v-icon color="primary" small v-else>mdi-table</v-icon> <v-icon color="primary" small v-else>mdi-table</v-icon>
</v-list-item-icon> </v-list-item-icon>
@ -49,7 +49,7 @@
@blur="updateViewName(view)" @blur="updateViewName(view)"
/> />
<template v-else <template v-else
><span v-on="on">{{ view.alias || view.title }}</span></template ><span v-on="on">{{ view.alias || view.title }}</span></template
> >
</div> </div>
</template> </template>
@ -93,13 +93,14 @@
</x-icon> </x-icon>
</template> </template>
<v-icon v-if="view.id === selectedViewId" small class="check-icon" <v-icon v-if="view.id === selectedViewId" small class="check-icon"
>mdi-check-bold</v-icon >mdi-check-bold
</v-icon
> >
</v-list-item> </v-list-item>
</v-list-item-group> </v-list-item-group>
</v-list> </v-list>
<template v-if="hideViews && _isUIAllowed('virtualViewsCreateOrEdit')"> <template v-if="hideViews && _isUIAllowed('virtualViewsCreateOrEdit')">
<v-divider class="advance-menu-divider"> </v-divider> <v-divider class="advance-menu-divider"></v-divider>
<v-list <v-list
dense dense
@ -121,7 +122,7 @@
@mouseleave="overShieldIcon = false" @mouseleave="overShieldIcon = false"
icon-class="ml-2" icon-class="ml-2"
small small
>mdi-shield-lock-outline >mdi-shield-lock-outline
</x-icon> </x-icon>
</template> </template>
<!-- Only visible to Creator --> <!-- Only visible to Creator -->
@ -137,7 +138,7 @@
<v-icon color="blue" x-small>mdi-grid-large</v-icon> <v-icon color="blue" x-small>mdi-grid-large</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title <v-list-item-title
><span class="font-weight-regular"> ><span class="font-weight-regular">
<!-- Grid --> <!-- Grid -->
{{ $t('nav_drawer.virtual_views.grid') }} {{ $t('nav_drawer.virtual_views.grid') }}
</span></v-list-item-title </span></v-list-item-title
@ -156,7 +157,7 @@
<v-icon color="orange" x-small>mdi-camera-image</v-icon> <v-icon color="orange" x-small>mdi-camera-image</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title <v-list-item-title
><span class="font-weight-regular"> ><span class="font-weight-regular">
<!-- Gallery --> <!-- Gallery -->
{{ $t('nav_drawer.virtual_views.gallery') }} {{ $t('nav_drawer.virtual_views.gallery') }}
@ -183,7 +184,7 @@
<v-icon x-small>mdi-calendar</v-icon> <v-icon x-small>mdi-calendar</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title <v-list-item-title
><span class="font-weight-regular"> ><span class="font-weight-regular">
<!-- Calendar --> <!-- Calendar -->
{{ $t('nav_drawer.virtual_views.calendar') }} {{ $t('nav_drawer.virtual_views.calendar') }}
</span></v-list-item-title </span></v-list-item-title
@ -208,7 +209,7 @@
<v-icon x-small>mdi-tablet-dashboard</v-icon> <v-icon x-small>mdi-tablet-dashboard</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title <v-list-item-title
><span class="font-weight-regular"> ><span class="font-weight-regular">
<!-- Kanban --> <!-- Kanban -->
{{ $t('nav_drawer.virtual_views.kanban') }} {{ $t('nav_drawer.virtual_views.kanban') }}
</span></v-list-item-title </span></v-list-item-title
@ -233,7 +234,7 @@
<v-icon x-small class="mt-n1">mdi-form-select</v-icon> <v-icon x-small class="mt-n1">mdi-form-select</v-icon>
</v-list-item-icon> </v-list-item-icon>
<v-list-item-title <v-list-item-title
><span class="font-weight-regular"> ><span class="font-weight-regular">
<!-- Form --> <!-- Form -->
{{ $t('nav_drawer.virtual_views.form') }} {{ $t('nav_drawer.virtual_views.form') }}
@ -256,9 +257,13 @@
v-if="time - $store.state.windows.miniSponsorCard > 15 * 60 * 1000" v-if="time - $store.state.windows.miniSponsorCard > 15 * 60 * 1000"
> >
<v-icon small class="close-icon" @click="hideMiniSponsorCard" <v-icon small class="close-icon" @click="hideMiniSponsorCard"
>mdi-close-circle-outline</v-icon >mdi-close-circle-outline
</v-icon
> >
<sponsor-mini :nav="true"></sponsor-mini>
<extras></extras>
</div> </div>
<!--<div class="text-center"> <!--<div class="text-center">
<v-hover > <v-hover >
@ -285,7 +290,7 @@
<span <span
class="body-2 grey--text" class="body-2 grey--text"
@dblclick="$emit('update:showAdvanceOptions', !showAdvanceOptions)" @dblclick="$emit('update:showAdvanceOptions', !showAdvanceOptions)"
>Advanced</span >Advanced</span
> >
<v-tooltip top> <v-tooltip top>
<template v-slot:activator="{ on }"> <template v-slot:activator="{ on }">
@ -296,7 +301,7 @@
@mouseleave="overAdvShieldIcon = false" @mouseleave="overAdvShieldIcon = false"
icon-class="ml-2" icon-class="ml-2"
small small
>mdi-shield-lock-outline >mdi-shield-lock-outline
</x-icon> </x-icon>
</template> </template>
<span class="caption"> <span class="caption">
@ -338,28 +343,6 @@
</v-list> </v-list>
</v-menu> </v-menu>
</v-list-item> </v-list-item>
<!-- </template>-->
<!-- <v-card dense class="backgroundColor">-->
<!-- <v-container fluid @click.stop>-->
<!-- <v-text-field-->
<!-- label="Password"-->
<!-- hint="Enter shared view password"-->
<!-- flat-->
<!-- solo-->
<!-- dense-->
<!-- v-model="sharedViewPassword"-->
<!-- ></v-text-field>-->
<!-- <div class="text-right">-->
<!-- <v-btn small color="primary" @click="genShareLink()">Create</v-btn>-->
<!-- </div>-->
<!-- </v-container>-->
<!-- </v-card>-->
<!-- </v-menu>-->
<!-- </template>-->
<!-- Generate shared view url-->
<!-- </v-tooltip>-->
<v-tooltip bottom> <v-tooltip bottom>
<template v-slot:activator="{ on }"> <template v-slot:activator="{ on }">
@ -407,12 +390,12 @@
</p> </p>
<div style="border-radius: 4px" class="share-link-box body-2 pa-2 d-flex align-center"> <div style="border-radius: 4px" class="share-link-box body-2 pa-2 d-flex align-center">
{{ shareLink.url }} {{ shareLink.url }}
<v-spacer> </v-spacer> <v-spacer></v-spacer>
<a :href="shareLink.url" style="text-decoration: none" target="_blank"> <a :href="shareLink.url" style="text-decoration: none" target="_blank">
<v-icon small class="mx-2">mdi-open-in-new</v-icon> <v-icon small class="mx-2">mdi-open-in-new</v-icon>
</a> </a>
<v-icon small class="pointer" @click="copyShareUrlToClipboard" <v-icon small class="pointer" @click="copyShareUrlToClipboard"
>mdi-content-copy >mdi-content-copy
</v-icon> </v-icon>
</div> </div>
@ -461,11 +444,11 @@
<script> <script>
import CreateViewDialog from '@/components/project/spreadsheet/dialog/createViewDialog'; import CreateViewDialog from '@/components/project/spreadsheet/dialog/createViewDialog';
import SponsorMini from '@/components/sponsorMini'; import Extras from "~/components/project/spreadsheet/components/extras";
export default { export default {
name: 'spreadsheetNavDrawer', name: 'spreadsheetNavDrawer',
components: { SponsorMini, CreateViewDialog }, components: {Extras, CreateViewDialog},
props: { props: {
showAdvanceOptions: Boolean, showAdvanceOptions: Boolean,
hideViews: Boolean, hideViews: Boolean,
@ -505,11 +488,11 @@ export default {
overShieldIcon: false, overShieldIcon: false,
viewsList: [], viewsList: [],
viewIcons: { viewIcons: {
grid: { icon: 'mdi-grid-large', color: 'blue' }, grid: {icon: 'mdi-grid-large', color: 'blue'},
form: { icon: 'mdi-form-select', color: 'pink' }, form: {icon: 'mdi-form-select', color: 'pink'},
calendar: { icon: 'mdi-calendar', color: 'purple' }, calendar: {icon: 'mdi-calendar', color: 'purple'},
gallery: { icon: 'mdi-camera-image', color: 'orange' }, gallery: {icon: 'mdi-camera-image', color: 'orange'},
kanban: { icon: 'mdi-tablet-dashboard', color: 'green' }, kanban: {icon: 'mdi-tablet-dashboard', color: 'green'},
}, },
copyViewRef: null, copyViewRef: null,
shareLink: {}, shareLink: {},
@ -592,7 +575,7 @@ export default {
async saveShareLinkPassword() { async saveShareLinkPassword() {
try { try {
await this.$store.dispatch('sqlMgr/ActSqlOp', [ await this.$store.dispatch('sqlMgr/ActSqlOp', [
{ dbAlias: this.nodes.dbAlias }, {dbAlias: this.nodes.dbAlias},
'updateSharedViewLinkPassword', 'updateSharedViewLinkPassword',
{ {
id: this.shareLink.id, id: this.shareLink.id,
@ -639,7 +622,7 @@ export default {
}, },
async updateViewName(view) { async updateViewName(view) {
try { try {
await this.sqlOp({ dbAlias: this.nodes.dbAlias }, 'xcVirtualTableRename', { await this.sqlOp({dbAlias: this.nodes.dbAlias}, 'xcVirtualTableRename', {
id: view.id, id: view.id,
title: view.title, title: view.title,
alias: view.alias, alias: view.alias,
@ -661,7 +644,7 @@ export default {
}, },
async deleteView(view) { async deleteView(view) {
try { try {
await this.sqlOp({ dbAlias: this.nodes.dbAlias }, 'xcVirtualTableDelete', { await this.sqlOp({dbAlias: this.nodes.dbAlias}, 'xcVirtualTableDelete', {
id: view.id, id: view.id,
title: view.alias || view.title, title: view.alias || view.title,
parent_model_title: this.table, parent_model_title: this.table,
@ -675,7 +658,7 @@ export default {
async genShareLink() { async genShareLink() {
this.showShareModel = true; this.showShareModel = true;
const sharedViewUrl = await this.$store.dispatch('sqlMgr/ActSqlOp', [ const sharedViewUrl = await this.$store.dispatch('sqlMgr/ActSqlOp', [
{ dbAlias: this.nodes.dbAlias }, {dbAlias: this.nodes.dbAlias},
'createSharedViewLink', 'createSharedViewLink',
{ {
model_name: this.table, model_name: this.table,

1194
packages/nc-gui/layouts/default.vue

File diff suppressed because it is too large Load Diff

1
packages/nc-gui/nuxt.config.js

@ -55,6 +55,7 @@ export default {
"@/plugins/vueClipboard", "@/plugins/vueClipboard",
"@/plugins/globalComponentLoader", "@/plugins/globalComponentLoader",
"@/plugins/globalMixin", "@/plugins/globalMixin",
"@/plugins/globalEventBus",
"~/plugins/i18n.js", "~/plugins/i18n.js",
{src: '~plugins/projectLoader.js', ssr: false} {src: '~plugins/projectLoader.js', ssr: false}
], ],

2
packages/nc-gui/pages/projects/index.vue

@ -508,7 +508,7 @@
<v-list-item <v-list-item
dense dense
target="_blank" target="_blank"
href="https://calendly.com/xgenecloud/demo" href="https://calendly.com/nocodb"
> >
<v-list-item-icon> <v-list-item-icon>
<v-icon class="ml-2" :color="textColors[3]" <v-icon class="ml-2" :color="textColors[3]"

9
packages/nc-gui/plugins/globalEventBus.js

@ -0,0 +1,9 @@
import Vue from 'vue';
const GlobalPlugins = {
install(v) {
v.prototype.$eventBus = new Vue();
},
};
Vue.use(GlobalPlugins);

1
packages/nc-gui/static/lang/en.json

@ -22,6 +22,7 @@
"projects.show_community_message_1_2": "us on Github", "projects.show_community_message_1_2": "us on Github",
"projects.show_community_message_2": "Book a Free DEMO", "projects.show_community_message_2": "Book a Free DEMO",
"projects.show_community_message_3": "Get your questions answered", "projects.show_community_message_3": "Get your questions answered",
"projects.show_community_message_3_short": "Join Discord",
"projects.show_community_message_4": "Follow NocoDB", "projects.show_community_message_4": "Follow NocoDB",
"projects.search.no_result": "Your search for {search} found no results", "projects.search.no_result": "Your search for {search} found no results",
"projects.ext_db.title.edit": "Edit Project", "projects.ext_db.title.edit": "Edit Project",

1
packages/nc-gui/static/lang/fr.json

@ -22,6 +22,7 @@
"projects.show_community_message_1_2": "sur Github", "projects.show_community_message_1_2": "sur Github",
"projects.show_community_message_2": "Planifier une démo gratuite", "projects.show_community_message_2": "Planifier une démo gratuite",
"projects.show_community_message_3": "Obtenir des réponses à vos questions", "projects.show_community_message_3": "Obtenir des réponses à vos questions",
"projects.show_community_message_3_short": "Join Discord",
"projects.show_community_message_4": "Suivre NocoDB", "projects.show_community_message_4": "Suivre NocoDB",
"projects.search.no_result": "Votre recherche pour {search} n'a renvoyée aucun résultat", "projects.search.no_result": "Votre recherche pour {search} n'a renvoyée aucun résultat",
"projects.ext_db.title.edit": "Editer le projet", "projects.ext_db.title.edit": "Editer le projet",

1
packages/nc-gui/static/lang/ja.json

@ -22,6 +22,7 @@
"projects.show_community_message_1_2": "us on Github", "projects.show_community_message_1_2": "us on Github",
"projects.show_community_message_2": "Book a Free DEMO", "projects.show_community_message_2": "Book a Free DEMO",
"projects.show_community_message_3": "Get your questions answered", "projects.show_community_message_3": "Get your questions answered",
"projects.show_community_message_3_short": "Join Discord",
"projects.show_community_message_4": "Follow NocoDB", "projects.show_community_message_4": "Follow NocoDB",
"projects.search.no_result": "Your search for {search} found no results", "projects.search.no_result": "Your search for {search} found no results",
"projects.ext_db.title.edit": "Edit Project", "projects.ext_db.title.edit": "Edit Project",

2
packages/nc-gui/static/lang/zh.json

@ -22,6 +22,7 @@
"projects.show_community_message_1_2": "us on Github", "projects.show_community_message_1_2": "us on Github",
"projects.show_community_message_2": "Book a Free DEMO", "projects.show_community_message_2": "Book a Free DEMO",
"projects.show_community_message_3": "Get your questions answered", "projects.show_community_message_3": "Get your questions answered",
"projects.show_community_message_3_short": "Join Discord",
"projects.show_community_message_4": "Follow NocoDB", "projects.show_community_message_4": "Follow NocoDB",
"projects.search.no_result": "Your search for {search} found no results", "projects.search.no_result": "Your search for {search} found no results",
"projects.ext_db.title.edit": "Edit Project", "projects.ext_db.title.edit": "Edit Project",
@ -128,4 +129,3 @@
"management.meta.operation_4.desc": "Import project meta zip file and restart.", "management.meta.operation_4.desc": "Import project meta zip file and restart.",
"management.meta.operation_5.desc": "Clear all metadata from meta tables." "management.meta.operation_5.desc": "Clear all metadata from meta tables."
} }

2
packages/nc-lib-gui/package.json

@ -1,6 +1,6 @@
{ {
"name": "nc-lib-gui", "name": "nc-lib-gui",
"version": "0.2.3", "version": "0.2.4",
"description": "> TODO: description", "description": "> TODO: description",
"author": "“pranavxc” <pranavxc@gmail.com>", "author": "“pranavxc” <pranavxc@gmail.com>",
"homepage": "https://gitlab.com/xgenecloud-ts/xgenecloud-ts#readme", "homepage": "https://gitlab.com/xgenecloud-ts/xgenecloud-ts#readme",

103
packages/nocodb/src/lib/noco/rest/RestAuthCtrlEE.ts

@ -11,15 +11,17 @@ export default class RestAuthCtrlEE extends RestAuthCtrl {
protected async addAdmin(req, res, next): Promise<any> { protected async addAdmin(req, res, next): Promise<any> {
// if (!this.config?.mailer || !this.emailClient) { const emails = (req.body.email || '').split(/\s*,\s*/).map(v => v.trim());
// return next(new Error('SMTP config is not found'));
// }
const email = req.body.email; // check for invalid emails
const invalidEmails = emails.filter(v => !validator.isEmail(v))
if (!email || !validator.isEmail(email)) { if (!emails.length) {
return next(new Error('Invalid email address')); return next(new Error('Invalid email address'));
} }
if (invalidEmails.length) {
return next(new Error('Invalid email address : ' + invalidEmails.join(', ')));
}
// todo: handle roles which contains super // todo: handle roles which contains super
if (!req.session?.passport?.user?.roles?.owner && req.body.roles.indexOf('owner') > -1) { if (!req.session?.passport?.user?.roles?.owner && req.body.roles.indexOf('owner') > -1) {
@ -27,45 +29,68 @@ export default class RestAuthCtrlEE extends RestAuthCtrl {
} }
const invite_token = uuidv4(); const invite_token = uuidv4();
const error = [];
const user = await this.users.where({email}).first(); for (const email of emails) {
if (user) {
if (!await this.xcMeta.isUserHaveAccessToProject(req.body.project_id, user.id)) {
await this.xcMeta.projectAddUser(req.body.project_id, user.id, 'editor');
}
} else {
try {
await this.users.insert({
invite_token,
invite_token_expires: new Date(Date.now() + (24 * 60 * 60 * 1000)),
email,
roles: 'user'
});
const {id} = await this.users.where({email}).first();
await this.xcMeta.projectAddUser(req.body.project_id, id, req.body.roles);
if (!await this.sendInviteEmail(email, invite_token, req)) { // add user to project if user already exist
res.json({invite_token, email}) const user = await this.users.where({email}).first();
if (user) {
if (!await this.xcMeta.isUserHaveAccessToProject(req.body.project_id, user.id)) {
await this.xcMeta.projectAddUser(req.body.project_id, user.id, 'editor');
}
this.xcMeta.audit(req.body.project_id, null, 'nc_audit', {
op_type: 'AUTHENTICATION',
op_sub_type: 'INVITE',
user: req.user.email,
description: `invited ${email} to ${req.body.project_id} project `, ip: req.clientIp
})
} else {
try {
// create new user with invite token
await this.users.insert({
invite_token,
invite_token_expires: new Date(Date.now() + (24 * 60 * 60 * 1000)),
email,
roles: 'user'
});
const {id} = await this.users.where({email}).first();
// add user to project
await this.xcMeta.projectAddUser(req.body.project_id, id, req.body.roles);
Tele.emit('evt', {evt_type: 'project:invite'})
this.xcMeta.audit(req.body.project_id, null, 'nc_audit', {
op_type: 'AUTHENTICATION',
op_sub_type: 'INVITE',
user: req.user.email,
description: `invited ${email} to ${req.body.project_id} project `, ip: req.clientIp
})
// in case of single user check for smtp failure
// and send back token if failed
if (emails.length === 1 && !await this.sendInviteEmail(email, invite_token, req)) {
return res.json({invite_token, email});
} else {
this.sendInviteEmail(email, invite_token, req)
}
} catch (e) {
if (emails.length === 1) {
return next(e);
} else {
error.push({email, error: e.message})
}
} }
} catch (e) {
return next(e);
} }
}
Tele.emit('evt', {evt_type: 'project:invite'}) }
this.xcMeta.audit(req.body.project_id, null, 'nc_audit', {
op_type: 'AUTHENTICATION',
op_sub_type: 'INVITE',
user: req.user.email,
description: `invited ${email} to ${req.body.project_id} project `, ip: req.clientIp
})
res.json({ if (emails.length === 1) {
msg: 'success' res.json({
}) msg: 'success'
})
} else {
return res.json({invite_token, emails, error});
}
} }
protected async updateAdmin(req, res, next): Promise<any> { protected async updateAdmin(req, res, next): Promise<any> {

Loading…
Cancel
Save