forked from administration/panel
parent
77075dad23
commit
bc48204410
|
@ -23,8 +23,10 @@ import { Input } from "../../ui/input";
|
|||
import {
|
||||
banUser,
|
||||
closeReportsByUser,
|
||||
resetBotToken,
|
||||
sendAlert,
|
||||
suspendUser,
|
||||
transferBot,
|
||||
unsuspendUser,
|
||||
updateBotDiscoverability,
|
||||
updateUserBadges,
|
||||
|
@ -37,6 +39,7 @@ import { Card, CardHeader } from "../../ui/card";
|
|||
import { cn } from "@/lib/utils";
|
||||
import { decodeTime } from "ulid";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import UserSelector from "@/components/ui/user-selector";
|
||||
|
||||
const badges = [1, 2, 4, 8, 16, 32, 128, 0, 256, 512, 1024];
|
||||
|
||||
|
@ -53,6 +56,8 @@ export function UserActions({ user, bot }: { user: User; bot?: Bot }) {
|
|||
displayName: false,
|
||||
status: false,
|
||||
});
|
||||
const [transferTarget, setTransferTarget] = useState<User | null>(null);
|
||||
const [transferResetToken, setTransferResetToken] = useState(true);
|
||||
|
||||
const userInaccessible = userDraft.flags === 4 || userDraft.flags === 2;
|
||||
|
||||
|
@ -416,6 +421,86 @@ export function UserActions({ user, bot }: { user: User; bot?: Bot }) {
|
|||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="ghost" disabled={!user.bot?.owner}>Reset bot token</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Reset token</AlertDialogTitle>
|
||||
<AlertDialogDescription className="flex flex-col gap-2">
|
||||
<span>
|
||||
Re-roll this bot's authentication token. This will not disconnect active connections.
|
||||
</span>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => resetBotToken(user._id)
|
||||
.then(() => toast({
|
||||
title: "Reset bot token",
|
||||
}))
|
||||
.catch((e) => toast({
|
||||
title: "Failed to reset token",
|
||||
description: String(e),
|
||||
variant: "destructive",
|
||||
}))
|
||||
}
|
||||
>
|
||||
Reset
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="ghost" disabled={!user.bot?.owner}>Transfer bot</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Transfer bot</AlertDialogTitle>
|
||||
<AlertDialogDescription className="flex flex-col gap-2">
|
||||
<span>
|
||||
Transfer this bot to a new owner.
|
||||
</span>
|
||||
<UserSelector
|
||||
onChange={setTransferTarget}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={transferResetToken}
|
||||
onChange={(e) => setTransferResetToken(!!e)}
|
||||
>
|
||||
Also reset token
|
||||
</Checkbox>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
disabled={!transferTarget}
|
||||
onClick={() => transferBot(user._id, transferTarget!._id, transferResetToken)
|
||||
.then(() => toast({
|
||||
title: "Reset bot token",
|
||||
}))
|
||||
.catch((e) => toast({
|
||||
title: "Failed to reset token",
|
||||
description: String(e),
|
||||
variant: "destructive",
|
||||
}))
|
||||
.finally(() => {
|
||||
setTransferResetToken(true);
|
||||
setTransferTarget(null);
|
||||
})
|
||||
}
|
||||
>
|
||||
Transfer
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="ghost">Close Open Reports</Button>
|
||||
|
|
|
@ -24,7 +24,7 @@ type Permission =
|
|||
| `bots${
|
||||
| ""
|
||||
| `/fetch${"" | "/by-id" | "/by-user"}`
|
||||
| `/update${"" | "/discoverability"}`}`
|
||||
| `/update${"" | "/discoverability" | "/owner" | "/reset-token"}`}`
|
||||
| `channels${
|
||||
| ""
|
||||
| `/fetch${"" | "/by-id" | "/by-server" | "/dm" | "/invites"}`
|
||||
|
@ -139,7 +139,10 @@ const PermissionSets = {
|
|||
"users/update/badges",
|
||||
|
||||
"servers/update/owner",
|
||||
"servers/update/add-member",
|
||||
|
||||
"bots/fetch/by-user",
|
||||
"bots/update/reset-token",
|
||||
"bots/update/owner",
|
||||
|
||||
"accounts/fetch/by-id",
|
||||
"accounts/fetch/by-email",
|
||||
|
@ -164,6 +167,9 @@ const PermissionSets = {
|
|||
"users/fetch/notices",
|
||||
|
||||
"bots/fetch/by-user",
|
||||
"bots/update/reset-token",
|
||||
"bots/update/owner",
|
||||
|
||||
// "messages/fetch/by-user",
|
||||
// "users/fetch/memberships",
|
||||
"servers/fetch",
|
||||
|
@ -175,6 +181,8 @@ const PermissionSets = {
|
|||
"channels/create/dm",
|
||||
|
||||
"servers/update/quarantine",
|
||||
"servers/update/owner",
|
||||
"servers/update/add-member",
|
||||
"backup/fetch",
|
||||
|
||||
"reports/fetch/related/by-user",
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
} from "revolt-api";
|
||||
import { checkPermission } from "./accessPermissions";
|
||||
import { Long } from "mongodb";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export async function sendAlert(userId: string, content: string) {
|
||||
await checkPermission("users/create/alert", userId, { content });
|
||||
|
@ -819,6 +820,84 @@ export async function updateBotDiscoverability(botId: string, state: boolean) {
|
|||
);
|
||||
}
|
||||
|
||||
export async function resetBotToken(botId: string) {
|
||||
await checkPermission("bots/update/reset-token", { botId });
|
||||
|
||||
// Should generate tokens the exact same as the backend generates them:
|
||||
// https://github.com/revoltchat/backend/blob/41f20c2239ed6307ad821b321d13240dc6ff3327/crates/core/database/src/models/bots/model.rs#L106
|
||||
|
||||
await mongo()
|
||||
.db("revolt")
|
||||
.collection<Bot>("bots")
|
||||
.updateOne(
|
||||
{
|
||||
_id: botId,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
token: nanoid(64),
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function transferBot(botId: string, ownerId: string, resetToken: boolean) {
|
||||
await checkPermission("bots/update/owner", { botId, ownerId, resetToken });
|
||||
|
||||
if (resetToken) {
|
||||
await checkPermission("bots/update/reset-token", { botId });
|
||||
}
|
||||
|
||||
await mongo()
|
||||
.db("revolt")
|
||||
.collection<Bot>("bots")
|
||||
.updateOne(
|
||||
{
|
||||
_id: botId,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
owner: ownerId,
|
||||
...(
|
||||
resetToken
|
||||
? {
|
||||
token: nanoid(64),
|
||||
}
|
||||
: {}
|
||||
),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await mongo()
|
||||
.db("revolt")
|
||||
.collection<User>("users")
|
||||
.updateOne(
|
||||
{
|
||||
_id: botId,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"bot.owner": ownerId,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// This doesn't appear to work, maybe Revite can't handle it. I'll leave it in regardless.
|
||||
await publishMessage(
|
||||
botId,
|
||||
{
|
||||
type: "UserUpdate",
|
||||
id: botId,
|
||||
data: {
|
||||
bot: {
|
||||
owner: ownerId,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function restoreAccount(accountId: string) {
|
||||
if (RESTRICT_ACCESS_LIST.includes(accountId)) throw "restricted access";
|
||||
await checkPermission("accounts/restore", accountId);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"lodash.debounce": "^4.0.8",
|
||||
"lucide-react": "^0.263.0",
|
||||
"mongodb": "^5.7.0",
|
||||
"nanoid": "^5.0.1",
|
||||
"next": "13.4.12",
|
||||
"next-auth": "^4.22.3",
|
||||
"ntfy": "^1.3.1",
|
||||
|
|
|
@ -80,6 +80,9 @@ dependencies:
|
|||
mongodb:
|
||||
specifier: ^5.7.0
|
||||
version: 5.7.0
|
||||
nanoid:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
next:
|
||||
specifier: 13.4.12
|
||||
version: 13.4.12(react-dom@18.2.0)(react@18.2.0)(sass@1.64.1)
|
||||
|
@ -3754,6 +3757,12 @@ packages:
|
|||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
/nanoid@5.0.1:
|
||||
resolution: {integrity: sha512-vWeVtV5Cw68aML/QaZvqN/3QQXc6fBfIieAlu05m7FZW2Dgb+3f0xc0TTxuJW+7u30t7iSDTV/j3kVI0oJqIfQ==}
|
||||
engines: {node: ^18 || >=20}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/natural-compare@1.4.0:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
|
||||
dev: false
|
||||
|
|
Loading…
Reference in New Issue