mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 17:24:59 -05:00
fix: minor fixes
This commit is contained in:
parent
3a727aad98
commit
fa421252c1
1
apps/www/.gitignore
vendored
1
apps/www/.gitignore
vendored
@ -35,6 +35,7 @@ yarn-error.log*
|
|||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env*.local
|
.env*.local
|
||||||
|
.env*.prod
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
|
||||||
<url><loc>https://mhsf.app/settings</loc><lastmod>2025-05-05T04:09:03.452Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/waitlist</loc><lastmod>2025-05-10T18:03:30.182Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
<url><loc>https://mhsf.app/support</loc><lastmod>2025-05-05T04:09:03.471Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/home</loc><lastmod>2025-05-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
<url><loc>https://mhsf.app/waitlist/oauth-need-discord</loc><lastmod>2025-05-05T04:09:03.471Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/waitlist/oauth-need-discord</loc><lastmod>2025-05-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
<url><loc>https://mhsf.app/waitlist/ref</loc><lastmod>2025-05-05T04:09:03.471Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/servers/embedded/sl-modification-frame/files</loc><lastmod>2025-05-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
<url><loc>https://mhsf.app/servers/embedded/sl-modification-frame</loc><lastmod>2025-05-05T04:09:03.471Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/support</loc><lastmod>2025-05-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
<url><loc>https://mhsf.app/home</loc><lastmod>2025-05-05T04:09:03.471Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/servers/embedded/sl-modification-frame</loc><lastmod>2025-05-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
<url><loc>https://mhsf.app/servers/embedded/sl-modification-frame/files</loc><lastmod>2025-05-05T04:09:03.471Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/settings</loc><lastmod>2025-05-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
<url><loc>https://mhsf.app/servers</loc><lastmod>2025-05-05T04:09:03.471Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/waitlist/ref</loc><lastmod>2025-05-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
<url><loc>https://mhsf.app/waitlist</loc><lastmod>2025-05-05T04:09:03.471Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<url><loc>https://mhsf.app/servers</loc><lastmod>2025-05-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||||
</urlset>
|
</urlset>
|
||||||
@ -29,6 +29,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import "tailwindcss";
|
@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';
|
@plugin 'tailwindcss-animate';
|
||||||
@config '../../tailwind-hero.config.ts';
|
@config '../../tailwind-hero.config.ts';
|
||||||
@ -36,6 +37,22 @@
|
|||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
@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 {
|
:root {
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
--border: 214.3 31.8% 91.4%;
|
--border: 214.3 31.8% 91.4%;
|
||||||
|
|||||||
@ -22,6 +22,18 @@ export function ModificationAction({ value }: { value?: Action }) {
|
|||||||
if (!(value !== undefined && "customAction" in value)) {
|
if (!(value !== undefined && "customAction" in value)) {
|
||||||
const filter = value as Filter;
|
const filter = value as Filter;
|
||||||
let existing = -1;
|
let existing = -1;
|
||||||
|
console.log(
|
||||||
|
(
|
||||||
|
(user?.unsafeMetadata.filters as Array<
|
||||||
|
ClerkEmbeddedFilter<unknown>
|
||||||
|
>) ?? []
|
||||||
|
).findIndex((c) => {
|
||||||
|
return (
|
||||||
|
JSON.stringify(c.metadata, replacer) === JSON.stringify(filter.toIdentifier(), replacer) &&
|
||||||
|
c.type === filter.getSpecificFilterId()
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
if (isSignedIn)
|
if (isSignedIn)
|
||||||
existing = (
|
existing = (
|
||||||
(user.unsafeMetadata.filters as Array<
|
(user.unsafeMetadata.filters as Array<
|
||||||
@ -29,8 +41,7 @@ export function ModificationAction({ value }: { value?: Action }) {
|
|||||||
>) ?? []
|
>) ?? []
|
||||||
).findIndex(
|
).findIndex(
|
||||||
(c) =>
|
(c) =>
|
||||||
JSON.stringify(c.metadata) ===
|
JSON.stringify(c.metadata, replacer) === JSON.stringify(filter.toIdentifier(), replacer) &&
|
||||||
JSON.stringify(filter.toIdentifier()) &&
|
|
||||||
c.type === filter.getSpecificFilterId(),
|
c.type === filter.getSpecificFilterId(),
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
@ -40,8 +51,7 @@ export function ModificationAction({ value }: { value?: Action }) {
|
|||||||
>) ?? []
|
>) ?? []
|
||||||
).findIndex(
|
).findIndex(
|
||||||
(c) =>
|
(c) =>
|
||||||
JSON.stringify(c.metadata) ===
|
JSON.stringify(c.metadata, replacer) === JSON.stringify(filter.toIdentifier(), replacer) &&
|
||||||
JSON.stringify(filter.toIdentifier()) &&
|
|
||||||
c.type === filter.getSpecificFilterId(),
|
c.type === filter.getSpecificFilterId(),
|
||||||
);
|
);
|
||||||
return existing;
|
return existing;
|
||||||
@ -120,12 +130,12 @@ export function ModificationAction({ value }: { value?: Action }) {
|
|||||||
type: filter.getSpecificFilterId(),
|
type: filter.getSpecificFilterId(),
|
||||||
metadata: filter.toIdentifier(),
|
metadata: filter.toIdentifier(),
|
||||||
},
|
},
|
||||||
]),
|
], replacer),
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"mhsf__filters",
|
"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;
|
||||||
@ -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;
|
||||||
|
}
|
||||||
@ -28,29 +28,58 @@
|
|||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { listenerCtx } from '@milkdown/kit/plugin/listener';
|
import { listenerCtx } from "@milkdown/kit/plugin/listener";
|
||||||
import { Crepe } from '@milkdown/crepe';
|
import { Crepe } from "@milkdown/crepe";
|
||||||
import { Milkdown, useEditor } from '@milkdown/react';
|
import { useEditor, type EditorInfoCtx, Milkdown } from "@milkdown/react";
|
||||||
|
|
||||||
import '@milkdown/crepe/theme/common/style.css';
|
import "@milkdown/crepe/theme/common/style.css";
|
||||||
import '@milkdown/crepe/theme/nord-dark.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 }) {
|
export function ServerEditorDescription({
|
||||||
useEditor((root) => {
|
defaultMarkdown,
|
||||||
|
onUpdate,
|
||||||
|
}: { defaultMarkdown: string; onUpdate?: (update: string) => void }) {
|
||||||
|
const { resolvedTheme } = useTheme();
|
||||||
|
const { loading } = useEditor((root) => {
|
||||||
const crepe = new Crepe({
|
const crepe = new Crepe({
|
||||||
root,
|
root,
|
||||||
defaultValue: defaultMarkdown,
|
defaultValue: defaultMarkdown,
|
||||||
});
|
});
|
||||||
crepe.editor.config(async (ctx) => {
|
crepe.editor.config(async (ctx) => {
|
||||||
ctx.get(listenerCtx).markdownUpdated((_, markdown) => {
|
ctx.get(listenerCtx).markdownUpdated((_, markdown) => {
|
||||||
if (onUpdate)
|
if (onUpdate) onUpdate(markdown);
|
||||||
onUpdate(markdown);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return crepe;
|
return crepe;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Milkdown />
|
<div className="h-[500px] max-h-[500px]">
|
||||||
|
{loading && (
|
||||||
|
<div>
|
||||||
|
<span className="flex items-center justify-center w-full">
|
||||||
|
<Spinner />
|
||||||
|
</span>
|
||||||
|
<span className="flex items-center justify-center w-full mt-2">
|
||||||
|
Loading Milkdown
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{resolvedTheme === "dark" ? (
|
||||||
|
<ServerDescriptionLightProvider>
|
||||||
|
<Milkdown />
|
||||||
|
</ServerDescriptionLightProvider>
|
||||||
|
) : (
|
||||||
|
<ServerDescriptionDarkProvider>
|
||||||
|
<Milkdown />
|
||||||
|
</ServerDescriptionDarkProvider>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const editorInfoContext = createContext<EditorInfoCtx>({} as EditorInfoCtx);
|
||||||
|
|||||||
@ -1,61 +1,257 @@
|
|||||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
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 { type ReactNode, useEffect, useState } from "react";
|
||||||
import { ServerEditorDescription } from "./server-editor-description";
|
import { ServerEditorDescription } from "./server-editor-description";
|
||||||
import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server";
|
import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server";
|
||||||
import { MilkdownProvider } from "@milkdown/react";
|
import { MilkdownProvider } from "@milkdown/react";
|
||||||
import { Material } from "@/components/ui/material";
|
import { Material } from "@/components/ui/material";
|
||||||
import { Placeholder } from "@/components/ui/placeholder";
|
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({
|
export function ServerEditorProvider({
|
||||||
children,
|
children,
|
||||||
serverData,
|
serverData,
|
||||||
|
minehutData,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode | ReactNode[];
|
children: ReactNode | ReactNode[];
|
||||||
serverData: ReturnType<typeof useMHSFServer>;
|
serverData: ReturnType<typeof useMHSFServer>;
|
||||||
|
minehutData: ServerResponse;
|
||||||
}) {
|
}) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const [onlineData, setOnlineData] = useState<OnlineServer>();
|
||||||
|
const { servers, loading } = useServers();
|
||||||
|
const [claimedUser, setClaimedUser] = useState<string>();
|
||||||
|
|
||||||
useEffect(() => {
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{children}
|
{children}
|
||||||
<MilkdownProvider>
|
<MilkdownProvider>
|
||||||
<Drawer open={open} onOpenChange={setOpen}>
|
<Drawer open={open} onOpenChange={setOpen}>
|
||||||
<DrawerContent className="p-4 ">
|
<DrawerContent className="p-4 ">
|
||||||
{serverData.server?.customizationData.isOwnedByUser ? (
|
<br />
|
||||||
|
{!serverData.server?.customizationData.isOwned ? (
|
||||||
<div className="h-full overflow-y-scroll">
|
<div className="h-full overflow-y-scroll">
|
||||||
<DrawerTitle className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl mb-3">
|
<DrawerTitle className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl mb-3">
|
||||||
Server Settings
|
Own your server.
|
||||||
</DrawerTitle>
|
</DrawerTitle>
|
||||||
<Material className="grid gap-1 max-h-[700px]">
|
<DrawerDescription>
|
||||||
<strong>Server Description</strong>
|
Own your server to completely control your server and how it
|
||||||
<p className="mb-3">
|
appears on MHSF.{" "}
|
||||||
A markdown enabled, fancy description for your server!
|
<Link
|
||||||
Describe what players will expect from your server and why
|
href="https://mhsf.mintlify.app/guides/owning-a-server"
|
||||||
they should join; don't worry, you have more space than
|
className="text-black dark:text-white"
|
||||||
MOTD's.
|
>
|
||||||
|
Learn more about server owning.
|
||||||
|
</Link>
|
||||||
|
</DrawerDescription>
|
||||||
|
{onlineData?.author === undefined && (
|
||||||
|
<Alert variant="error">
|
||||||
|
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.
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
<div className="mt-2 text-sm">
|
||||||
|
<p className="pb-2">
|
||||||
|
<code
|
||||||
|
className={cn(
|
||||||
|
"border rounded-full h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center",
|
||||||
|
requirementOne ? successClasses : errorClasses,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{requirementOne ? <Check size={16} /> : <X size={16} />}
|
||||||
|
</code>
|
||||||
|
<span className="ml-[2.25rem] pt-0.5 grid grid-rows-2">
|
||||||
|
<strong>Your server must be online.</strong> This is
|
||||||
|
required as this is the only way to ensure you are the
|
||||||
|
owner of the server.
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
{!serverData.loading && (
|
{requirementOne && (
|
||||||
<ServerEditorDescription
|
<>
|
||||||
defaultMarkdown={
|
<p className="pb-2">
|
||||||
serverData.server?.customizationData.description ?? ""
|
<code
|
||||||
}
|
className={cn(
|
||||||
/>
|
"border rounded-full h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center",
|
||||||
|
requirementTwo !== null
|
||||||
|
? successClasses
|
||||||
|
: errorClasses,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{requirementTwo !== null ? (
|
||||||
|
<Check size={16} />
|
||||||
|
) : (
|
||||||
|
<X size={16} />
|
||||||
|
)}
|
||||||
|
</code>
|
||||||
|
<span className="ml-[2.25rem] pt-0.5 grid grid-rows-2">
|
||||||
|
<strong>
|
||||||
|
Your server must be on the server list.
|
||||||
|
</strong>
|
||||||
|
This sometimes can't be achieved if the server isn't
|
||||||
|
visible or another issue appears.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
{requirementTwo && (
|
||||||
|
<>
|
||||||
|
<p className="pb-2">
|
||||||
|
<code
|
||||||
|
className={cn(
|
||||||
|
"border rounded-full h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center",
|
||||||
|
requirementThree
|
||||||
|
? successClasses
|
||||||
|
: errorClasses,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{requirementThree ? (
|
||||||
|
<Check size={16} />
|
||||||
|
) : (
|
||||||
|
<X size={16} />
|
||||||
|
)}
|
||||||
|
</code>
|
||||||
|
<span className="ml-[2.25rem] pt-0.5 grid grid-rows-2">
|
||||||
|
<strong>
|
||||||
|
Your account must be linked to{" "}
|
||||||
|
<i>{onlineData?.author}</i>
|
||||||
|
</strong>
|
||||||
|
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.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="pb-2">
|
||||||
|
<code
|
||||||
|
className={cn(
|
||||||
|
"border rounded-full h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center",
|
||||||
|
requirementFour ? successClasses : errorClasses,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{requirementFour ? (
|
||||||
|
<Check size={16} />
|
||||||
|
) : (
|
||||||
|
<X size={16} />
|
||||||
|
)}
|
||||||
|
</code>
|
||||||
|
<span className="ml-[2.25rem] pt-0.5 grid grid-rows-2">
|
||||||
|
<strong>
|
||||||
|
Your account must be linked on MHSF
|
||||||
|
</strong>
|
||||||
|
You must link your account to your Minecraft: Java
|
||||||
|
Edition account to be able to link this server.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</Material>
|
</div>
|
||||||
|
<DrawerFooter>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
toast.promise(serverData.ownServer(), {
|
||||||
|
success: "Successfully owned server",
|
||||||
|
error:
|
||||||
|
"There was an error while linking this server. Please contact support.",
|
||||||
|
loading: "Linking server...",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={
|
||||||
|
!(
|
||||||
|
requirementOne &&
|
||||||
|
requirementTwo &&
|
||||||
|
requirementThree &&
|
||||||
|
requirementFour
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Own Server
|
||||||
|
</Button>
|
||||||
|
</DrawerFooter>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Placeholder
|
<>
|
||||||
icon={<X />}
|
{serverData.server?.customizationData.isOwnedByUser ? (
|
||||||
className="h-full justify-center flex items-center"
|
<div className="max-h-[400px] overflow-y-scroll">
|
||||||
title="You don't own this server"
|
<DrawerTitle className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl mb-3">
|
||||||
description="Unfortunately, that one ain't gonna work. Atleast not on my watch."
|
Server Settings
|
||||||
/>
|
</DrawerTitle>
|
||||||
|
<Material className="grid gap-1 max-h-[700px]">
|
||||||
|
<strong>Server Description</strong>
|
||||||
|
<p className="mb-3">
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
{!serverData.loading && (
|
||||||
|
<ServerEditorDescription
|
||||||
|
defaultMarkdown={
|
||||||
|
serverData.server?.customizationData.description ??
|
||||||
|
""
|
||||||
|
}
|
||||||
|
onUpdate={(content) => console.log(content)}
|
||||||
|
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Material>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Placeholder
|
||||||
|
icon={<X />}
|
||||||
|
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."
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ServerResponse } from "@/lib/types/mh-server";
|
import { ServerResponse } from "@/lib/types/mh-server";
|
||||||
import { SignedIn, SignedOut, useClerk } from "@clerk/nextjs";
|
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 { useFavoriteStore } from "@/lib/hooks/use-favorite-store";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server";
|
import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server";
|
||||||
@ -105,6 +105,10 @@ export function ServerPageButtons({
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
<DropdownMenuSeparator>Server</DropdownMenuSeparator>
|
<DropdownMenuSeparator>Server</DropdownMenuSeparator>
|
||||||
|
<DropdownMenuItem className="flex items-center gap-2" onClick={() => window.dispatchEvent(new Event("open-server-editor"))}>
|
||||||
|
<Pencil size={16} />
|
||||||
|
Edit Server
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem className="flex items-center gap-2">
|
<DropdownMenuItem className="flex items-center gap-2">
|
||||||
<Share size={16} />
|
<Share size={16} />
|
||||||
Share
|
Share
|
||||||
|
|||||||
@ -72,7 +72,7 @@ export function ServerProvider({ serverId }: { serverId: string }) {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="px-10">
|
<div className="px-10">
|
||||||
<ServerEditorProvider serverData={mhsf}>
|
<ServerEditorProvider serverData={mhsf} minehutData={server as ServerResponse}>
|
||||||
<ReportingProvider server={mhsf}>
|
<ReportingProvider server={mhsf}>
|
||||||
<ServerMainPage
|
<ServerMainPage
|
||||||
server={server as ServerResponse}
|
server={server as ServerResponse}
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
import { getBackendProcedure } from "@/lib/backend-procedure";
|
import { getBackendProcedure } from "@/lib/backend-procedure";
|
||||||
import type { MHSFData } from "@/lib/types/data";
|
import type { MHSFData } from "@/lib/types/data";
|
||||||
import { clerkClient, getAuth } from "@clerk/nextjs/server";
|
import { clerkClient, getAuth, User } from "@clerk/nextjs/server";
|
||||||
import { MongoClient } from "mongodb";
|
import { MongoClient } from "mongodb";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
@ -78,12 +78,12 @@ export default async function handler(
|
|||||||
const serverData = await findServerData(server as string);
|
const serverData = await findServerData(server as string);
|
||||||
if (!serverData.exists) return res.status(404).send({ server: null });
|
if (!serverData.exists) return res.status(404).send({ server: null });
|
||||||
|
|
||||||
const mongo = new MongoClient(process.env.MONGO_DB as string);
|
const mongo = new MongoClient(process.env.MONGO_DB as string);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await mongo.connect();
|
await mongo.connect();
|
||||||
const db = mongo.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
const db = mongo.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const {userId} = getAuth(req);
|
const { userId } = getAuth(req);
|
||||||
|
|
||||||
// Run queries in parallel
|
// Run queries in parallel
|
||||||
const [favoriteData, customizationData, playerData, achievements] =
|
const [favoriteData, customizationData, playerData, achievements] =
|
||||||
@ -93,7 +93,12 @@ export default async function handler(
|
|||||||
favoriteTimespanStart,
|
favoriteTimespanStart,
|
||||||
favoriteTimespanEnd,
|
favoriteTimespanEnd,
|
||||||
}),
|
}),
|
||||||
findCustomizationData(serverData.name, userId ?? undefined, db),
|
findCustomizationData(
|
||||||
|
serverData.name,
|
||||||
|
server as string,
|
||||||
|
userId ?? undefined,
|
||||||
|
db,
|
||||||
|
),
|
||||||
findPlayerData(serverData.name, db, {
|
findPlayerData(serverData.name, db, {
|
||||||
maxPlayerEntries,
|
maxPlayerEntries,
|
||||||
playerTimespanStart,
|
playerTimespanStart,
|
||||||
@ -136,6 +141,7 @@ export default async function handler(
|
|||||||
|
|
||||||
async function findCustomizationData(
|
async function findCustomizationData(
|
||||||
serverName: string,
|
serverName: string,
|
||||||
|
serverId: string,
|
||||||
userId: string | undefined,
|
userId: string | undefined,
|
||||||
db: any,
|
db: any,
|
||||||
): Promise<{
|
): Promise<{
|
||||||
@ -150,18 +156,40 @@ async function findCustomizationData(
|
|||||||
const clerk = await clerkClient();
|
const clerk = await clerkClient();
|
||||||
// Run queries in parallel
|
// Run queries in parallel
|
||||||
const [customizationData, ownedServerData] = await Promise.all([
|
const [customizationData, ownedServerData] = await Promise.all([
|
||||||
db.collection("customization").findOne({ server: serverName }),
|
db.collection("customization").findOne({ server: serverId }),
|
||||||
userId
|
userId
|
||||||
? db.collection("owned-servers").findOne({ server: serverName })
|
? db.collection("owned-servers").findOne({ server: serverId })
|
||||||
: null,
|
: null,
|
||||||
]);
|
]);
|
||||||
|
let user: User | undefined = undefined;
|
||||||
|
try {
|
||||||
|
user = await clerk.users.getUser(ownedServerData?.author);
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
isOwned: false,
|
||||||
|
isOwnedByUser: false,
|
||||||
|
description: undefined,
|
||||||
|
banner: undefined,
|
||||||
|
discord: undefined,
|
||||||
|
colorScheme: undefined,
|
||||||
|
userProfilePicture: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (customizationData) {
|
console.log(
|
||||||
|
ownedServerData?.author === userId,
|
||||||
|
userId,
|
||||||
|
ownedServerData?.author,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (customizationData || ownedServerData) {
|
||||||
return {
|
return {
|
||||||
...(customizationData as any),
|
...(customizationData as any),
|
||||||
isOwned: true,
|
isOwned: true,
|
||||||
isOwnedByUser: ownedServerData?.author === userId,
|
isOwnedByUser: ownedServerData?.author === userId,
|
||||||
userProfilePicture: userId ? (await clerk.users.getUser(ownedServerData?.author)).imageUrl : 'no user'
|
userProfilePicture: userId
|
||||||
|
? user.imageUrl
|
||||||
|
: "no user",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,8 +36,8 @@ import { waitUntil } from "@vercel/functions";
|
|||||||
import { getBackendProcedure } from "@/lib/backend-procedure";
|
import { getBackendProcedure } from "@/lib/backend-procedure";
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse
|
res: NextApiResponse,
|
||||||
) {
|
) {
|
||||||
const backendProcedure = await getBackendProcedure(req);
|
const backendProcedure = await getBackendProcedure(req);
|
||||||
|
|
||||||
@ -45,90 +45,79 @@ export default async function handler(
|
|||||||
return res.status(403).json({
|
return res.status(403).json({
|
||||||
error: `Backend procedure marked request as '${backendProcedure.status}' instead of required 'OK'`,
|
error: `Backend procedure marked request as '${backendProcedure.status}' instead of required 'OK'`,
|
||||||
});
|
});
|
||||||
const { userId } = getAuth(req);
|
const { userId } = getAuth(req);
|
||||||
const { server } = req.query;
|
const { server } = req.query;
|
||||||
|
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
res.status(400).send({ message: "Couldn't find data" });
|
res.status(400).send({ message: "Couldn't find data" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return res.status(401).json({ error: "Unauthorized" });
|
return res.status(401).json({ error: "Unauthorized" });
|
||||||
}
|
}
|
||||||
if (
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
(await (await clerkClient()).users.getUser(userId)).publicMetadata.player ===
|
await client.connect();
|
||||||
undefined
|
|
||||||
) {
|
|
||||||
return res.status(401).json({ error: "Account not linked" });
|
|
||||||
}
|
|
||||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
|
||||||
await client.connect();
|
|
||||||
|
|
||||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("owned-servers");
|
const collection = db.collection("owned-servers");
|
||||||
|
const users = db.collection("claimed-users");
|
||||||
|
|
||||||
if ((await collection.findOne({ server: server })) === undefined) {
|
if ((await users.findOne({ userId })) === null) {
|
||||||
const mh = await fetch(
|
return res.status(401).json({ error: "Account not linked" });
|
||||||
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<OnlineServer> = (await mh.json()).servers;
|
|
||||||
|
|
||||||
servers.forEach(async (c, i) => {
|
const minecraftUsername = (await users.findOne({ userId }))?.player;
|
||||||
if (c.name === server) {
|
|
||||||
const MCUsername = (await (await clerkClient()).users.getUser(userId))
|
|
||||||
.publicMetadata.player;
|
|
||||||
|
|
||||||
if (MCUsername === c.author) {
|
if ((await collection.findOne({ server })) === null) {
|
||||||
await collection.insertOne({ server, author: userId });
|
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<OnlineServer> = (await mh.json()).servers;
|
||||||
|
const serverObj = servers.find((c) => c.staticInfo._id === server);
|
||||||
|
|
||||||
// Close the database, but don't close this
|
if (serverObj === undefined)
|
||||||
// serverless instance until it happens
|
return res
|
||||||
waitUntil(client.close());
|
.status(400)
|
||||||
|
.send({ message: "The server needs to be online." });
|
||||||
|
|
||||||
res.send({ message: "Successfully owned server!" });
|
if (minecraftUsername === serverObj.author) {
|
||||||
} else {
|
await collection.insertOne({ server, author: userId });
|
||||||
// Close the database, but don't close this
|
|
||||||
// serverless instance until it happens
|
|
||||||
waitUntil(client.close());
|
|
||||||
|
|
||||||
res
|
// Close the database, but don't close this
|
||||||
.status(400)
|
// serverless instance until it happens
|
||||||
.send({ message: "The linked account doesn't own the server." });
|
waitUntil(client.close());
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i === servers.length) {
|
|
||||||
// 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." });
|
res.send({ message: "Successfully owned server!" });
|
||||||
}
|
} else {
|
||||||
});
|
res
|
||||||
} else {
|
.status(400)
|
||||||
// Close the database, but don't close this
|
.send({ message: "The linked account doesn't own the server." });
|
||||||
// serverless instance until it happens
|
}
|
||||||
waitUntil(client.close());
|
} 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." });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
apps/www/src/pages/api/v1/user/claimed-user.ts
Normal file
52
apps/www/src/pages/api/v1/user/claimed-user.ts
Normal file
@ -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});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user