diff --git a/app/panel/inspect/account/[id]/page.tsx b/app/panel/inspect/account/[id]/page.tsx new file mode 100644 index 0000000..29697a6 --- /dev/null +++ b/app/panel/inspect/account/[id]/page.tsx @@ -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 ( +
+ Inspecting Account + {user && } + update password, reset 2FA, disable / undisable (disabled if pending + delete), delete / cancel delete +
+ list active 2FA methods without keys +
+ email account +
+ ); +} diff --git a/app/panel/inspect/channel/[id]/page.tsx b/app/panel/inspect/channel/[id]/page.tsx new file mode 100644 index 0000000..6e4bdd8 --- /dev/null +++ b/app/panel/inspect/channel/[id]/page.tsx @@ -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 ( +
+ Inspecting Channel + + {server && } + +
+ ); +} diff --git a/app/panel/inspect/message/[id]/page.tsx b/app/panel/inspect/message/[id]/page.tsx new file mode 100644 index 0000000..450fa7a --- /dev/null +++ b/app/panel/inspect/message/[id]/page.tsx @@ -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 ( +
+ Inspecting Message + + + +

{message.content}

+
+
+ + + + + + + + +
+ ); +} diff --git a/app/panel/inspect/page.tsx b/app/panel/inspect/page.tsx new file mode 100644 index 0000000..3163663 --- /dev/null +++ b/app/panel/inspect/page.tsx @@ -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 ( +
+ setId(e.currentTarget.value)} + /> +
+ + + + + +
+
+ ); +} diff --git a/app/panel/inspect/server/[id]/page.tsx b/app/panel/inspect/server/[id]/page.tsx new file mode 100644 index 0000000..cda7726 --- /dev/null +++ b/app/panel/inspect/server/[id]/page.tsx @@ -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 ( +
+ Inspecting Server + + +
+ ); +} diff --git a/app/panel/inspect/user/[id]/page.tsx b/app/panel/inspect/user/[id]/page.tsx new file mode 100644 index 0000000..40e4b0c --- /dev/null +++ b/app/panel/inspect/user/[id]/page.tsx @@ -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 ( +
+ Inspecting User + + + + + {user.profile?.content && ( + + +

{user.profile?.content}

+
+
+ )} + + {botOwner && ( + + + + )} + + + + + + + + + + + + +
+ ); +}