From 0627ab7396633a8599bf1bd5824d966643c87dfb Mon Sep 17 00:00:00 2001 From: Max Leiter Date: Sun, 13 Nov 2022 23:28:51 -0800 Subject: [PATCH] fix raw file viewing, rm password from settings, add admin api --- .../components/post-page/post-page.module.css | 4 - client/app/(posts)/post/[id]/page.tsx | 22 ++-- .../components/post-list/list-item.module.css | 5 +- client/app/settings/page.tsx | 4 +- client/next.config.mjs | 2 +- client/pages/api/admin/index.ts | 121 ++++++++++++++++++ client/pages/api/post/[id].ts | 3 + 7 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 client/pages/api/admin/index.ts diff --git a/client/app/(posts)/post/[id]/components/post-page/post-page.module.css b/client/app/(posts)/post/[id]/components/post-page/post-page.module.css index d92a920a..97d8c94f 100644 --- a/client/app/(posts)/post/[id]/components/post-page/post-page.module.css +++ b/client/app/(posts)/post/[id]/components/post-page/post-page.module.css @@ -45,10 +45,6 @@ align-items: center; } - .header .title .badges > * { - width: min-content; - } - .header .buttons { display: flex; justify-content: center; diff --git a/client/app/(posts)/post/[id]/page.tsx b/client/app/(posts)/post/[id]/page.tsx index a80a8a3b..69b05323 100644 --- a/client/app/(posts)/post/[id]/page.tsx +++ b/client/app/(posts)/post/[id]/page.tsx @@ -29,10 +29,10 @@ const getPost = async (id: string) => { return notFound() } - const isAuthor = user?.id === post?.authorId + const isAuthorOrAdmin = user?.id === post?.authorId || user?.role === "admin" if (post.visibility === "public") { - return { post, isAuthor } + return { post, isAuthor: isAuthorOrAdmin } } // must be authed to see unlisted/private @@ -43,19 +43,19 @@ const getPost = async (id: string) => { return notFound() } - if (post.visibility === "private" && !isAuthor) { + if (post.visibility === "private" && !isAuthorOrAdmin) { return notFound() } - if (post.visibility === "protected" && !isAuthor) { + if (post.visibility === "protected" && !isAuthorOrAdmin) { return { post, isProtected: true, - isAuthor + isAuthor: isAuthorOrAdmin } } - return { post, isAuthor } + return { post, isAuthor: isAuthorOrAdmin } } const PostView = async ({ @@ -67,8 +67,14 @@ const PostView = async ({ }) => { const { post, isProtected, isAuthor } = await getPost(params.id) // TODO: serialize dates in prisma middleware instead of passing as JSON - const stringifiedPost = JSON.stringify(post); - return + const stringifiedPost = JSON.stringify(post) + return ( + + ) } // export const getServerSideProps: GetServerSideProps = async ({ diff --git a/client/app/components/post-list/list-item.module.css b/client/app/components/post-list/list-item.module.css index 5a934da0..51cc7094 100644 --- a/client/app/components/post-list/list-item.module.css +++ b/client/app/components/post-list/list-item.module.css @@ -24,10 +24,7 @@ .badges { flex-direction: column; align-items: flex-start; - } - - .badges > * { - width: min-content; + margin-top: var(--gap-half); } .title { diff --git a/client/app/settings/page.tsx b/client/app/settings/page.tsx index 07f9e851..eb48549c 100644 --- a/client/app/settings/page.tsx +++ b/client/app/settings/page.tsx @@ -26,9 +26,9 @@ export default async function SettingsPage() { - + {/* - + */} ) } diff --git a/client/next.config.mjs b/client/next.config.mjs index f18f93b8..77f6ab66 100644 --- a/client/next.config.mjs +++ b/client/next.config.mjs @@ -23,7 +23,7 @@ const nextConfig = { return [ { source: "/file/raw/:id", - destination: `/api/raw/:id` + destination: `/api/file/raw/:id` }, { source: "/home", diff --git a/client/pages/api/admin/index.ts b/client/pages/api/admin/index.ts new file mode 100644 index 00000000..c98a234d --- /dev/null +++ b/client/pages/api/admin/index.ts @@ -0,0 +1,121 @@ +// ts nextjs api handler + +import { withMethods } from "@lib/api-middleware/with-methods" +import { parseQueryParam } from "@lib/server/parse-query-param" +import { NextApiRequest, NextApiResponse } from "next" +import { prisma } from "lib/server/prisma" +import { getSession } from "next-auth/react" + +const actions = [ + "user", + "post", + "users", + "posts", + "set-role", + "delete-user", + "delete-post" +] as const + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + const { action: requestedAction } = req.query + const action = parseQueryParam(requestedAction) as typeof actions[number] + + if (!action || !actions.includes(action)) { + res.status(400).json({ error: "Invalid action" }) + return + } + + const session = await getSession({ req }) + const id = session?.user?.id + // get admin from db + + const isAdmin = await prisma.user + .findUnique({ + where: { + id + }, + select: { + role: true + } + }) + .then((user) => user?.role === "admin") + + if (!isAdmin) { + return res.status(403).json({ error: "Not authorized" }) + } + + switch (req.method) { + case "GET": + switch (action) { + case "users": + const users = await prisma.user.findMany() + return res.status(200).json(users) + case "posts": + const posts = await prisma.post.findMany() + return res.status(200).json(posts) + case "user": + const { id: userId } = req.query + const user = await prisma.user.findUnique({ + where: { + id: parseQueryParam(userId) + } + }) + return res.status(200).json(user) + case "post": + const { id: postId } = req.query + const post = await prisma.post.findUnique({ + where: { + id: parseQueryParam(postId) + } + }) + return res.status(200).json(post) + } + break + case "PATCH": + switch (action) { + case "set-role": + const { userId, role } = req.body + if (!userId || !role || role !== "admin" || role !== "user") { + return res.status(400).json({ error: "Invalid request" }) + } + + const user = await prisma.user.update({ + where: { id: userId }, + data: { + role: role + } + }) + + return res.status(200).json(user) + } + break + case "DELETE": + switch (action) { + case "delete-user": + const { userId } = req.body + if (!userId) { + return res.status(400).json({ error: "Invalid request" }) + } + + const user = await prisma.user.delete({ + where: { id: userId } + }) + + return res.status(200).json(user) + case "delete-post": + const { postId } = req.body + if (!postId) { + return res.status(400).json({ error: "Invalid request" }) + } + + const post = await prisma.post.delete({ + where: { id: postId } + }) + + return res.status(200).json(post) + } + break + } +} + +export default withMethods(["GET", "PATCH", "DELETE"], handler) diff --git a/client/pages/api/post/[id].ts b/client/pages/api/post/[id].ts index 9126d7a8..0ee77ba8 100644 --- a/client/pages/api/post/[id].ts +++ b/client/pages/api/post/[id].ts @@ -141,6 +141,9 @@ async function handleDelete(req: NextApiRequest, res: NextApiResponse) { await prisma.post.delete({ where: { id + }, + include: { + files: true } })