1
0
Fork 0

feat: wipe user messages

main
Paul Makles 2023-11-26 13:14:44 +00:00
parent eb0bd7a7c9
commit 8113a86db9
No known key found for this signature in database
GPG Key ID: 5059F398521BB0F6
2 changed files with 167 additions and 92 deletions

View File

@ -30,6 +30,7 @@ import {
unsuspendUser, unsuspendUser,
updateBotDiscoverability, updateBotDiscoverability,
updateUserBadges, updateUserBadges,
wipeUser,
wipeUserProfile, wipeUserProfile,
} from "@/lib/actions"; } from "@/lib/actions";
import { useRef, useState } from "react"; import { useRef, useState } from "react";
@ -264,6 +265,51 @@ export function UserActions({ user, bot }: { user: User; bot?: Bot }) {
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button className="flex-1 bg-pink-600" disabled={userInaccessible}>
Wipe Messages
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Are you sure you want to wipe this user&apos;s messages?
</AlertDialogTitle>
<AlertDialogDescription>
All messages sent by this user will be deleted immediately.
<br className="text-base/8" />
<span className="text-red-700">
This action is irreversible and{" "}
<b className="font-bold">will not publish any events</b>!
</span>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
className="hover:bg-red-700 transition-all"
onClick={() =>
wipeUser(user._id, 0, true)
.then(() => {
setUserDraft((user) => ({ ...user, flags: 4 }));
toast({ title: "Wiped user's messages" });
})
.catch((err) =>
toast({
title: "Failed to wipe user's messages!",
description: String(err),
variant: "destructive",
})
)
}
>
Ban
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button className="flex-1 bg-yellow-600">Bees</Button> <Button className="flex-1 bg-yellow-600">Bees</Button>
@ -424,29 +470,37 @@ export function UserActions({ user, bot }: { user: User; bot?: Bot }) {
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="ghost" disabled={!user.bot?.owner}>Reset bot token</Button> <Button variant="ghost" disabled={!user.bot?.owner}>
Reset bot token
</Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>Reset token</AlertDialogTitle> <AlertDialogTitle>Reset token</AlertDialogTitle>
<AlertDialogDescription className="flex flex-col gap-2"> <AlertDialogDescription className="flex flex-col gap-2">
<span> <span>
Re-roll this bot&apos;s authentication token. This will not disconnect active connections. Re-roll this bot&apos;s authentication token. This will
not disconnect active connections.
</span> </span>
</AlertDialogDescription> </AlertDialogDescription>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter> <AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
onClick={() => resetBotToken(user._id) onClick={() =>
.then(() => toast({ resetBotToken(user._id)
.then(() =>
toast({
title: "Reset bot token", title: "Reset bot token",
})) })
.catch((e) => toast({ )
.catch((e) =>
toast({
title: "Failed to reset token", title: "Failed to reset token",
description: String(e), description: String(e),
variant: "destructive", variant: "destructive",
})) })
)
} }
> >
Reset Reset
@ -457,18 +511,16 @@ export function UserActions({ user, bot }: { user: User; bot?: Bot }) {
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
<Button variant="ghost" disabled={!user.bot?.owner}>Transfer bot</Button> <Button variant="ghost" disabled={!user.bot?.owner}>
Transfer bot
</Button>
</AlertDialogTrigger> </AlertDialogTrigger>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle>Transfer bot</AlertDialogTitle> <AlertDialogTitle>Transfer bot</AlertDialogTitle>
<AlertDialogDescription className="flex flex-col gap-2"> <AlertDialogDescription className="flex flex-col gap-2">
<span> <span>Transfer this bot to a new owner.</span>
Transfer this bot to a new owner. <UserSelector onChange={setTransferTarget} />
</span>
<UserSelector
onChange={setTransferTarget}
/>
<Checkbox <Checkbox
checked={transferResetToken} checked={transferResetToken}
onChange={(e) => setTransferResetToken(!!e)} onChange={(e) => setTransferResetToken(!!e)}
@ -481,15 +533,24 @@ export function UserActions({ user, bot }: { user: User; bot?: Bot }) {
<AlertDialogCancel>Cancel</AlertDialogCancel> <AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction <AlertDialogAction
disabled={!transferTarget} disabled={!transferTarget}
onClick={() => transferBot(user._id, transferTarget!._id, transferResetToken) onClick={() =>
.then(() => toast({ transferBot(
user._id,
transferTarget!._id,
transferResetToken
)
.then(() =>
toast({
title: "Reset bot token", title: "Reset bot token",
})) })
.catch((e) => toast({ )
.catch((e) =>
toast({
title: "Failed to reset token", title: "Failed to reset token",
description: String(e), description: String(e),
variant: "destructive", variant: "destructive",
})) })
)
.finally(() => { .finally(() => {
setTransferResetToken(true); setTransferResetToken(true);
setTransferTarget(null); setTransferTarget(null);

View File

@ -446,12 +446,18 @@ export async function updateUserBadges(userId: string, badges: number) {
} }
} }
export async function wipeUser(userId: string, flags = 4) { export async function wipeUser(
userId: string,
flags = 4,
onlyMessages = false
) {
if (RESTRICT_ACCESS_LIST.includes(userId)) throw "restricted access"; if (RESTRICT_ACCESS_LIST.includes(userId)) throw "restricted access";
await checkPermission("users/action/wipe", userId, { flags }); await checkPermission("users/action/wipe", userId, { flags });
const user = await mongo() const user = onlyMessages
? null
: await mongo()
.db("revolt") .db("revolt")
.collection<User>("users") .collection<User>("users")
.findOne({ _id: userId }); .findOne({ _id: userId });
@ -462,7 +468,9 @@ export async function wipeUser(userId: string, flags = 4) {
.find({ author: userId }, { sort: { _id: -1 } }) .find({ author: userId }, { sort: { _id: -1 } })
.toArray(); .toArray();
const dms = await mongo() const dms = onlyMessages
? null
: await mongo()
.db("revolt") .db("revolt")
.collection<Channel>("channels") .collection<Channel>("channels")
.find({ .find({
@ -471,7 +479,9 @@ export async function wipeUser(userId: string, flags = 4) {
}) })
.toArray(); .toArray();
const memberships = await mongo() const memberships = onlyMessages
? null
: await mongo()
.db("revolt") .db("revolt")
.collection<{ _id: { user: string; server: string } }>("server_members") .collection<{ _id: { user: string; server: string } }>("server_members")
.find({ "_id.user": userId }) .find({ "_id.user": userId })
@ -479,7 +489,7 @@ export async function wipeUser(userId: string, flags = 4) {
// retrieve messages, dm channels, relationships, server memberships // retrieve messages, dm channels, relationships, server memberships
const backup = { const backup = {
_event: "wipe", _event: onlyMessages ? "messages" : "wipe",
user, user,
messages, messages,
dms, dms,
@ -499,6 +509,7 @@ export async function wipeUser(userId: string, flags = 4) {
.filter((attachment) => attachment) .filter((attachment) => attachment)
.map((attachment) => attachment!._id); .map((attachment) => attachment!._id);
if (!onlyMessages) {
if (backup.user?.avatar) { if (backup.user?.avatar) {
attachmentIds.push(backup.user.avatar._id); attachmentIds.push(backup.user.avatar._id);
} }
@ -506,6 +517,7 @@ export async function wipeUser(userId: string, flags = 4) {
if (backup.user?.profile?.background) { if (backup.user?.profile?.background) {
attachmentIds.push(backup.user.profile.background._id); attachmentIds.push(backup.user.profile.background._id);
} }
}
if (attachmentIds.length) { if (attachmentIds.length) {
await mongo() await mongo()
@ -527,6 +539,7 @@ export async function wipeUser(userId: string, flags = 4) {
author: userId, author: userId,
}); });
if (!onlyMessages) {
// delete server memberships // delete server memberships
await mongo().db("revolt").collection<Member>("server_members").deleteMany({ await mongo().db("revolt").collection<Member>("server_members").deleteMany({
"_id.user": userId, "_id.user": userId,
@ -557,8 +570,8 @@ export async function wipeUser(userId: string, flags = 4) {
// broadcast wipe event // broadcast wipe event
for (const topic of [ for (const topic of [
...backup.dms.map((x) => x._id), ...backup.dms!.map((x) => x._id),
...backup.memberships.map((x) => x._id.server), ...backup.memberships!.map((x) => x._id.server),
]) { ]) {
await publishMessage(topic, { await publishMessage(topic, {
type: "UserPlatformWipe", type: "UserPlatformWipe",
@ -566,6 +579,7 @@ export async function wipeUser(userId: string, flags = 4) {
flags, flags,
}); });
} }
}
} }
export async function banUser(userId: string) { export async function banUser(userId: string) {