Skip to content

Commit

Permalink
feat: admin users can update some user details (#371)
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-clegg authored Sep 29, 2024
1 parent 7c8dfbc commit bf2abb0
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 14 deletions.
5 changes: 5 additions & 0 deletions directus/extensions/directus-extension-mcc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@
"type": "hook",
"name": "redirect-hook",
"source": "src/redirect-hook/index.ts"
},
{
"type": "endpoint",
"name": "user-admin",
"source": "src/user-admin/index.ts"
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {defineEndpoint} from "@directus/extensions-sdk";
import {update} from "./update";

export default defineEndpoint((router, {services, database}) => {
router.post("/update-user", async (req: any, res: any) => {
return await update(req, res, services, database);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {AdminAccountability, userHasRole} from "../utils";

export async function update(req: any, res: any, services: any, database: any) {
const {
UsersService
} = services;

try {
const userToUpdate = req.body;
const userId = req.accountability.user;

if (!userToUpdate) {
return res.status(400).send("Missing user");
}

const userHasPermission = await userHasRole(req, services, database, userId, ["Administrator", "Committee"]);

if (!userHasPermission) {
return res.status(401).send("You are not allowed to update user details");
}

const userService = new UsersService({
knex: database,
schema: req.schema,
accountability: AdminAccountability
});

const existingUser = await userService.readOne(userToUpdate.id);
if (!existingUser) {
return res.status(400).send("Cannot update user that does not exist");
}

const canUpdateUser = !(await userHasRole(req, services, database, userToUpdate.id, ["Administrator", "Unverified", "Junior"]));

if (!canUpdateUser) {
return res.status(401).send("This user cannot be updated");
}

await userService.updateOne(userToUpdate.id, userToUpdate);
return res.status(200).send("User updated successfully");
} catch (err: any) {
onsole.error("Error updating user from admin area", err);
return res.status(500).send("Error updating user from admin area");
}
}
60 changes: 60 additions & 0 deletions directus/extensions/directus-extension-mcc/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
export const AdminAccountability = {
admin: true
};

export async function userHasRole(req: any, services: any, database: any, userId: string, roles: string[]) {
console.log("checking is user has role", userId, roles);
const {
UsersService
} = services;

const userService = new UsersService({
knex: database,
schema: req.schema,
accountability: AdminAccountability
});

const user = await userService.readOne(userId, {
fields: ["role.name"]
});

console.log("found user", user);
const includes = roles.map(r => r.toLowerCase()).includes(user.role.name.toLowerCase());

console.log("result", includes);
return includes;
}

export async function isUserLeader(req: any, services: any, database: any, eventId: string, userId: string) {
console.log("checking if user is leader");
const {
ItemsService
} = services;

const eventLeadersService = new ItemsService("events_directus_users", {
knex: database,
schema: req.schema,
accountability: AdminAccountability
});

const leaders = await eventLeadersService.readByQuery({
fields: ["*", "directus_users_id.first_name", "directus_users_id.last_name", "directus_users_id.avatar", "directus_users_id.id"],
filter: {
events_id: {
_eq: eventId
}
}
});

console.log("leaders", leaders);

if (!leaders || leaders.length === 0) {
return false;
}

console.log("got leaders", leaders);

const userIsLeader = leaders.find((x: any) => x.directus_users_id.id === userId);
console.log("result", !!userIsLeader);
return !!userIsLeader;
}
16 changes: 9 additions & 7 deletions frontend/src/components/admin/UserEditModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import type {DirectusUser} from "nuxt-directus/dist/runtime/types";
import {UserIcon} from '@heroicons/vue/24/outline'
const emits = defineEmits(["close"]);
const emits = defineEmits(["close", "save"]);
const props = defineProps<{
user?: DirectusUser,
Expand All @@ -22,13 +22,13 @@ function close() {
}
const directus = useDirectus();
const {updateUser} = useDirectusUsers();
const user = useDirectusUser();
const {newError} = useErrors();
const {data: roles} = await useAsyncData("directus-roles", async () => await fetchRoles());
const canEditUser = computed(() => {
return editingUser.value.role.name !== "Administrator";
return editingUser.value.role.name !== "Administrator" && editingUser.value.id !== user.value.id;
});
async function fetchRoles() {
Expand All @@ -54,14 +54,16 @@ const rolesOptions = computed(() => {
async function save() {
try {
await updateUser({
id: editingUser.value.id,
user: {
await directus("/user-admin/update-user", {
method: "POST",
body: {
id: editingUser.value.id,
role: editingUser.value.role.id,
bc_number: editingUser.value.bc_number,
club_number: editingUser.value.club_number
}
});
})
emits("save");
close();
} catch (err: any) {
console.error("Error updating user:", err);
Expand Down
30 changes: 24 additions & 6 deletions frontend/src/components/admin/UsersTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defineProps<{
searchLoading: boolean
}>();
const emits = defineEmits(["search"])
const emits = defineEmits(["search", "save"])
const page = defineModel("page", {required: true});
const searchQuery = defineModel("searchQuery", {required: true});
Expand Down Expand Up @@ -38,8 +38,20 @@ function onSearch() {
emits("search");
}
function timeSinceLastLogin(user: DirectusUser){
return formatDistanceToNow(new Date(user.last_access), { addSuffix: true});
function timeSinceLastLogin(user: DirectusUser) {
if(user.role.name === "Junior"){
return null;
}
if (!user.last_access) {
return "Never"
}
// todo clear search button
return formatDistanceToNow(new Date(user.last_access), {addSuffix: true});
}
function onUserSave() {
emits("save");
}
</script>
Expand All @@ -64,8 +76,12 @@ function timeSinceLastLogin(user: DirectusUser){
<thead>
<tr>
<th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-0">Name</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:table-cell">Email</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:table-cell">Last access</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:table-cell">
Email
</th>
<th scope="col" class="hidden px-3 py-3.5 text-left text-sm font-semibold text-gray-900 sm:table-cell">Last
access
</th>
<th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">Role</th>
<th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-0">
<span class="sr-only">Edit</span>
Expand Down Expand Up @@ -108,7 +124,9 @@ function timeSinceLastLogin(user: DirectusUser){
:page="page"
@prev="onPrevPage"
@next="onNextPage"/>
<UserEditModal :user="editingUser" :open="editingUserModalOpen"
<UserEditModal :user="editingUser"
:open="editingUserModalOpen"
@save="onUserSave"
@close="onModalClose"/>
</div>
</template>
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/middleware/admin-area.global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
export default defineNuxtRouteMiddleware((to, from) => {
if(from.path.toLowerCase().startsWith("/admin")){
const user = useDirectusUser();

if (!user.value) {
return navigateTo(`/login?redirect=${encodeURIComponent(to.fullPath)}`);
}

if (!hasRole(user.value, "Committee")) {
return createError({
statusCode: 401,
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/pages/admin/users.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ const {newError} = useErrors();
const users = computed(() => userData.value.data.map(u => ({
fullName: `${u.first_name} ${u.last_name}`,
...u
...u,
role: {
...u.role,
name: u.role.name === "Unapproved" ? "Non-member" : u.role.name,
}
})));
const totalItems = computed(() => userData.value.meta.filter_count);
Expand Down Expand Up @@ -76,6 +80,10 @@ async function onSearch() {
}
}
async function onSave(){
await refresh();
}
watch(page, async () => {
await refresh();
});
Expand All @@ -89,6 +97,7 @@ watch(page, async () => {
:search-loading="searchLoading"
:total-items="totalItems"
:items-per-page="itemsPerPage"
@save="onSave"
@search="onSearch"/>
</template>

Expand Down

0 comments on commit bf2abb0

Please sign in to comment.