From 4dae7025b532d5d069eee44ac2a37880047506d6 Mon Sep 17 00:00:00 2001 From: "max.tyson" Date: Wed, 21 Feb 2024 09:45:06 +1300 Subject: [PATCH] Admin Page --- website/src/lib/users.ts | 2 + website/src/pages/admin/index.tsx | 2 +- website/src/pages/admin/users.tsx | 107 +++++++++++++++++----- website/src/pages/api/auth/random.ts | 6 +- website/src/pages/api/user/update.ts | 28 +++++- website/src/styles/pages/admin.module.css | 10 ++ 6 files changed, 128 insertions(+), 27 deletions(-) diff --git a/website/src/lib/users.ts b/website/src/lib/users.ts index 91d4906..0ca9e51 100644 --- a/website/src/lib/users.ts +++ b/website/src/lib/users.ts @@ -119,6 +119,7 @@ export interface UserPermissions { update: { publicAccess: boolean; internalAccess: boolean; + admin: boolean; }; }; }; @@ -264,6 +265,7 @@ export const getDefaultPermissions = () : UserPermissions => { update: { publicAccess: false, internalAccess: true, + admin: false, }, }, diff --git a/website/src/pages/admin/index.tsx b/website/src/pages/admin/index.tsx index 71d0426..75dd176 100644 --- a/website/src/pages/admin/index.tsx +++ b/website/src/pages/admin/index.tsx @@ -101,7 +101,7 @@ export default function Admin(){ setLoadingMessage("Fetching logs...") - const aplQuery = "['vercel'] | where message contains '' | limit 100" + const aplQuery = "['vercel'] | where message contains '' and not(['vercel.source'] contains 'build') | top 100 by _time desc" const res = await client.query(aplQuery); if (!res.matches || res.matches.length === 0) { diff --git a/website/src/pages/admin/users.tsx b/website/src/pages/admin/users.tsx index edd3b36..0842d64 100644 --- a/website/src/pages/admin/users.tsx +++ b/website/src/pages/admin/users.tsx @@ -10,7 +10,7 @@ import {loginSection} from "@/pages/account"; import {useSession} from "next-auth/react"; import {Loading} from "@/components/loading"; import {Error} from "@/components/error"; -import {checkUserPermissions, RongoaUser} from "@/lib/users"; +import {ADMIN_USER_TYPE, checkUserPermissions, EDITOR_USER_TYPE, MEMBER_USER_TYPE, RongoaUser} from "@/lib/users"; import {makeCachedRequest, makeRequestWithToken} from "@/lib/api_tools"; import {globalStyles} from "@/lib/global_css"; import { useLogger } from 'next-axiom'; @@ -25,7 +25,7 @@ export default function Admin(){ const { data: session } = useSession() // Stats - const [pla, setPlants] = useState([] as PlantData[]) + const [users, setUsers] = useState([] as RongoaUser[]) // Load the data const [loading, setLoading] = useState(true) @@ -64,26 +64,85 @@ export default function Admin(){ setLoading(true) // Download the plants from the database - const plants = await makeCachedRequest("plant_admin_data", "/api/plants/search?getNames=true&getExtras=true&mushrooms=include") - if(!plants){ - setError("Failed to fetch the plant data.") + const users = await makeCachedRequest("user_admin_data", "/api/auth/random?amount=9999999&extraData=true") + if(!users){ + setError("Failed to fetch the user data.") setLoading(false) return } - // Convert the data to the PlantData type - const plantData = plants as PlantData[] - for(let i = 0; i < plantData.length; i++) - plantData[i] = macronsForDisplay(plantData[i]) + // Set the users + let userData = users as RongoaUser[] + for(let i = 0; i < userData.length; i++){ + userData[i].database = userData[i] as any; + } + + setUsers(userData) + setLoading(false) + } + + + const updateUser = async (id: number) => { + setLoading(true) + setLoadingMessage("Updating user...") + + // Get the input values + const name = (document.getElementById(`name_${id}`) as HTMLInputElement).value + const email = (document.getElementById(`email_${id}`) as HTMLInputElement).value + const type = (document.getElementById(`type_${id}`) as HTMLInputElement).value + let permValue = 0 + + // Check the if correct type + switch (type){ + case "Admin": + permValue = ADMIN_USER_TYPE + break + case "Editor": + permValue = EDITOR_USER_TYPE + break + case "Member": + permValue = MEMBER_USER_TYPE + break + default: + setError("Invalid user type") + setLoading(false) + return + } - console.log(plantData) + // Check if name is not empty + if(name == ""){ + setError("Name cannot be empty") + setLoading(false) + return + } + + // Check if email is not empty + if(email == ""){ + setError("Email cannot be empty") + setLoading(false) + return + } + + const adminData = { + id: id, + name: name, + email: email, + type: permValue + } + + const response = await makeRequestWithToken("get", "/api/user/update?adminData=" + JSON.stringify(adminData)) + + // Remove the item in the local storage + localStorage.removeItem("user_admin_data") - // Set the plant data - setPlants(plantData) setLoading(false) } + const reload = () => { + window.location.reload() + } + const adminPage = () => { return ( <> @@ -112,24 +171,27 @@ export default function Admin(){
+

Type can be Admin, Member or Editor

+ + - - + - {pla.map((plant, index) => ( + {users.map((user, index) => ( - - - - - - + + + + + + + ))}
ID NameEmail TypeView Restricted Last Logged InEditDeleteUpdate
{plant.id} {getNamesInPreference(plant)[0]} {plant.plant_type} {new Date(plant.last_modified).toLocaleString()} EditDelete {user.id} {user.database.user_restricted_access ? "Yes" : "No"} {new Date(user.database.user_last_login).toLocaleString()}
@@ -164,6 +226,9 @@ export default function Admin(){
+
+ +
: diff --git a/website/src/pages/api/auth/random.ts b/website/src/pages/api/auth/random.ts index 7552c2f..ae19c52 100644 --- a/website/src/pages/api/auth/random.ts +++ b/website/src/pages/api/auth/random.ts @@ -32,13 +32,17 @@ export default async function handler( // Check if the user is permitted to access the API const session = await getServerSession(request, response, authOptions) const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:auth:random:access") + const canGetAll = await checkApiPermissions(request, response, session, client, makeQuery, "data:account:viewPrivateDetails") if(!permission) return response.status(401).json({error: "Not Authorized"}) // Try querying the database try { + + const selector = canGetAll ? "*" : "id"; + // Get x random plant ids from the database - const plantIds = await makeQuery(`SELECT id FROM users ORDER BY ${USE_POSTGRES ? "RANDOM" : "RAND"}() LIMIT ${amount}`, client); + const plantIds = await makeQuery(`SELECT ${selector} FROM users ORDER BY ${USE_POSTGRES ? "RANDOM" : "RAND"}() LIMIT ${amount}`, client); // If there are no plants, return an error if (!plantIds) { diff --git a/website/src/pages/api/user/update.ts b/website/src/pages/api/user/update.ts index a77b3bd..34511a6 100644 --- a/website/src/pages/api/user/update.ts +++ b/website/src/pages/api/user/update.ts @@ -20,16 +20,23 @@ export default async function handler( const tables = getTables(); // Get the variables - const { id, name, email, image } = request.query; + const { id, + name, + email, + image, + adminData + } = request.query; // If there is a missing variable then return an error - if(!id && !name && !email) { - return response.status(400).json({ error: 'Missing variables, must have id, name and email', id, name, email }); - } + if(!adminData) + if(!id || !name || !email) { + return response.status(400).json({ error: 'Missing variables, must have id, name and email', id, name, email }); + } // Check if the user is permitted to access the API const session = await getServerSession(request, response, authOptions) const permission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:update:access") + const adminPermission = await checkApiPermissions(request, response, session, client, makeQuery, "api:user:update:admin") if(!permission) return response.status(401).json({error: "Not Authorized"}) try { @@ -41,6 +48,19 @@ export default async function handler( let query = `UPDATE users SET ${tables.user_name} = '${name}', ${tables.user_email} = '${email}' ${imageQuery} WHERE id = ${id}`; + + // If it is an admin update then add the admin data + if(adminData) { + + // If not an admin then return an error + if(!adminPermission) { + return response.status(401).json({error: "Not Authorized"}) + } + + let data = JSON.parse(adminData as any); + query = `UPDATE users SET ${tables.user_name} = '${data.name}', ${tables.user_email} = '${data.email}', ${tables.user_type} = '${data.user_type}' WHERE id = ${data.id}`; + } + console.log("===================================="); console.log(query); console.log("===================================="); diff --git a/website/src/styles/pages/admin.module.css b/website/src/styles/pages/admin.module.css index 141ce93..4a795ee 100644 --- a/website/src/styles/pages/admin.module.css +++ b/website/src/styles/pages/admin.module.css @@ -114,4 +114,14 @@ .plantTable button:hover{ background-color: var(--darker-secondary-gray); color: var(--secondary-gray) +} + +.reloadButton{ + margin-top: 50px; + background-color: var(--main-green); + padding: 8px; + border-radius: 8px; + width: 100%; + color: var(--text-light); + cursor: pointer; } \ No newline at end of file