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
}
})