some user table fixes
This commit is contained in:
parent
6eb4f189ce
commit
4543b1213f
|
@ -27,6 +27,7 @@
|
|||
import { AppStatus } from "constants"
|
||||
import Logo from "assets/bb-space-man.svg"
|
||||
import AccessFilter from "./_components/AcessFilter.svelte"
|
||||
import { Constants } from "@budibase/frontend-core"
|
||||
|
||||
let sortBy = "name"
|
||||
let template
|
||||
|
@ -68,6 +69,8 @@
|
|||
$: unlocked = lockedApps?.length === 0
|
||||
$: automationErrors = getAutomationErrors(enrichedApps)
|
||||
|
||||
$: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE
|
||||
|
||||
const enrichApps = (apps, user, sortBy) => {
|
||||
const enrichedApps = apps.map(app => ({
|
||||
...app,
|
||||
|
@ -355,7 +358,7 @@
|
|||
</Button>
|
||||
{/if}
|
||||
<div class="filter">
|
||||
{#if $groups.length}
|
||||
{#if isProPlan && $groups.length}
|
||||
<AccessFilter on:change={accessFilterAction} />
|
||||
{/if}
|
||||
<Select
|
||||
|
|
|
@ -9,12 +9,14 @@
|
|||
Tags,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import { groups } from "stores/portal"
|
||||
import { groups, auth } from "stores/portal"
|
||||
import { onMount } from "svelte"
|
||||
import { Constants } from "@budibase/frontend-core"
|
||||
import CreateEditGroupModal from "./_components/CreateEditGroupModal.svelte"
|
||||
|
||||
import UserGroupsRow from "./_components/UserGroupsRow.svelte"
|
||||
|
||||
$: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE
|
||||
|
||||
let modal
|
||||
let group = {
|
||||
name: "",
|
||||
|
@ -23,7 +25,6 @@
|
|||
users: [],
|
||||
apps: [],
|
||||
}
|
||||
let proPlan = true
|
||||
|
||||
async function deleteGroup(group) {
|
||||
try {
|
||||
|
@ -54,7 +55,7 @@
|
|||
<Layout gap="XS" noPadding>
|
||||
<div style="display: flex;">
|
||||
<Heading size="M">User groups</Heading>
|
||||
{#if !proPlan}
|
||||
{#if !isProPlan}
|
||||
<Tags>
|
||||
<div class="tags">
|
||||
<div class="tag">
|
||||
|
@ -68,13 +69,15 @@
|
|||
</Layout>
|
||||
<div class="align-buttons">
|
||||
<Button
|
||||
icon={proPlan ? "UserGroup" : ""}
|
||||
cta={proPlan}
|
||||
newStyles
|
||||
icon={isProPlan ? "UserGroup" : ""}
|
||||
cta={isProPlan}
|
||||
on:click={() => modal.show()}
|
||||
>{proPlan ? "Create user group" : "Upgrade Account"}</Button
|
||||
>{isProPlan ? "Create user group" : "Upgrade Account"}</Button
|
||||
>
|
||||
{#if !proPlan}
|
||||
{#if !isProPlan}
|
||||
<Button
|
||||
newStyles
|
||||
secondary
|
||||
on:click={() => {
|
||||
window.open("https://budibase.com/pricing/", "_blank")
|
||||
|
@ -83,7 +86,7 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
{#if proPlan}
|
||||
{#if isProPlan}
|
||||
<div class="groupTable">
|
||||
{#each $groups as group}
|
||||
<div>
|
||||
|
|
|
@ -40,6 +40,8 @@
|
|||
let selectedGroups = []
|
||||
let allAppList = []
|
||||
|
||||
$: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE
|
||||
|
||||
$: allAppList = $apps
|
||||
.filter(x => {
|
||||
if ($userFetch.data?.roles) {
|
||||
|
@ -244,52 +246,53 @@
|
|||
</div>
|
||||
</Layout>
|
||||
|
||||
<!-- User groups -->
|
||||
<Layout gap="XS" noPadding>
|
||||
<div class="tableTitle">
|
||||
<div>
|
||||
<Heading size="XS">User groups</Heading>
|
||||
<Body size="S">Add or remove this user from user groups</Body>
|
||||
</div>
|
||||
<div bind:this={popoverAnchor}>
|
||||
<Button on:click={popover.show()} icon="UserGroup" cta
|
||||
>Add User Group</Button
|
||||
>
|
||||
</div>
|
||||
<Popover align="right" bind:this={popover} anchor={popoverAnchor}>
|
||||
<UserGroupPicker
|
||||
key={"name"}
|
||||
title={"Group"}
|
||||
bind:searchTerm
|
||||
bind:selected={selectedGroups}
|
||||
bind:filtered={filteredGroups}
|
||||
{addAll}
|
||||
select={addGroup}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
<List>
|
||||
{#if userGroups.length}
|
||||
{#each userGroups as group}
|
||||
<ListItem
|
||||
title={group.name}
|
||||
icon={group.icon}
|
||||
iconBackground={group.color}
|
||||
><Icon
|
||||
on:click={removeGroup(group._id)}
|
||||
hoverable
|
||||
size="L"
|
||||
name="Close"
|
||||
/></ListItem
|
||||
{#if isProPlan}
|
||||
<!-- User groups -->
|
||||
<Layout gap="XS" noPadding>
|
||||
<div class="tableTitle">
|
||||
<div>
|
||||
<Heading size="XS">User groups</Heading>
|
||||
<Body size="S">Add or remove this user from user groups</Body>
|
||||
</div>
|
||||
<div bind:this={popoverAnchor}>
|
||||
<Button on:click={popover.show()} icon="UserGroup" cta
|
||||
>Add User Group</Button
|
||||
>
|
||||
{/each}
|
||||
{:else}
|
||||
<ListItem icon="UserGroup" title="No groups" />
|
||||
{/if}
|
||||
</List>
|
||||
</Layout>
|
||||
</div>
|
||||
<Popover align="right" bind:this={popover} anchor={popoverAnchor}>
|
||||
<UserGroupPicker
|
||||
key={"name"}
|
||||
title={"Group"}
|
||||
bind:searchTerm
|
||||
bind:selected={selectedGroups}
|
||||
bind:filtered={filteredGroups}
|
||||
{addAll}
|
||||
select={addGroup}
|
||||
/>
|
||||
</Popover>
|
||||
</div>
|
||||
|
||||
<List>
|
||||
{#if userGroups.length}
|
||||
{#each userGroups as group}
|
||||
<ListItem
|
||||
title={group.name}
|
||||
icon={group.icon}
|
||||
iconBackground={group.color}
|
||||
><Icon
|
||||
on:click={removeGroup(group._id)}
|
||||
hoverable
|
||||
size="L"
|
||||
name="Close"
|
||||
/></ListItem
|
||||
>
|
||||
{/each}
|
||||
{:else}
|
||||
<ListItem icon="UserGroup" title="No groups" />
|
||||
{/if}
|
||||
</List>
|
||||
</Layout>
|
||||
{/if}
|
||||
<!-- User Apps -->
|
||||
<Layout gap="S" noPadding>
|
||||
<div class="appsTitle">
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
InputDropdown,
|
||||
Layout,
|
||||
} from "@budibase/bbui"
|
||||
import { groups } from "stores/portal"
|
||||
import { groups, auth } from "stores/portal"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { Constants } from "@budibase/frontend-core"
|
||||
|
||||
|
@ -17,6 +17,8 @@
|
|||
let disabled
|
||||
let userGroups = []
|
||||
|
||||
$: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE
|
||||
|
||||
$: userData = [
|
||||
{
|
||||
email: "",
|
||||
|
@ -67,14 +69,16 @@
|
|||
</div>
|
||||
</Layout>
|
||||
|
||||
<Multiselect
|
||||
bind:value={userGroups}
|
||||
placeholder="Select User Groups"
|
||||
label="User Groups"
|
||||
options={$groups}
|
||||
getOptionLabel={option => option.name}
|
||||
getOptionValue={option => option._id}
|
||||
/>
|
||||
{#if isProPlan}
|
||||
<Multiselect
|
||||
bind:value={userGroups}
|
||||
placeholder="Select User Groups"
|
||||
label="User Groups"
|
||||
options={$groups}
|
||||
getOptionLabel={option => option.name}
|
||||
getOptionValue={option => option._id}
|
||||
/>
|
||||
{/if}
|
||||
</ModalContent>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
Multiselect,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import { groups } from "stores/portal"
|
||||
import { groups, auth } from "stores/portal"
|
||||
import { emailValidator } from "../../../../../../helpers/validation"
|
||||
|
||||
import { Constants } from "@budibase/frontend-core"
|
||||
|
||||
const BYTES_IN_MB = 1000000
|
||||
|
@ -21,7 +20,9 @@
|
|||
let userEmails = []
|
||||
let userGroups = []
|
||||
let usersRole = null
|
||||
|
||||
$: invalidEmails = []
|
||||
$: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE
|
||||
|
||||
const validEmails = userEmails => {
|
||||
for (const email of userEmails) {
|
||||
|
@ -86,14 +87,16 @@
|
|||
options={Constants.BuilderRoleDescriptions}
|
||||
/>
|
||||
|
||||
<Multiselect
|
||||
bind:value={userGroups}
|
||||
placeholder="Select User Groups"
|
||||
label="User Groups"
|
||||
options={$groups}
|
||||
getOptionLabel={option => option.name}
|
||||
getOptionValue={option => option._id}
|
||||
/>
|
||||
{#if isProPlan}
|
||||
<Multiselect
|
||||
bind:value={userGroups}
|
||||
placeholder="Select User Groups"
|
||||
label="User Groups"
|
||||
options={$groups}
|
||||
getOptionLabel={option => option.name}
|
||||
getOptionValue={option => option._id}
|
||||
/>
|
||||
{/if}
|
||||
</ModalContent>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
{value}
|
||||
{:else}
|
||||
<div class="text">Invite pending...</div>
|
||||
<div class="text">Not Available</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
Label,
|
||||
} from "@budibase/bbui"
|
||||
import AddUserModal from "./_components/AddUserModal.svelte"
|
||||
import { users, groups } from "stores/portal"
|
||||
import { users, groups, auth } from "stores/portal"
|
||||
import { onMount } from "svelte"
|
||||
import DeleteRowsButton from "components/backend/DataTable/buttons/DeleteRowsButton.svelte"
|
||||
import GroupsTableRenderer from "./_components/GroupsTableRenderer.svelte"
|
||||
import AppsTableRenderer from "./_components/AppsTableRenderer.svelte"
|
||||
import NameTableRenderer from "./_components/NameTableRenderer.svelte"
|
||||
|
@ -27,25 +28,7 @@
|
|||
import PasswordModal from "./_components/PasswordModal.svelte"
|
||||
import ImportUsersModal from "./_components/ImportUsersModal.svelte"
|
||||
import { createPaginationStore } from "helpers/pagination"
|
||||
|
||||
const schema = {
|
||||
name: {},
|
||||
email: {},
|
||||
role: {
|
||||
noPropagation: true,
|
||||
sortable: false,
|
||||
},
|
||||
userGroups: { sortable: false, displayName: "User groups" },
|
||||
apps: { width: "120px" },
|
||||
settings: {
|
||||
sortable: false,
|
||||
width: "60px",
|
||||
displayName: "",
|
||||
align: "Right",
|
||||
},
|
||||
}
|
||||
|
||||
$: userData = []
|
||||
import { Constants } from "@budibase/frontend-core"
|
||||
|
||||
const accessTypes = [
|
||||
{
|
||||
|
@ -74,6 +57,38 @@
|
|||
let prevEmail = undefined,
|
||||
searchEmail = undefined
|
||||
|
||||
let selectedRows = []
|
||||
let customRenderers = [
|
||||
{ column: "userGroups", component: GroupsTableRenderer },
|
||||
{ column: "apps", component: AppsTableRenderer },
|
||||
{ column: "name", component: NameTableRenderer },
|
||||
{ column: "settings", component: SettingsTableRenderer },
|
||||
{ column: "role", component: RoleTableRenderer },
|
||||
]
|
||||
|
||||
$: isProPlan = $auth.user?.license.plan.type !== Constants.PlanType.FREE
|
||||
|
||||
$: schema = {
|
||||
name: {},
|
||||
email: {},
|
||||
role: {
|
||||
noPropagation: true,
|
||||
sortable: false,
|
||||
},
|
||||
...(isProPlan && {
|
||||
userGroups: { sortable: false, displayName: "User groups" },
|
||||
}),
|
||||
apps: { width: "120px" },
|
||||
settings: {
|
||||
sortable: false,
|
||||
width: "60px",
|
||||
displayName: "",
|
||||
align: "Right",
|
||||
},
|
||||
}
|
||||
|
||||
$: userData = []
|
||||
|
||||
$: page = $pageInfo.page
|
||||
$: fetchUsers(page, searchEmail)
|
||||
|
||||
|
@ -164,6 +179,19 @@
|
|||
}
|
||||
})
|
||||
|
||||
const deleteRows = async () => {
|
||||
try {
|
||||
let ids = selectedRows.map(user => user._id)
|
||||
await users.bulkDelete(ids)
|
||||
notifications.success(`Successfully deleted ${selectedRows.length} rows`)
|
||||
selectedRows = []
|
||||
await fetchUsers(page, searchEmail)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
notifications.error("Error deleting rows")
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchUsers(page, email) {
|
||||
if ($pageInfo.loading) {
|
||||
return
|
||||
|
@ -211,26 +239,25 @@
|
|||
<Button on:click={importUsersModal.show} icon="Import" primary
|
||||
>Import Users</Button
|
||||
>
|
||||
|
||||
<div class="field">
|
||||
<Label size="L">Search email</Label>
|
||||
<Search bind:value={searchEmail} placeholder="" />
|
||||
</div>
|
||||
{#if selectedRows.length > 0}
|
||||
<DeleteRowsButton on:updaterows {selectedRows} {deleteRows} />
|
||||
{/if}
|
||||
</ButtonGroup>
|
||||
<Table
|
||||
on:click={({ detail }) => $goto(`./${detail._id}`)}
|
||||
{schema}
|
||||
bind:selectedRows
|
||||
data={enrichedUsers}
|
||||
allowEditColumns={false}
|
||||
allowEditRows={false}
|
||||
allowSelectRows={true}
|
||||
showHeaderBorder={false}
|
||||
customRenderers={[
|
||||
{ column: "userGroups", component: GroupsTableRenderer },
|
||||
{ column: "apps", component: AppsTableRenderer },
|
||||
{ column: "name", component: NameTableRenderer },
|
||||
{ column: "settings", component: SettingsTableRenderer },
|
||||
{ column: "role", component: RoleTableRenderer },
|
||||
]}
|
||||
{customRenderers}
|
||||
/>
|
||||
<div class="pagination">
|
||||
<Pagination
|
||||
|
|
|
@ -8,13 +8,15 @@
|
|||
ListItem,
|
||||
Modal,
|
||||
notifications,
|
||||
Pagination,
|
||||
} from "@budibase/bbui"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
import RoleSelect from "components/common/RoleSelect.svelte"
|
||||
import { users, groups, apps } from "stores/portal"
|
||||
import { users, groups, apps, auth } from "stores/portal"
|
||||
import AssignmentModal from "./AssignmentModal.svelte"
|
||||
import { createPaginationStore } from "helpers/pagination"
|
||||
import { Constants } from "@budibase/frontend-core"
|
||||
|
||||
export let app
|
||||
|
||||
|
@ -22,11 +24,11 @@
|
|||
let appGroups = []
|
||||
let appUsers = []
|
||||
let pageInfo = createPaginationStore()
|
||||
let prevSearch = undefined,
|
||||
search = undefined
|
||||
|
||||
$: page = $pageInfo.page
|
||||
$: fetchUsers(page, search)
|
||||
$: fetchUsers(page)
|
||||
|
||||
$: isProPlan = $auth.user?.license.plan.type === Constants.PlanType.FREE
|
||||
|
||||
$: appUsers =
|
||||
$users.data?.filter(x => {
|
||||
|
@ -41,6 +43,19 @@
|
|||
})
|
||||
})
|
||||
|
||||
$: filteredUsers =
|
||||
$users.data?.filter(x => {
|
||||
return !Object.keys(x.roles).find(y => {
|
||||
return extractAppId(y) === extractAppId(app.appId)
|
||||
})
|
||||
}) || []
|
||||
|
||||
$: filteredGroups = $groups.filter(element => {
|
||||
return !element.apps.find(y => {
|
||||
return y.appId === app.appId
|
||||
})
|
||||
})
|
||||
|
||||
function extractAppId(id) {
|
||||
const split = id?.split("_") || []
|
||||
return split.length ? split[split.length - 1] : null
|
||||
|
@ -70,26 +85,27 @@
|
|||
await users.save(newUser)
|
||||
}
|
||||
})
|
||||
await pageInfo.reset()
|
||||
await groups.actions.init()
|
||||
await users.search({ page, appId: app.appId })
|
||||
}
|
||||
/*
|
||||
async function updateRole(user) {
|
||||
console.log(user)
|
||||
|
||||
async function updateUserRole(role, user) {
|
||||
user.roles[app.appId] = role
|
||||
users.save(user)
|
||||
}
|
||||
*/
|
||||
async function fetchUsers(page, search) {
|
||||
|
||||
async function updateGroupRole(role, group) {
|
||||
group.role = role
|
||||
groups.actions.save(group)
|
||||
}
|
||||
|
||||
async function fetchUsers(page) {
|
||||
if ($pageInfo.loading) {
|
||||
return
|
||||
}
|
||||
// need to remove the page if they've started searching
|
||||
if (search && !prevSearch) {
|
||||
pageInfo.reset()
|
||||
page = undefined
|
||||
}
|
||||
prevSearch = search
|
||||
try {
|
||||
pageInfo.loading()
|
||||
await users.search({ page, search })
|
||||
await users.search({ page, appId: app.appId })
|
||||
pageInfo.fetched($users.hasNextPage, $users.nextPage)
|
||||
} catch (error) {
|
||||
notifications.error("Error getting user list")
|
||||
|
@ -120,21 +136,29 @@
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
<List title="User Groups">
|
||||
{#each appGroups as group}
|
||||
<ListItem
|
||||
title={group.name}
|
||||
icon={group.icon}
|
||||
iconBackground={group.color}
|
||||
>
|
||||
<RoleSelect autoWidth quiet value={group.role} />
|
||||
</ListItem>
|
||||
{/each}
|
||||
</List>
|
||||
{#if isProPlan}
|
||||
<List title="User Groups">
|
||||
{#each appGroups as group}
|
||||
<ListItem
|
||||
title={group.name}
|
||||
icon={group.icon}
|
||||
iconBackground={group.color}
|
||||
>
|
||||
<RoleSelect
|
||||
on:change={e => updateGroupRole(e.detail, group)}
|
||||
autoWidth
|
||||
quiet
|
||||
value={group.role}
|
||||
/>
|
||||
</ListItem>
|
||||
{/each}
|
||||
</List>
|
||||
{/if}
|
||||
<List title="Users">
|
||||
{#each appUsers as user}
|
||||
<ListItem title={user.email} avatar>
|
||||
<RoleSelect
|
||||
on:change={e => updateUserRole(e.detail, user)}
|
||||
autoWidth
|
||||
quiet
|
||||
value={user.roles[
|
||||
|
@ -146,6 +170,15 @@
|
|||
</ListItem>
|
||||
{/each}
|
||||
</List>
|
||||
<div class="pagination">
|
||||
<Pagination
|
||||
page={$pageInfo.pageNumber}
|
||||
hasPrevPage={$pageInfo.loading ? false : $pageInfo.hasPrevPage}
|
||||
hasNextPage={$pageInfo.loading ? false : $pageInfo.hasNextPage}
|
||||
goToPrevPage={pageInfo.prevPage}
|
||||
goToNextPage={pageInfo.nextPage}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="align">
|
||||
<Layout gap="S">
|
||||
|
@ -167,7 +200,11 @@
|
|||
</div>
|
||||
|
||||
<Modal bind:this={assignmentModal}>
|
||||
<AssignmentModal userData={$users.data} {addData} />
|
||||
<AssignmentModal
|
||||
userData={filteredUsers.length ? filteredUsers : $users.data}
|
||||
groups={isProPlan ? filteredGroups : []}
|
||||
{addData}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
<script>
|
||||
import { ModalContent, PickerDropdown, ActionButton } from "@budibase/bbui"
|
||||
import { groups } from "stores/portal"
|
||||
import { roles } from "stores/backend"
|
||||
import { RoleUtils } from "@budibase/frontend-core"
|
||||
|
||||
export let addData
|
||||
export let userData = []
|
||||
export let groups = []
|
||||
|
||||
$: optionSections = {
|
||||
groups: {
|
||||
data: $groups,
|
||||
getLabel: group => group.name,
|
||||
getValue: group => group._id,
|
||||
getIcon: group => group.icon,
|
||||
getColour: group => group.color,
|
||||
},
|
||||
...(groups.length && {
|
||||
groups: {
|
||||
data: groups,
|
||||
getLabel: group => group.name,
|
||||
getValue: group => group._id,
|
||||
getIcon: group => group.icon,
|
||||
getColour: group => group.color,
|
||||
},
|
||||
}),
|
||||
users: {
|
||||
data: userData,
|
||||
getLabel: user => user.email,
|
||||
|
|
|
@ -75,6 +75,10 @@ export function createUsersStore() {
|
|||
update(users => users.filter(user => user._id !== id))
|
||||
}
|
||||
|
||||
async function bulkDelete(userIds) {
|
||||
await API.deleteUsers(userIds)
|
||||
}
|
||||
|
||||
async function save(user) {
|
||||
return await API.saveUser(user)
|
||||
}
|
||||
|
@ -87,6 +91,7 @@ export function createUsersStore() {
|
|||
acceptInvite,
|
||||
create,
|
||||
save,
|
||||
bulkDelete,
|
||||
delete: del,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,19 @@ export const buildUserEndpoints = API => ({
|
|||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes multiple users
|
||||
* @param userId the ID of the user to delete
|
||||
*/
|
||||
deleteUsers: async userIds => {
|
||||
return await API.post({
|
||||
url: `/api/global/users/bulkDelete`,
|
||||
body: {
|
||||
userIds,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Invites a user to the current tenant.
|
||||
* @param email the email address to send the invitation to
|
||||
|
|
|
@ -84,6 +84,13 @@ export const BuilderRoleDescriptions = [
|
|||
},
|
||||
]
|
||||
|
||||
export const PlanType = {
|
||||
FREE: "free",
|
||||
TEAM: "team",
|
||||
BUSINESS: "business",
|
||||
ENTERPRISE: "enterprise",
|
||||
}
|
||||
|
||||
/**
|
||||
* API version header attached to all requests.
|
||||
* Version changelog:
|
||||
|
|
|
@ -73,6 +73,7 @@ const checkAuthorizedResource = async (
|
|||
|
||||
export = (permType: any, permLevel: any = null, opts = { schema: false }) =>
|
||||
async (ctx: any, next: any) => {
|
||||
console.log(ctx)
|
||||
// webhooks don't need authentication, each webhook unique
|
||||
// also internal requests (between services) don't need authorized
|
||||
if (isWebhookEndpoint(ctx) || ctx.internal) {
|
||||
|
|
|
@ -31,7 +31,7 @@ export const bulkSave = async (ctx: any) => {
|
|||
newUsers.forEach((user: any) => {
|
||||
usersToSave.push(
|
||||
users.save(user, {
|
||||
hashPassword: false,
|
||||
hashPassword: true,
|
||||
requirePassword: user.requirePassword,
|
||||
bulkCreate: true,
|
||||
})
|
||||
|
@ -53,10 +53,11 @@ export const bulkSave = async (ctx: any) => {
|
|||
delete user.password
|
||||
})
|
||||
|
||||
groupsToSave.forEach(async group => {
|
||||
group.users = [...group.users, ...allUsers]
|
||||
await db.put(group)
|
||||
})
|
||||
if (groupsToSave.length)
|
||||
groupsToSave.forEach(async group => {
|
||||
group.users = [...group.users, ...allUsers]
|
||||
await db.put(group)
|
||||
})
|
||||
|
||||
ctx.body = response
|
||||
} catch (err: any) {
|
||||
|
@ -130,6 +131,19 @@ export const destroy = async (ctx: any) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const bulkDelete = async (ctx: any) => {
|
||||
const { userIds } = ctx.request.body
|
||||
|
||||
let deleted = 0
|
||||
userIds.forEach(async (id: any) => {
|
||||
await users.destroy(id, ctx.user)
|
||||
deleted++
|
||||
})
|
||||
ctx.body = {
|
||||
message: `${deleted} user(s) deleted`,
|
||||
}
|
||||
}
|
||||
|
||||
export const search = async (ctx: any) => {
|
||||
const paginated = await users.paginatedUsers(ctx.request.body)
|
||||
// user hashed password shouldn't ever be returned
|
||||
|
|
|
@ -14,6 +14,7 @@ function buildGroupSaveValidation() {
|
|||
color: Joi.string().required(),
|
||||
icon: Joi.string().required(),
|
||||
name: Joi.string().required(),
|
||||
role: Joi.string().optional(),
|
||||
users: Joi.array().optional(),
|
||||
apps: Joi.array().optional(),
|
||||
createdAt: Joi.string().optional(),
|
||||
|
|
|
@ -63,6 +63,7 @@ router
|
|||
.get("/api/global/users", builderOrAdmin, controller.fetch)
|
||||
.post("/api/global/users/search", builderOrAdmin, controller.search)
|
||||
.delete("/api/global/users/:id", adminOnly, controller.destroy)
|
||||
.post("/api/global/users/bulkDelete", adminOnly, controller.bulkDelete)
|
||||
.get("/api/global/roles/:appId")
|
||||
.post(
|
||||
"/api/global/users/invite",
|
||||
|
|
Loading…
Reference in New Issue