forked from administration/panel
feat: wipe user messages
parent
eb0bd7a7c9
commit
8113a86db9
|
@ -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'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's authentication token. This will not disconnect active connections.
|
Re-roll this bot'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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue