mirror of https://github.com/MaxLeiter/Drift
Add deleting users to admin, refactor auth
parent
65cf59e96b
commit
447974a74a
@ -1,5 +1,10 @@
|
||||
import config from "@lib/config"
|
||||
import Auth from "../components"
|
||||
|
||||
export function isGithubEnabled() {
|
||||
return config.github_client_id.length && config.github_client_secret.length ? true : false
|
||||
}
|
||||
|
||||
export default function SignInPage() {
|
||||
return <Auth page="signin" />
|
||||
return <Auth page="signin" isGithubEnabled={isGithubEnabled()} />
|
||||
}
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
.table {
|
||||
width: 100%;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.table .id {
|
||||
width: 10ch;
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
.table {
|
||||
width: 100%;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table thead th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.id {
|
||||
width: 130px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
"use client"
|
||||
|
||||
import Button from "@components/button"
|
||||
import ButtonDropdown from "@components/button-dropdown"
|
||||
import { Spinner } from "@components/spinner"
|
||||
import { useToasts } from "@components/toasts"
|
||||
import { Post, User } from "@lib/server/prisma"
|
||||
import { useState } from "react"
|
||||
import styles from "./table.module.css"
|
||||
|
||||
export function UserTable({
|
||||
users: initialUsers
|
||||
}: {
|
||||
users?: {
|
||||
createdAt: string
|
||||
posts?: Post[]
|
||||
id: string
|
||||
email: string | null
|
||||
role: string | null
|
||||
displayName: string | null
|
||||
|
||||
}[]
|
||||
}) {
|
||||
const { setToast } = useToasts()
|
||||
const [users, setUsers] = useState<typeof initialUsers>(initialUsers)
|
||||
|
||||
const deleteUser = async (id: string) => {
|
||||
try {
|
||||
const res = await fetch("/api/admin?action=delete-user", {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
userId: id
|
||||
})
|
||||
})
|
||||
|
||||
if (res.status === 200) {
|
||||
setToast({
|
||||
message: "User deleted",
|
||||
type: "success"
|
||||
})
|
||||
setUsers(users?.filter((user) => user.id !== id))
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
setToast({
|
||||
message: "Error deleting user",
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>User ID</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!users ? (
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<Spinner />
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
{users?.map((user) => (
|
||||
<tr key={user.id}>
|
||||
<td>{user.displayName ? user.displayName : "no name"}</td>
|
||||
<td>{user.email}</td>
|
||||
<td>{user.role}</td>
|
||||
<td className={styles.id} title={user.id}>
|
||||
{user.id}
|
||||
</td>
|
||||
<td>
|
||||
<Button onClick={() => deleteUser(user.id)}>Delete</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
export function PostTable({
|
||||
posts
|
||||
}: {
|
||||
posts?: {
|
||||
createdAt: string
|
||||
id: string
|
||||
author?: User | null
|
||||
title: string
|
||||
visibility: string
|
||||
}[]
|
||||
}) {
|
||||
return (
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Author</th>
|
||||
<th>Created</th>
|
||||
<th>Visibility</th>
|
||||
<th className={styles.id}>Post ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{!posts ? (
|
||||
<tr>
|
||||
<td colSpan={5}>
|
||||
<Spinner />
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
{posts?.map((post) => (
|
||||
<tr key={post.id}>
|
||||
<td>
|
||||
<a href={`/post/${post.id}`} target="_blank" rel="noreferrer">
|
||||
{post.title}
|
||||
</a>
|
||||
</td>
|
||||
<td>{"author" in post ? post.author?.name : "no author"}</td>
|
||||
<td>{new Date(post.createdAt).toLocaleDateString()}</td>
|
||||
<td>{post.visibility}</td>
|
||||
<td>{post.id}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
@ -1,100 +1,55 @@
|
||||
import { getAllPosts, getAllUsers } from "@lib/server/prisma"
|
||||
import { Spinner } from "@components/spinner"
|
||||
import styles from "./admin.module.css"
|
||||
|
||||
export function UserTable({
|
||||
users
|
||||
}: {
|
||||
users?: Awaited<ReturnType<typeof getAllUsers>>
|
||||
}) {
|
||||
return (
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>User ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users?.map((user) => (
|
||||
<tr key={user.id}>
|
||||
<td>{user.displayName ? user.displayName : "no name"}</td>
|
||||
<td>{user.email}</td>
|
||||
<td>{user.role}</td>
|
||||
<td className={styles.id}>{user.id}</td>
|
||||
</tr>
|
||||
))}
|
||||
{!users && (
|
||||
<tr>
|
||||
<td colSpan={4}>
|
||||
<Spinner />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
export function PostTable({
|
||||
posts
|
||||
}: {
|
||||
posts?: Awaited<ReturnType<typeof getAllPosts>>
|
||||
}) {
|
||||
return (
|
||||
<table className={styles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Title</th>
|
||||
<th>Author</th>
|
||||
<th>Created</th>
|
||||
<th>Visibility</th>
|
||||
<th className={styles.id}>Post ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{posts?.map((post) => (
|
||||
<tr key={post.id}>
|
||||
<td>
|
||||
<a href={`/post/${post.id}`} target="_blank" rel="noreferrer">
|
||||
{post.title}
|
||||
</a>
|
||||
</td>
|
||||
<td>{"author" in post ? post.author?.name : "no author"}</td>
|
||||
<td>{post.createdAt.toLocaleDateString()}</td>
|
||||
<td>{post.visibility}</td>
|
||||
<td>{post.id}</td>
|
||||
</tr>
|
||||
))}
|
||||
{!posts && (
|
||||
<tr>
|
||||
<td colSpan={5}>
|
||||
<Spinner />
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
import { getAllPosts, getAllUsers } from "@lib/server/prisma"
|
||||
import { PostTable, UserTable } from "./components/tables"
|
||||
|
||||
export default async function AdminPage() {
|
||||
const usersPromise = getAllUsers()
|
||||
const usersPromise = getAllUsers({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
createdAt: true
|
||||
}
|
||||
})
|
||||
const postsPromise = getAllPosts({
|
||||
withAuthor: true
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
author: {
|
||||
select: {
|
||||
name: true
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const [users, posts] = await Promise.all([usersPromise, postsPromise])
|
||||
|
||||
const serializedPosts = posts.map((post) => {
|
||||
return {
|
||||
...post,
|
||||
createdAt: post.createdAt.toISOString(),
|
||||
updatedAt: post.updatedAt.toISOString(),
|
||||
expiresAt: post.expiresAt?.toISOString(),
|
||||
deletedAt: post.deletedAt?.toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
const serializedUsers = users.map((user) => {
|
||||
return {
|
||||
...user,
|
||||
createdAt: user.createdAt.toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Admin</h1>
|
||||
<h2>Users</h2>
|
||||
<UserTable users={users} />
|
||||
<UserTable users={serializedUsers} />
|
||||
<h2>Posts</h2>
|
||||
<PostTable posts={posts} />
|
||||
<PostTable posts={serializedPosts} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue