1
0
Fork 0

feat: flesh out inspector

fix-1
Paul Makles 2023-07-27 12:27:43 +01:00
parent 2b296330c9
commit b7a3a61717
No known key found for this signature in database
GPG Key ID: 5059F398521BB0F6
6 changed files with 297 additions and 0 deletions

View File

@ -0,0 +1,26 @@
import { UserCard } from "@/components/cards/UserCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
import { fetchUserById } from "@/lib/db";
import { notFound } from "next/navigation";
export default async function User({
params,
}: {
params: { id: string; type: string };
}) {
const user = await fetchUserById(params.id);
// if (!user) return notFound();
return (
<div className="flex flex-col gap-2">
<NavigationToolbar>Inspecting Account</NavigationToolbar>
{user && <UserCard user={user} subtitle="Associated User" />}
update password, reset 2FA, disable / undisable (disabled if pending
delete), delete / cancel delete
<br />
list active 2FA methods without keys
<br />
email account
</div>
);
}

View File

@ -0,0 +1,25 @@
import { ChannelCard } from "@/components/cards/ChannelCard";
import { JsonCard } from "@/components/cards/JsonCard";
import { ServerCard } from "@/components/cards/ServerCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
import { fetchChannelById, fetchServerById } from "@/lib/db";
import { notFound } from "next/navigation";
export default async function Message({ params }: { params: { id: string } }) {
const channel = await fetchChannelById(params.id);
if (!channel) return notFound();
const server =
channel.channel_type === "TextChannel"
? await fetchServerById(channel.server)
: undefined;
return (
<div className="flex flex-col gap-2">
<NavigationToolbar>Inspecting Channel</NavigationToolbar>
<ChannelCard channel={channel!} subtitle="Channel" />
{server && <ServerCard server={server} subtitle="Server" />}
<JsonCard obj={channel} />
</div>
);
}

View File

@ -0,0 +1,40 @@
import { ChannelCard } from "@/components/cards/ChannelCard";
import { JsonCard } from "@/components/cards/JsonCard";
import { UserCard } from "@/components/cards/UserCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
import { Card, CardHeader } from "@/components/ui/card";
import { fetchChannelById, fetchMessageById, fetchUserById } from "@/lib/db";
import Link from "next/link";
import { notFound } from "next/navigation";
export default async function Message({
params,
}: {
params: { id: string; type: string };
}) {
const message = await fetchMessageById(params.id);
if (!message) return notFound();
const author = await fetchUserById(message.author);
const channel = await fetchChannelById(message.channel);
return (
<div className="flex flex-col gap-2">
<NavigationToolbar>Inspecting Message</NavigationToolbar>
<Card className="shadow-none">
<CardHeader>
<p>{message.content}</p>
</CardHeader>
</Card>
<Link href={`/panel/inspect/user/${author!._id}`}>
<UserCard user={author!} subtitle="Message Author" />
</Link>
<Link href={`/panel/inspect/channel/${channel!._id}`}>
<ChannelCard channel={channel!} subtitle="Channel" />
</Link>
<JsonCard obj={message} />
</div>
);
}

View File

@ -0,0 +1,67 @@
"use client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useRouter } from "next/navigation";
import { useState } from "react";
export default function Inspect() {
const [id, setId] = useState("");
const router = useRouter();
const createHandler = (type: string) => () =>
router.push(`/panel/inspect/${type}/${id}`);
return (
<div className="flex flex-col gap-2">
<Input
placeholder="Enter an ID..."
value={id}
onChange={(e) => setId(e.currentTarget.value)}
/>
<div className="flex gap-2">
<Button
className="flex-1"
variant="outline"
onClick={createHandler("user")}
>
User
</Button>
<Button
className="flex-1"
variant="outline"
onClick={createHandler("server")}
>
Server
</Button>
<Button
className="flex-1"
variant="outline"
onClick={createHandler("channel")}
>
Channel
</Button>
<Button
className="flex-1"
variant="outline"
onClick={() =>
fetch("https://api.revolt.chat/invites/" + id)
.then((res) => res.json())
.then((invite) =>
router.push(`/panel/inspect/server/${invite.server_id}`)
)
}
>
Invite
</Button>
<Button
className="flex-1"
variant="outline"
onClick={createHandler("message")}
>
Message
</Button>
</div>
</div>
);
}

View File

@ -0,0 +1,18 @@
import { JsonCard } from "@/components/cards/JsonCard";
import { ServerCard } from "@/components/cards/ServerCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
import { fetchServerById } from "@/lib/db";
import { notFound } from "next/navigation";
export default async function Server({ params }: { params: { id: string } }) {
const server = await fetchServerById(params.id);
if (!server) return notFound();
return (
<div className="flex flex-col gap-2">
<NavigationToolbar>Inspecting Server</NavigationToolbar>
<ServerCard server={server} subtitle="Server" />
<JsonCard obj={server} />
</div>
);
}

View File

@ -0,0 +1,121 @@
import { JsonCard } from "@/components/cards/JsonCard";
import { UserCard } from "@/components/cards/UserCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
import { RelevantModerationNotices } from "@/components/inspector/RelevantModerationNotices";
import { RelevantObjects } from "@/components/inspector/RelevantObjects";
import { RelevantReports } from "@/components/inspector/RelevantReports";
import { UserActions } from "@/components/inspector/UserActions";
import { Card, CardHeader } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
import { PLATFORM_MOD_ID } from "@/lib/constants";
import {
fetchBotsByUser,
fetchMembershipsByUser,
fetchMessages,
fetchReports,
fetchServers,
fetchSnapshots,
fetchStrikesByUser,
fetchUserById,
fetchUsersById,
findDM,
} from "@/lib/db";
import Link from "next/link";
import { notFound } from "next/navigation";
export default async function User({
params,
}: {
params: { id: string; type: string };
}) {
const user = await fetchUserById(params.id);
if (!user) return notFound();
const botOwner = user.bot ? await fetchUserById(user.bot.owner) : undefined;
// Fetch strikes
const strikes = await fetchStrikesByUser(user._id);
// Fetch moderation alerts
const dm = await findDM(PLATFORM_MOD_ID, user._id);
const notices = dm ? await fetchMessages({ channel: dm._id }) : [];
// Fetch friends and bots
const botIds = await fetchBotsByUser(user._id).then((bots) =>
bots.map((bot) => bot._id)
);
const relevantUsers = await fetchUsersById([
...botIds,
...(
user.relations?.filter((relation) => relation.status === "Friend") ?? []
).map((relation) => relation._id),
]);
relevantUsers.sort((a) => (a.bot ? -1 : 0));
// Fetch server memberships
const serverMemberships = await fetchMembershipsByUser(user._id);
const servers = await fetchServers({
_id: {
$in: serverMemberships.map((member) => member._id.server),
},
});
// Fetch reports
const reportsByUser = await fetchReports({
author_id: user._id,
});
const reportIdsInvolvingUser = await fetchSnapshots({
// TODO: slow query
$or: [{ "content._id": user._id }, { "content.author": user._id }],
}).then((snapshots) => [
...new Set(snapshots.map((snapshot) => snapshot.report_id)),
]);
const reportsAgainstUser = await fetchReports({
_id: {
$in: reportIdsInvolvingUser,
},
});
return (
<div className="flex flex-col gap-2">
<NavigationToolbar>Inspecting User</NavigationToolbar>
<UserCard user={user} subtitle={user.status?.text ?? "No status set"} />
<UserActions id={user._id} />
{user.profile?.content && (
<Card>
<CardHeader>
<p>{user.profile?.content}</p>
</CardHeader>
</Card>
)}
{botOwner && (
<Link href={`/panel/inspect/user/${botOwner!._id}`}>
<UserCard user={botOwner} subtitle="Bot Owner" />
</Link>
)}
<Separator />
<RelevantModerationNotices strikes={strikes} notices={notices} />
<Separator />
<RelevantObjects
users={relevantUsers}
servers={servers}
userId={user._id}
/>
<Separator />
<RelevantReports byUser={reportsByUser} forUser={reportsAgainstUser} />
<Separator />
<JsonCard obj={user} />
</div>
);
}