diff --git a/app/panel/inspect/page.tsx b/app/panel/inspect/page.tsx
index c51266b..f73b17a 100644
--- a/app/panel/inspect/page.tsx
+++ b/app/panel/inspect/page.tsx
@@ -3,7 +3,7 @@
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { toast } from "@/components/ui/use-toast";
-import { lookupEmail } from "@/lib/actions";
+import { lookupEmail, searchUserByTag } from "@/lib/actions";
import { API_URL } from "@/lib/constants";
import { useRouter } from "next/navigation";
import { useState } from "react";
@@ -11,6 +11,8 @@ import { useState } from "react";
export default function Inspect() {
const [id, setId] = useState("");
const [email, setEmail] = useState("");
+ const [username, setUsername] = useState("");
+ const [discriminator, setDiscriminator] = useState("");
const router = useRouter();
const searchEmail = async () => {
@@ -31,6 +33,29 @@ export default function Inspect() {
}
};
+ const searchUsername = async () => {
+ try {
+ if (!discriminator) {
+ // Display all users with this username
+ router.push(`/panel/inspect/search?username=${encodeURIComponent(username)}`);
+ } else {
+ // Show the specific user that matches username#discriminator
+ const result = await searchUserByTag(username, discriminator);
+ if (!result) toast({
+ title: "Couldn't find user",
+ variant: "destructive",
+ });
+ else router.push(`/panel/inspect/user/${result}`);
+ }
+ } catch(e) {
+ toast({
+ title: "Failed to search",
+ description: String(e),
+ variant: "destructive",
+ })
+ }
+ };
+
const createHandler = (type: string) => () =>
router.push(`/panel/inspect/${type}/${id}`);
@@ -41,7 +66,7 @@ export default function Inspect() {
value={id}
onChange={(e) => setId(e.currentTarget.value)}
/>
-
+
-
-
setEmail(e.currentTarget.value)}
- onKeyDown={(e) => e.key == "Enter" && email && searchEmail()}
- />
-
+
+
+ setEmail(e.currentTarget.value)}
+ onKeyDown={(e) => e.key == "Enter" && email && searchEmail()}
+ />
+
+
+
);
}
diff --git a/app/panel/inspect/search/page.tsx b/app/panel/inspect/search/page.tsx
new file mode 100644
index 0000000..6b032cf
--- /dev/null
+++ b/app/panel/inspect/search/page.tsx
@@ -0,0 +1,34 @@
+import { UserCard } from "@/components/cards/UserCard";
+import { fetchUsersByUsername } from "@/lib/actions";
+import { SearchX } from "lucide-react";
+import { redirect } from "next/navigation";
+
+export default async function Search({ searchParams }: { searchParams: any }) {
+ const username = searchParams.username;
+
+ if (!username) return redirect("/panel/inspect");
+ const users = await fetchUsersByUsername(username);
+
+ if (!users.length) return (
+ <>
+
+
+
+
+ No search results
+
+ >
+ );
+
+ return (
+
+ {
+ users.map((user) => (
+
+
+
+ ))
+ }
+
+ );
+}
diff --git a/lib/accessPermissions.ts b/lib/accessPermissions.ts
index 59f4196..aa5f108 100644
--- a/lib/accessPermissions.ts
+++ b/lib/accessPermissions.ts
@@ -62,6 +62,8 @@ type Permission =
| `/fetch${
| ""
| "/by-id"
+ | "/by-tag"
+ | "/bulk-by-username"
| "/memberships"
| "/strikes"
| "/notices"
@@ -163,6 +165,8 @@ const PermissionSets = {
// Moderate users
"moderate-users": [
"users/fetch/by-id",
+ "users/fetch/by-tag",
+ "users/fetch/bulk-by-username",
"users/fetch/strikes",
"users/fetch/notices",
diff --git a/lib/actions.ts b/lib/actions.ts
index 210bf27..9c33b69 100644
--- a/lib/actions.ts
+++ b/lib/actions.ts
@@ -1027,3 +1027,29 @@ export async function deleteEmailClassification(domain: string) {
{ _id: domain },
);
}
+
+export async function searchUserByTag(username: string, discriminator: string): Promise
{
+ await checkPermission("users/fetch/by-tag", { username, discriminator });
+
+ const result = await mongo()
+ .db("revolt")
+ .collection("users")
+ .findOne({
+ username,
+ discriminator,
+ });
+
+ return result?._id || false;
+}
+
+export async function fetchUsersByUsername(username: string) {
+ await checkPermission("users/fetch/bulk-by-username", { username });
+
+ return await mongo()
+ .db("revolt")
+ .collection("users")
+ .find({
+ username,
+ })
+ .toArray();
+}