From 429f1d4b38875954fc61dfccaa13a2345341238b Mon Sep 17 00:00:00 2001 From: Peaceultime Date: Thu, 28 Nov 2024 17:18:35 +0100 Subject: [PATCH] Add page and user monitoring in admin. Add permission editing in administration. --- components/base/Collapsible.vue | 5 +- components/base/TagsInput.vue | 21 ++ db.sqlite | Bin 569344 -> 569344 bytes db.sqlite-shm | Bin 32768 -> 32768 bytes db.sqlite-wal | Bin 230752 -> 86552 bytes layouts/default.vue | 35 +-- pages/admin/index.vue | 240 +++++++++++++++++- pages/roadmap.vue | 4 +- pages/user/profile.vue | 14 - server/api/admin/pages.get.ts | 51 +++- .../api/admin/user/[id]/permissions.post.ts | 55 ++++ server/api/admin/users.get.ts | 35 ++- shared/general.utils.ts | 6 +- 13 files changed, 416 insertions(+), 50 deletions(-) create mode 100644 components/base/TagsInput.vue create mode 100644 server/api/admin/user/[id]/permissions.post.ts diff --git a/components/base/Collapsible.vue b/components/base/Collapsible.vue index 4425838..cc735cd 100644 --- a/components/base/Collapsible.vue +++ b/components/base/Collapsible.vue @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/server/api/admin/pages.get.ts b/server/api/admin/pages.get.ts index 569883b..7224b76 100644 --- a/server/api/admin/pages.get.ts +++ b/server/api/admin/pages.get.ts @@ -1,3 +1,50 @@ -export default defineEventHandler((e) => { - return []; +import { ne, sql } from 'drizzle-orm'; +import useDatabase from '~/composables/useDatabase'; +import { explorerContentTable } from '~/db/schema'; +import { hasPermissions } from '~/shared/auth.util'; + +export default defineEventHandler(async (e) => { + const session = await getUserSession(e); + + if(!session || !session.user || !hasPermissions(session.user.permissions, ['admin'])) + { + throw createError({ + statusCode: 401, + message: 'Unauthorized', + }); + } + + const db = useDatabase(); + const content = db.select({ + path: explorerContentTable.path, + owner: explorerContentTable.owner, + title: explorerContentTable.title, + type: explorerContentTable.type, + size: sql`CASE WHEN ${explorerContentTable.content} IS NULL THEN 0 ELSE length(${explorerContentTable.content}) END`.as('size'), + navigable: explorerContentTable.navigable, + private: explorerContentTable.private, + order: explorerContentTable.order, + visit: explorerContentTable.visit, + timestamp: explorerContentTable.timestamp, + }).from(explorerContentTable).all(); + + content.sort((a, b) => { + return a.path.split('/').length - b.path.split('/').length; + }); + + for(let i = 0; i < content.length; i++) + { + const path = content[i].path.substring(0, content[i].path.lastIndexOf('/')); + if(path !== '') + { + const parent = content.find(e => e.path === path); + + if(parent) + { + content[i].private = content[i].private || parent.private; + } + } + } + + return content.filter(e => e.type !== 'folder'); }) \ No newline at end of file diff --git a/server/api/admin/user/[id]/permissions.post.ts b/server/api/admin/user/[id]/permissions.post.ts new file mode 100644 index 0000000..faf7a60 --- /dev/null +++ b/server/api/admin/user/[id]/permissions.post.ts @@ -0,0 +1,55 @@ +import { hasPermissions } from "~/shared/auth.util"; +import useDatabase from '~/composables/useDatabase'; +import { and, eq, notInArray } from "drizzle-orm"; +import { z } from "zod"; +import { userPermissionsTable } from "~/db/schema"; + +const schema = z.array(z.string()); + +export default defineEventHandler(async (e) => { + const session = await getUserSession(e); + + if(!session || !session.user || !hasPermissions(session.user.permissions, ['admin'])) + { + throw createError({ + statusCode: 401, + message: 'Unauthorized', + }); + } + + const param = getRouterParam(e, 'id'); + + if(!param) + { + throw createError({ + statusCode: 403, + message: 'Forbidden', + }); + } + + const body = await readValidatedBody(e, schema.safeParse); + + if(!body.success) + { + throw createError({ + statusCode: 403, + message: 'Forbidden', + }); + } + + try { + const id = parseInt(param, 10); + + const db = useDatabase(); + const permissions = body.data.map(e => ({ id: id, permission: e })); + + db.transaction((tx) => { + tx.delete(userPermissionsTable).where(eq(userPermissionsTable.id, id)).run(); + tx.insert(userPermissionsTable).values(permissions).run(); + }); + } catch(e) { + console.error(e); + + throw e; + } +}); \ No newline at end of file diff --git a/server/api/admin/users.get.ts b/server/api/admin/users.get.ts index 569883b..8ccb302 100644 --- a/server/api/admin/users.get.ts +++ b/server/api/admin/users.get.ts @@ -1,3 +1,34 @@ -export default defineEventHandler((e) => { - return []; +import { sql } from 'drizzle-orm'; +import useDatabase from '~/composables/useDatabase'; +import { userSessionsTable } from '~/db/schema'; +import { hasPermissions } from '~/shared/auth.util'; + +export default defineEventHandler(async (e) => { + const session = await getUserSession(e); + + if(!session || !session.user || !hasPermissions(session.user.permissions, ['admin'])) + { + throw createError({ + statusCode: 401, + message: 'Unauthorized', + }); + } + + const db = useDatabase(); + return db.query.usersTable.findMany({ + columns: { + email: false, + hash: false, + }, + with: { + data: true, + permission: true, + session: { + columns: { + timestamp: false, + user_id: false, + } + } + } + }).sync(); }) \ No newline at end of file diff --git a/shared/general.utils.ts b/shared/general.utils.ts index 8562dc0..06746f4 100644 --- a/shared/general.utils.ts +++ b/shared/general.utils.ts @@ -26,9 +26,9 @@ export function format(date: Date, template: string): string "yyyy": (date: Date) => date.getUTCFullYear().toString(), "MM": (date: Date) => padRight((date.getUTCMonth() + 1).toString(), '0', 2), "dd": (date: Date) => padRight(date.getUTCDate().toString(), '0', 2), - "mm": (date: Date) => padRight(date.getFullYear().toString(), '0', 2), - "HH": (date: Date) => padRight(date.getFullYear().toString(), '0', 2), - "ss": (date: Date) => padRight(date.getFullYear().toString(), '0', 2), + "mm": (date: Date) => padRight(date.getUTCMinutes().toString(), '0', 2), + "HH": (date: Date) => padRight(date.getUTCHours().toString(), '0', 2), + "ss": (date: Date) => padRight(date.getUTCSeconds().toString(), '0', 2), }; const keys = Object.keys(transforms);