From fa421252c1f752dd76fe9ab3175449f26e2f42cf Mon Sep 17 00:00:00 2001 From: dvelo <52332868+DeveloLongScript@users.noreply.github.com> Date: Sat, 10 May 2025 13:07:01 -0500 Subject: [PATCH] fix: minor fixes --- apps/www/.gitignore | 1 + apps/www/public/sitemap-0.xml | 18 +- apps/www/src/app/globals.css | 17 ++ .../modification/modification-action.tsx | 31 ++- .../server-editor/server-dark-provider.tsx | 36 +++ .../server-editor-description.tsx | 51 +++- .../server-editor/server-editor-provider.tsx | 246 ++++++++++++++++-- .../server-editor/server-light-provider.tsx | 36 +++ .../feat/server-page/server-page-buttons.tsx | 6 +- .../feat/server-page/server-provider.tsx | 2 +- .../pages/api/v1/server/get/[server]/index.ts | 44 +++- .../api/v1/server/get/[server]/own-server.ts | 143 +++++----- .../www/src/pages/api/v1/user/claimed-user.ts | 52 ++++ 13 files changed, 545 insertions(+), 138 deletions(-) create mode 100644 apps/www/src/components/feat/server-page/server-editor/server-dark-provider.tsx create mode 100644 apps/www/src/components/feat/server-page/server-editor/server-light-provider.tsx create mode 100644 apps/www/src/pages/api/v1/user/claimed-user.ts diff --git a/apps/www/.gitignore b/apps/www/.gitignore index 36a6e5e..dcc406e 100644 --- a/apps/www/.gitignore +++ b/apps/www/.gitignore @@ -35,6 +35,7 @@ yarn-error.log* # local env files .env*.local +.env*.prod # vercel .vercel diff --git a/apps/www/public/sitemap-0.xml b/apps/www/public/sitemap-0.xml index 84d9307..0dc28c3 100644 --- a/apps/www/public/sitemap-0.xml +++ b/apps/www/public/sitemap-0.xml @@ -1,12 +1,12 @@ -https://mhsf.app/settings2025-05-05T04:09:03.452Zdaily0.7 -https://mhsf.app/support2025-05-05T04:09:03.471Zdaily0.7 -https://mhsf.app/waitlist/oauth-need-discord2025-05-05T04:09:03.471Zdaily0.7 -https://mhsf.app/waitlist/ref2025-05-05T04:09:03.471Zdaily0.7 -https://mhsf.app/servers/embedded/sl-modification-frame2025-05-05T04:09:03.471Zdaily0.7 -https://mhsf.app/home2025-05-05T04:09:03.471Zdaily0.7 -https://mhsf.app/servers/embedded/sl-modification-frame/files2025-05-05T04:09:03.471Zdaily0.7 -https://mhsf.app/servers2025-05-05T04:09:03.471Zdaily0.7 -https://mhsf.app/waitlist2025-05-05T04:09:03.471Zdaily0.7 +https://mhsf.app/waitlist2025-05-10T18:03:30.182Zdaily0.7 +https://mhsf.app/home2025-05-10T18:03:30.186Zdaily0.7 +https://mhsf.app/waitlist/oauth-need-discord2025-05-10T18:03:30.186Zdaily0.7 +https://mhsf.app/servers/embedded/sl-modification-frame/files2025-05-10T18:03:30.186Zdaily0.7 +https://mhsf.app/support2025-05-10T18:03:30.186Zdaily0.7 +https://mhsf.app/servers/embedded/sl-modification-frame2025-05-10T18:03:30.186Zdaily0.7 +https://mhsf.app/settings2025-05-10T18:03:30.186Zdaily0.7 +https://mhsf.app/waitlist/ref2025-05-10T18:03:30.186Zdaily0.7 +https://mhsf.app/servers2025-05-10T18:03:30.186Zdaily0.7 \ No newline at end of file diff --git a/apps/www/src/app/globals.css b/apps/www/src/app/globals.css index ca8e366..597ad3b 100644 --- a/apps/www/src/app/globals.css +++ b/apps/www/src/app/globals.css @@ -29,6 +29,7 @@ */ @import "tailwindcss"; +@import url('https://fonts.googleapis.com/css2?family=Inter+Tight:ital,wght@0,100..900;1,100..900&display=swap'); @plugin 'tailwindcss-animate'; @config '../../tailwind-hero.config.ts'; @@ -36,6 +37,22 @@ @custom-variant dark (&:is(.dark *)); +.milkdown { + --crepe-font-title: "Inter Tight", "Roboto" !important; + + .milkdown-slash-menu { + position: fixed !important; + + } + .dark .milkdown-icon { + fill: white !important; + } + + .dark { + @import "@milkdown/crepe/theme/nord-dark.css"; + } +} + :root { --background: 0 0% 100%; --border: 214.3 31.8% 91.4%; diff --git a/apps/www/src/components/feat/server-list/modification/modification-action.tsx b/apps/www/src/components/feat/server-list/modification/modification-action.tsx index e61b17a..96d0715 100644 --- a/apps/www/src/components/feat/server-list/modification/modification-action.tsx +++ b/apps/www/src/components/feat/server-list/modification/modification-action.tsx @@ -22,6 +22,18 @@ export function ModificationAction({ value }: { value?: Action }) { if (!(value !== undefined && "customAction" in value)) { const filter = value as Filter; let existing = -1; + console.log( + ( + (user?.unsafeMetadata.filters as Array< + ClerkEmbeddedFilter + >) ?? [] + ).findIndex((c) => { + return ( + JSON.stringify(c.metadata, replacer) === JSON.stringify(filter.toIdentifier(), replacer) && + c.type === filter.getSpecificFilterId() + ); + }), + ); if (isSignedIn) existing = ( (user.unsafeMetadata.filters as Array< @@ -29,8 +41,7 @@ export function ModificationAction({ value }: { value?: Action }) { >) ?? [] ).findIndex( (c) => - JSON.stringify(c.metadata) === - JSON.stringify(filter.toIdentifier()) && + JSON.stringify(c.metadata, replacer) === JSON.stringify(filter.toIdentifier(), replacer) && c.type === filter.getSpecificFilterId(), ); else @@ -40,8 +51,7 @@ export function ModificationAction({ value }: { value?: Action }) { >) ?? [] ).findIndex( (c) => - JSON.stringify(c.metadata) === - JSON.stringify(filter.toIdentifier()) && + JSON.stringify(c.metadata, replacer) === JSON.stringify(filter.toIdentifier(), replacer) && c.type === filter.getSpecificFilterId(), ); return existing; @@ -120,12 +130,12 @@ export function ModificationAction({ value }: { value?: Action }) { type: filter.getSpecificFilterId(), metadata: filter.toIdentifier(), }, - ]), + ], replacer), ); else localStorage.setItem( "mhsf__filters", - JSON.stringify(existingArray), + JSON.stringify(existingArray, replacer), ); } @@ -140,3 +150,12 @@ export function ModificationAction({ value }: { value?: Action }) { ); } +const replacer = (key, value) => + value instanceof Object && !(Array.isArray(value)) ? + Object.keys(value) + .sort() + .reduce((sorted, key) => { + sorted[key] = value[key]; + return sorted + }, {}) : + value; \ No newline at end of file diff --git a/apps/www/src/components/feat/server-page/server-editor/server-dark-provider.tsx b/apps/www/src/components/feat/server-page/server-editor/server-dark-provider.tsx new file mode 100644 index 0000000..d233ac3 --- /dev/null +++ b/apps/www/src/components/feat/server-page/server-editor/server-dark-provider.tsx @@ -0,0 +1,36 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2025 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +import '@milkdown/crepe/theme/nord-dark.css'; +import type { ReactNode } from 'react'; + +export function ServerDescriptionDarkProvider({children}: {children: ReactNode}) { + return children; +} \ No newline at end of file diff --git a/apps/www/src/components/feat/server-page/server-editor/server-editor-description.tsx b/apps/www/src/components/feat/server-page/server-editor/server-editor-description.tsx index 93aab10..dbeca36 100644 --- a/apps/www/src/components/feat/server-page/server-editor/server-editor-description.tsx +++ b/apps/www/src/components/feat/server-page/server-editor/server-editor-description.tsx @@ -28,29 +28,58 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -import { listenerCtx } from '@milkdown/kit/plugin/listener'; -import { Crepe } from '@milkdown/crepe'; -import { Milkdown, useEditor } from '@milkdown/react'; +import { listenerCtx } from "@milkdown/kit/plugin/listener"; +import { Crepe } from "@milkdown/crepe"; +import { useEditor, type EditorInfoCtx, Milkdown } from "@milkdown/react"; -import '@milkdown/crepe/theme/common/style.css'; -import '@milkdown/crepe/theme/nord-dark.css'; +import "@milkdown/crepe/theme/common/style.css"; +import { createContext } from "react"; +import { Spinner } from "@/components/ui/spinner"; +import { ServerDescriptionLightProvider } from "./server-light-provider"; +import { useTheme } from "@/lib/hooks/use-theme"; +import { ServerDescriptionDarkProvider } from "./server-dark-provider"; -export function ServerEditorDescription({ defaultMarkdown, onUpdate }: { defaultMarkdown: string, onUpdate?: (update: string) => void }) { - useEditor((root) => { +export function ServerEditorDescription({ + defaultMarkdown, + onUpdate, +}: { defaultMarkdown: string; onUpdate?: (update: string) => void }) { + const { resolvedTheme } = useTheme(); + const { loading } = useEditor((root) => { const crepe = new Crepe({ root, defaultValue: defaultMarkdown, - }); + }); crepe.editor.config(async (ctx) => { ctx.get(listenerCtx).markdownUpdated((_, markdown) => { - if (onUpdate) - onUpdate(markdown); + if (onUpdate) onUpdate(markdown); }); }); return crepe; }, []); return ( - +
+ {loading && ( +
+ + + + + Loading Milkdown + +
+ )} + {resolvedTheme === "dark" ? ( + + + + ) : ( + + + + )} +
); } + +const editorInfoContext = createContext({} as EditorInfoCtx); diff --git a/apps/www/src/components/feat/server-page/server-editor/server-editor-provider.tsx b/apps/www/src/components/feat/server-page/server-editor/server-editor-provider.tsx index bc41d58..10c93bc 100644 --- a/apps/www/src/components/feat/server-page/server-editor/server-editor-provider.tsx +++ b/apps/www/src/components/feat/server-page/server-editor/server-editor-provider.tsx @@ -1,61 +1,257 @@ import { Dialog, DialogContent } from "@/components/ui/dialog"; -import { Drawer, DrawerContent, DrawerTitle } from "@/components/ui/drawer"; +import { + Drawer, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerTitle, +} from "@/components/ui/drawer"; import { type ReactNode, useEffect, useState } from "react"; import { ServerEditorDescription } from "./server-editor-description"; import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server"; import { MilkdownProvider } from "@milkdown/react"; import { Material } from "@/components/ui/material"; import { Placeholder } from "@/components/ui/placeholder"; -import { X } from "lucide-react"; +import { Check, X } from "lucide-react"; +import { Link } from "@/components/util/link"; +import { Button } from "@/components/ui/button"; +import { cn } from "@/lib/utils"; +import type { OnlineServer, ServerResponse } from "@/lib/types/mh-server"; +import { useServers } from "@/lib/hooks/use-servers"; +import { Alert } from "@/components/ui/alert"; +import { toast } from "sonner"; + +const successClasses = + "bg-green-200 border-green-400 dark:bg-green-800 dark:border-green-600"; +const errorClasses = + "bg-red-200 border-red-400 dark:bg-red-800 dark:border-red-600"; export function ServerEditorProvider({ children, serverData, + minehutData, }: { children: ReactNode | ReactNode[]; serverData: ReturnType; + minehutData: ServerResponse; }) { const [open, setOpen] = useState(false); + const [onlineData, setOnlineData] = useState(); + const { servers, loading } = useServers(); + const [claimedUser, setClaimedUser] = useState(); useEffect(() => { - window.addEventListener("open-server-editor", () => setOpen(true)); + window.addEventListener("open-server-editor", () => { + setOpen(true); + }); }, []); + useEffect(() => { + if (open && !loading) { + const server = servers.find((c) => c.name === minehutData.name); + if (server !== null) { + setOnlineData(server); + } + } + }, [open, loading]); + + useEffect(() => { + (async () => { + const response = await fetch("/api/v1/user/claimed-user"); + const json = await response.json(); + + setClaimedUser(json.player ?? null); + })(); + }); + + const requirementOne = minehutData.online; + const requirementTwo = onlineData !== null; + const requirementThree = claimedUser === onlineData?.author; + const requirementFour = claimedUser !== null; + return ( <> {children} - {serverData.server?.customizationData.isOwnedByUser ? ( +
+ {!serverData.server?.customizationData.isOwned ? (
- Server Settings + Own your server. - - Server Description -

- A markdown enabled, fancy description for your server! - Describe what players will expect from your server and why - they should join; don't worry, you have more space than - MOTD's. + + Own your server to completely control your server and how it + appears on MHSF.{" "} + + Learn more about server owning. + + + {onlineData?.author === undefined && ( + + This server cannot be automatically linked as it doesn't + have an author. You must either contact support to link this + server or link your Minehut account to your Minecraft: Java + Edition one. + + )} +

+

+ + {requirementOne ? : } + + + Your server must be online. This is + required as this is the only way to ensure you are the + owner of the server. +

- {!serverData.loading && ( - + {requirementOne && ( + <> +

+ + {requirementTwo !== null ? ( + + ) : ( + + )} + + + + Your server must be on the server list. + + This sometimes can't be achieved if the server isn't + visible or another issue appears. + +

+ {requirementTwo && ( + <> +

+ + {requirementThree ? ( + + ) : ( + + )} + + + + Your account must be linked to{" "} + {onlineData?.author} + + You must link your MHSF account to your Minecraft: + Java Edition account that is the owner of this + server to be able to link this server. + +

+

+ + {requirementFour ? ( + + ) : ( + + )} + + + + Your account must be linked on MHSF + + You must link your account to your Minecraft: Java + Edition account to be able to link this server. + +

+ + )} + )} - +
+ + +
) : ( - } - className="h-full justify-center flex items-center" - title="You don't own this server" - description="Unfortunately, that one ain't gonna work. Atleast not on my watch." - /> + <> + {serverData.server?.customizationData.isOwnedByUser ? ( +
+ + Server Settings + + + Server Description +

+ A markdown enabled, fancy description for your server! + Describe what players will expect from your server and + why they should join; don't worry, you have more space + than MOTD's. +

+ {!serverData.loading && ( + console.log(content)} + + /> + )} +
+
+ ) : ( + } + className="h-full justify-center flex items-center" + title="You don't own this server" + description="Unfortunately, that one ain't gonna work. Atleast not on my watch." + /> + )} + )}
diff --git a/apps/www/src/components/feat/server-page/server-editor/server-light-provider.tsx b/apps/www/src/components/feat/server-page/server-editor/server-light-provider.tsx new file mode 100644 index 0000000..642313a --- /dev/null +++ b/apps/www/src/components/feat/server-page/server-editor/server-light-provider.tsx @@ -0,0 +1,36 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2025 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +import '@milkdown/crepe/theme/frame.css'; +import type { ReactNode } from 'react'; + +export function ServerDescriptionLightProvider({children}: {children: ReactNode}) { + return children; +} \ No newline at end of file diff --git a/apps/www/src/components/feat/server-page/server-page-buttons.tsx b/apps/www/src/components/feat/server-page/server-page-buttons.tsx index 85b593a..25792d1 100644 --- a/apps/www/src/components/feat/server-page/server-page-buttons.tsx +++ b/apps/www/src/components/feat/server-page/server-page-buttons.tsx @@ -31,7 +31,7 @@ import { Button } from "@/components/ui/button"; import { ServerResponse } from "@/lib/types/mh-server"; import { SignedIn, SignedOut, useClerk } from "@clerk/nextjs"; -import { EllipsisVertical, Flag, Heart, Share, Star } from "lucide-react"; +import { EllipsisVertical, Flag, Heart, Pencil, Share, Star } from "lucide-react"; import { useFavoriteStore } from "@/lib/hooks/use-favorite-store"; import { useState } from "react"; import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server"; @@ -105,6 +105,10 @@ export function ServerPageButtons({ Server + window.dispatchEvent(new Event("open-server-editor"))}> + + Edit Server + Share diff --git a/apps/www/src/components/feat/server-page/server-provider.tsx b/apps/www/src/components/feat/server-page/server-provider.tsx index 7b72db2..ee9bcff 100644 --- a/apps/www/src/components/feat/server-page/server-provider.tsx +++ b/apps/www/src/components/feat/server-page/server-provider.tsx @@ -72,7 +72,7 @@ export function ServerProvider({ serverId }: { serverId: string }) { ) : (
- + = (await mh.json()).servers; + if ((await users.findOne({ userId })) === null) { + return res.status(401).json({ error: "Account not linked" }); + } - servers.forEach(async (c, i) => { - if (c.name === server) { - const MCUsername = (await (await clerkClient()).users.getUser(userId)) - .publicMetadata.player; + const minecraftUsername = (await users.findOne({ userId }))?.player; - if (MCUsername === c.author) { - await collection.insertOne({ server, author: userId }); + if ((await collection.findOne({ server })) === null) { + const mh = await fetch( + process.env.MHSF_BACKEND_API_LOCATION ?? + "https://api.minehut.com/servers", + { + headers: { + accept: "*/*", + "accept-language": Math.random().toString(), + priority: "u=1, i", + "sec-ch-ua": '"Not/A)Brand";v="8", "Chromium";v="126"', + "sec-ch-ua-mobile": "?0", + "sec-ch-ua-platform": '"macOS"', + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "cross-site", + Referer: "http://localhost:3000/", + "Referrer-Policy": "strict-origin-when-cross-origin", + Authentication: `MHSF-Backend-Server ${process.env.MHSF_BACKEND_API_LOCATION ? process.env.MHSF_BACKEND_SECRET : "Sorry Minehut Devs."}`, + }, + body: null, + method: "GET", + }, + ); + const servers: Array = (await mh.json()).servers; + const serverObj = servers.find((c) => c.staticInfo._id === server); - // Close the database, but don't close this - // serverless instance until it happens - waitUntil(client.close()); + if (serverObj === undefined) + return res + .status(400) + .send({ message: "The server needs to be online." }); - res.send({ message: "Successfully owned server!" }); - } else { - // Close the database, but don't close this - // serverless instance until it happens - waitUntil(client.close()); + if (minecraftUsername === serverObj.author) { + await collection.insertOne({ server, author: userId }); - res - .status(400) - .send({ message: "The linked account doesn't own the server." }); - } - } - if (i === servers.length) { - // Close the database, but don't close this - // serverless instance until it happens - waitUntil(client.close()); + // Close the database, but don't close this + // serverless instance until it happens + waitUntil(client.close()); - res.status(400).send({ message: "The server needs to be online." }); - } - }); - } else { - // Close the database, but don't close this - // serverless instance until it happens - waitUntil(client.close()); + res.send({ message: "Successfully owned server!" }); + } else { + res + .status(400) + .send({ message: "The linked account doesn't own the server." }); + } + } else { + // Close the database, but don't close this + // serverless instance until it happens + waitUntil(client.close()); - res.status(400).send({ message: "This server has already been owned." }); - } + res.status(400).send({ message: "This server has already been owned." }); + } } diff --git a/apps/www/src/pages/api/v1/user/claimed-user.ts b/apps/www/src/pages/api/v1/user/claimed-user.ts new file mode 100644 index 0000000..ba024f5 --- /dev/null +++ b/apps/www/src/pages/api/v1/user/claimed-user.ts @@ -0,0 +1,52 @@ +/* + * MHSF, Minehut Server List + * All external content is rather licensed under the ECA Agreement + * located here: https://mhsf.app/docs/legal/external-content-agreement + * + * All code under MHSF is licensed under the MIT License + * by open source contributors + * + * Copyright (c) 2025 dvelo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +import type { NextApiRequest, NextApiResponse } from "next"; +import { getAuth, clerkClient } from "@clerk/nextjs/server"; +import { MongoClient } from "mongodb"; +import { waitUntil } from "@vercel/functions"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + const { userId } = getAuth(req); + + if (!userId) { + return res.status(401).json({ error: "Unauthorized" }); + } + const client = new MongoClient(process.env.MONGO_DB as string); + await client.connect(); + + const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf"); + const users = db.collection("claimed-users"); + + return res.send((await users.findOne({ userId })) ?? {player: null}); +}