mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-09 08:05:00 -05:00
Compare commits
No commits in common. "66de2e9c120a738940006af08f4a95c69fbab6b3" and "3a727aad984ee04697d67bbcbd32b93aabcba4f3" have entirely different histories.
66de2e9c12
...
3a727aad98
1
apps/www/.gitignore
vendored
1
apps/www/.gitignore
vendored
@ -35,7 +35,6 @@ 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/waitlist</loc><lastmod>2025-05-10T18:03:30.182Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
<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/home</loc><lastmod>2025-05-10T18:03:30.186Z</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/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/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/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/waitlist/ref</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/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/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/home</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/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/waitlist/ref</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/servers</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>
|
||||||
</urlset>
|
</urlset>
|
||||||
@ -29,7 +29,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@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';
|
||||||
@ -37,22 +36,6 @@
|
|||||||
|
|
||||||
@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,18 +22,6 @@ 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<
|
||||||
@ -41,7 +29,8 @@ export function ModificationAction({ value }: { value?: Action }) {
|
|||||||
>) ?? []
|
>) ?? []
|
||||||
).findIndex(
|
).findIndex(
|
||||||
(c) =>
|
(c) =>
|
||||||
JSON.stringify(c.metadata, replacer) === JSON.stringify(filter.toIdentifier(), replacer) &&
|
JSON.stringify(c.metadata) ===
|
||||||
|
JSON.stringify(filter.toIdentifier()) &&
|
||||||
c.type === filter.getSpecificFilterId(),
|
c.type === filter.getSpecificFilterId(),
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
@ -51,7 +40,8 @@ export function ModificationAction({ value }: { value?: Action }) {
|
|||||||
>) ?? []
|
>) ?? []
|
||||||
).findIndex(
|
).findIndex(
|
||||||
(c) =>
|
(c) =>
|
||||||
JSON.stringify(c.metadata, replacer) === JSON.stringify(filter.toIdentifier(), replacer) &&
|
JSON.stringify(c.metadata) ===
|
||||||
|
JSON.stringify(filter.toIdentifier()) &&
|
||||||
c.type === filter.getSpecificFilterId(),
|
c.type === filter.getSpecificFilterId(),
|
||||||
);
|
);
|
||||||
return existing;
|
return existing;
|
||||||
@ -130,12 +120,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, replacer),
|
JSON.stringify(existingArray),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,12 +140,3 @@ 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;
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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,58 +28,29 @@
|
|||||||
* 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 { useEditor, type EditorInfoCtx, Milkdown } from "@milkdown/react";
|
import { Milkdown, useEditor } from '@milkdown/react';
|
||||||
|
|
||||||
import "@milkdown/crepe/theme/common/style.css";
|
import '@milkdown/crepe/theme/common/style.css';
|
||||||
import { createContext } from "react";
|
import '@milkdown/crepe/theme/nord-dark.css';
|
||||||
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({
|
export function ServerEditorDescription({ defaultMarkdown, onUpdate }: { defaultMarkdown: string, onUpdate?: (update: string) => void }) {
|
||||||
defaultMarkdown,
|
useEditor((root) => {
|
||||||
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) onUpdate(markdown);
|
if (onUpdate)
|
||||||
|
onUpdate(markdown);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return crepe;
|
return crepe;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-[500px] max-h-[500px]">
|
<Milkdown />
|
||||||
{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,257 +1,61 @@
|
|||||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||||
import {
|
import { Drawer, DrawerContent, DrawerTitle } from "@/components/ui/drawer";
|
||||||
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 { Check, X } from "lucide-react";
|
import { 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", () => {
|
window.addEventListener("open-server-editor", () => setOpen(true));
|
||||||
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 ">
|
||||||
<br />
|
{serverData.server?.customizationData.isOwnedByUser ? (
|
||||||
{!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">
|
||||||
Own your server.
|
Server Settings
|
||||||
</DrawerTitle>
|
</DrawerTitle>
|
||||||
<DrawerDescription>
|
<Material className="grid gap-1 max-h-[700px]">
|
||||||
Own your server to completely control your server and how it
|
<strong>Server Description</strong>
|
||||||
appears on MHSF.{" "}
|
<p className="mb-3">
|
||||||
<Link
|
A markdown enabled, fancy description for your server!
|
||||||
href="https://mhsf.mintlify.app/guides/owning-a-server"
|
Describe what players will expect from your server and why
|
||||||
className="text-black dark:text-white"
|
they should join; don't worry, you have more space than
|
||||||
>
|
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>
|
||||||
{requirementOne && (
|
{!serverData.loading && (
|
||||||
<>
|
<ServerEditorDescription
|
||||||
<p className="pb-2">
|
defaultMarkdown={
|
||||||
<code
|
serverData.server?.customizationData.description ?? ""
|
||||||
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>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</Material>
|
||||||
<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
|
||||||
{serverData.server?.customizationData.isOwnedByUser ? (
|
icon={<X />}
|
||||||
<div className="max-h-[400px] overflow-y-scroll">
|
className="h-full justify-center flex items-center"
|
||||||
<DrawerTitle className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl mb-3">
|
title="You don't own this server"
|
||||||
Server Settings
|
description="Unfortunately, that one ain't gonna work. Atleast not on my watch."
|
||||||
</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>
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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, Pencil, Share, Star } from "lucide-react";
|
import { EllipsisVertical, Flag, Heart, 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,10 +105,6 @@ 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} minehutData={server as ServerResponse}>
|
<ServerEditorProvider serverData={mhsf}>
|
||||||
<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, User } from "@clerk/nextjs/server";
|
import { clerkClient, getAuth } 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,12 +93,7 @@ export default async function handler(
|
|||||||
favoriteTimespanStart,
|
favoriteTimespanStart,
|
||||||
favoriteTimespanEnd,
|
favoriteTimespanEnd,
|
||||||
}),
|
}),
|
||||||
findCustomizationData(
|
findCustomizationData(serverData.name, userId ?? undefined, db),
|
||||||
serverData.name,
|
|
||||||
server as string,
|
|
||||||
userId ?? undefined,
|
|
||||||
db,
|
|
||||||
),
|
|
||||||
findPlayerData(serverData.name, db, {
|
findPlayerData(serverData.name, db, {
|
||||||
maxPlayerEntries,
|
maxPlayerEntries,
|
||||||
playerTimespanStart,
|
playerTimespanStart,
|
||||||
@ -141,7 +136,6 @@ 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<{
|
||||||
@ -156,41 +150,18 @@ 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({ $or: [{ serverId: serverId }, { server: serverName }] }),
|
db.collection("customization").findOne({ server: serverName }),
|
||||||
userId
|
userId
|
||||||
? db.collection("owned-servers").findOne({ $or: [{ serverId: serverId }, { server: serverName }] })
|
? db.collection("owned-servers").findOne({ server: serverName })
|
||||||
: null,
|
: null,
|
||||||
]);
|
]);
|
||||||
let user: User | undefined = undefined;
|
|
||||||
try {
|
|
||||||
user = await clerk.users.getUser(ownedServerData?.author);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e);
|
|
||||||
return {
|
|
||||||
isOwned: false,
|
|
||||||
isOwnedByUser: false,
|
|
||||||
description: undefined,
|
|
||||||
banner: undefined,
|
|
||||||
discord: undefined,
|
|
||||||
colorScheme: undefined,
|
|
||||||
userProfilePicture: undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
if (customizationData) {
|
||||||
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
|
userProfilePicture: userId ? (await clerk.users.getUser(ownedServerData?.author)).imageUrl : 'no user'
|
||||||
? 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,79 +45,90 @@ 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" });
|
||||||
}
|
}
|
||||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
if (
|
||||||
await client.connect();
|
(await (await clerkClient()).users.getUser(userId)).publicMetadata.player ===
|
||||||
|
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 users.findOne({ userId })) === null) {
|
if ((await collection.findOne({ server: server })) === undefined) {
|
||||||
return res.status(401).json({ error: "Account not linked" });
|
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 minecraftUsername = (await users.findOne({ userId }))?.player;
|
servers.forEach(async (c, i) => {
|
||||||
|
if (c.name === server) {
|
||||||
|
const MCUsername = (await (await clerkClient()).users.getUser(userId))
|
||||||
|
.publicMetadata.player;
|
||||||
|
|
||||||
if ((await collection.findOne({ server })) === null) {
|
if (MCUsername === c.author) {
|
||||||
const mh = await fetch(
|
await collection.insertOne({ server, author: userId });
|
||||||
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);
|
|
||||||
|
|
||||||
if (serverObj === undefined)
|
// Close the database, but don't close this
|
||||||
return res
|
// serverless instance until it happens
|
||||||
.status(400)
|
waitUntil(client.close());
|
||||||
.send({ message: "The server needs to be online." });
|
|
||||||
|
|
||||||
if (minecraftUsername === serverObj.author) {
|
res.send({ message: "Successfully owned server!" });
|
||||||
await collection.insertOne({ serverId: server, author: userId });
|
} else {
|
||||||
|
// Close the database, but don't close this
|
||||||
|
// serverless instance until it happens
|
||||||
|
waitUntil(client.close());
|
||||||
|
|
||||||
// Close the database, but don't close this
|
res
|
||||||
// serverless instance until it happens
|
.status(400)
|
||||||
waitUntil(client.close());
|
.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());
|
||||||
|
|
||||||
res.send({ message: "Successfully owned server!" });
|
res.status(400).send({ message: "The server needs to be online." });
|
||||||
} else {
|
}
|
||||||
res
|
});
|
||||||
.status(400)
|
} else {
|
||||||
.send({ message: "The linked account doesn't own the server." });
|
// Close the database, but don't close this
|
||||||
}
|
// serverless instance until it happens
|
||||||
} else {
|
waitUntil(client.close());
|
||||||
// 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." });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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