1
0
Fork 0

feat: show active sessions on account page

fix-1
Paul Makles 2023-07-28 12:32:06 +01:00
parent 251cb9b7f6
commit 9f5de75d26
No known key found for this signature in database
GPG Key ID: 5059F398521BB0F6
3 changed files with 186 additions and 1 deletions

View File

@ -1,10 +1,30 @@
import { JsonCard } from "@/components/cards/JsonCard";
import { UserCard } from "@/components/cards/UserCard";
import { EmailClassificationCard } from "@/components/cards/authifier/EmailClassificationCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
import { AccountActions } from "@/components/inspector/AccountActions";
import { fetchAccountById, fetchUserById } from "@/lib/db";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import {
fetchAccountById,
fetchSessionsByAccount,
fetchUserById,
} from "@/lib/db";
import dayjs from "dayjs";
import { notFound } from "next/navigation";
import { User } from "revolt-api";
import { decodeTime } from "ulid";
import relativeTime from "dayjs/plugin/relativeTime";
dayjs.extend(relativeTime);
export default async function User({
params,
@ -15,6 +35,7 @@ export default async function User({
if (!account) return notFound();
const user = await fetchUserById(params.id);
const sessions = await fetchSessionsByAccount(params.id);
return (
<div className="flex flex-col gap-2">
@ -22,6 +43,37 @@ export default async function User({
{user && <UserCard user={user} subtitle={account.email} />}
<AccountActions account={account} user={user as User} />
<EmailClassificationCard email={account.email} />
<Separator />
<Card>
<CardHeader>
<CardTitle>Active Sessions</CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Created</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{sessions.map((session) => (
<TableRow key={session._id}>
<TableCell>{session.name}</TableCell>
<TableCell>
{dayjs(decodeTime(session._id)).fromNow()}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
<Separator />
<JsonCard obj={account} />
{/*TODO? update password, reset 2FA, disable / undisable (disabled if pending
delete), delete / cancel delete
<br />

114
components/ui/table.tsx Normal file
View File

@ -0,0 +1,114 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn("bg-primary font-medium text-primary-foreground", className)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

View File

@ -8,6 +8,7 @@ import type {
Message,
Report,
Server,
SessionInfo,
SnapshotContent,
User,
} from "revolt-api";
@ -89,6 +90,24 @@ export async function fetchAccountById(id: string) {
);
}
export async function fetchSessionsByAccount(accountId: string) {
return await mongo()
.db("revolt")
.collection<SessionInfo>("sessions")
.find(
{ user_id: accountId },
{
projection: {
token: 0,
},
sort: {
_id: -1,
},
}
)
.toArray();
}
export async function fetchUserById(id: string) {
return await mongo()
.db("revolt")