mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 21:24:58 -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
|
||||
.env*.local
|
||||
.env*.prod
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<?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">
|
||||
<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/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-05T04:09:03.471Z</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</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-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-05T04:09:03.471Z</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</loc><lastmod>2025-05-05T04:09:03.471Z</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/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-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-10T18:03:30.186Z</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-10T18:03:30.186Z</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/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-10T18:03:30.186Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url>
|
||||
</urlset>
|
||||
@ -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%;
|
||||
|
||||
@ -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<unknown>
|
||||
>) ?? []
|
||||
).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;
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
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 (
|
||||
<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,34 +1,225 @@
|
||||
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<typeof useMHSFServer>;
|
||||
minehutData: ServerResponse;
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [onlineData, setOnlineData] = useState<OnlineServer>();
|
||||
const { servers, loading } = useServers();
|
||||
const [claimedUser, setClaimedUser] = useState<string>();
|
||||
|
||||
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}
|
||||
<MilkdownProvider>
|
||||
<Drawer open={open} onOpenChange={setOpen}>
|
||||
<DrawerContent className="p-4 ">
|
||||
{serverData.server?.customizationData.isOwnedByUser ? (
|
||||
<br />
|
||||
{!serverData.server?.customizationData.isOwned ? (
|
||||
<div className="h-full overflow-y-scroll">
|
||||
<DrawerTitle className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl mb-3">
|
||||
Own your server.
|
||||
</DrawerTitle>
|
||||
<DrawerDescription>
|
||||
Own your server to completely control your server and how it
|
||||
appears on MHSF.{" "}
|
||||
<Link
|
||||
href="https://mhsf.mintlify.app/guides/owning-a-server"
|
||||
className="text-black dark:text-white"
|
||||
>
|
||||
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>
|
||||
{requirementOne && (
|
||||
<>
|
||||
<p className="pb-2">
|
||||
<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>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</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>
|
||||
) : (
|
||||
<>
|
||||
{serverData.server?.customizationData.isOwnedByUser ? (
|
||||
<div className="max-h-[400px] overflow-y-scroll">
|
||||
<DrawerTitle className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl mb-3">
|
||||
Server Settings
|
||||
</DrawerTitle>
|
||||
@ -36,15 +227,18 @@ export function ServerEditorProvider({
|
||||
<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.
|
||||
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 ?? ""
|
||||
serverData.server?.customizationData.description ??
|
||||
""
|
||||
}
|
||||
onUpdate={(content) => console.log(content)}
|
||||
|
||||
/>
|
||||
)}
|
||||
</Material>
|
||||
@ -57,6 +251,8 @@ export function ServerEditorProvider({
|
||||
description="Unfortunately, that one ain't gonna work. Atleast not on my watch."
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</MilkdownProvider>
|
||||
|
||||
@ -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 { 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({
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<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">
|
||||
<Share size={16} />
|
||||
Share
|
||||
|
||||
@ -72,7 +72,7 @@ export function ServerProvider({ serverId }: { serverId: string }) {
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-10">
|
||||
<ServerEditorProvider serverData={mhsf}>
|
||||
<ServerEditorProvider serverData={mhsf} minehutData={server as ServerResponse}>
|
||||
<ReportingProvider server={mhsf}>
|
||||
<ServerMainPage
|
||||
server={server as ServerResponse}
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
import { getBackendProcedure } from "@/lib/backend-procedure";
|
||||
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 type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
@ -83,7 +83,7 @@ export default async function handler(
|
||||
try {
|
||||
await mongo.connect();
|
||||
const db = mongo.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const {userId} = getAuth(req);
|
||||
const { userId } = getAuth(req);
|
||||
|
||||
// Run queries in parallel
|
||||
const [favoriteData, customizationData, playerData, achievements] =
|
||||
@ -93,7 +93,12 @@ export default async function handler(
|
||||
favoriteTimespanStart,
|
||||
favoriteTimespanEnd,
|
||||
}),
|
||||
findCustomizationData(serverData.name, userId ?? undefined, db),
|
||||
findCustomizationData(
|
||||
serverData.name,
|
||||
server as string,
|
||||
userId ?? undefined,
|
||||
db,
|
||||
),
|
||||
findPlayerData(serverData.name, db, {
|
||||
maxPlayerEntries,
|
||||
playerTimespanStart,
|
||||
@ -136,6 +141,7 @@ export default async function handler(
|
||||
|
||||
async function findCustomizationData(
|
||||
serverName: string,
|
||||
serverId: string,
|
||||
userId: string | undefined,
|
||||
db: any,
|
||||
): Promise<{
|
||||
@ -150,18 +156,40 @@ async function findCustomizationData(
|
||||
const clerk = await clerkClient();
|
||||
// Run queries in parallel
|
||||
const [customizationData, ownedServerData] = await Promise.all([
|
||||
db.collection("customization").findOne({ server: serverName }),
|
||||
db.collection("customization").findOne({ server: serverId }),
|
||||
userId
|
||||
? db.collection("owned-servers").findOne({ server: serverName })
|
||||
? db.collection("owned-servers").findOne({ server: serverId })
|
||||
: 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 {
|
||||
...(customizationData as any),
|
||||
isOwned: true,
|
||||
isOwnedByUser: ownedServerData?.author === userId,
|
||||
userProfilePicture: userId ? (await clerk.users.getUser(ownedServerData?.author)).imageUrl : 'no user'
|
||||
userProfilePicture: userId
|
||||
? user.imageUrl
|
||||
: "no user",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ import { getBackendProcedure } from "@/lib/backend-procedure";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
res: NextApiResponse,
|
||||
) {
|
||||
const backendProcedure = await getBackendProcedure(req);
|
||||
|
||||
@ -56,19 +56,20 @@ export default async function handler(
|
||||
if (!userId) {
|
||||
return res.status(401).json({ error: "Unauthorized" });
|
||||
}
|
||||
if (
|
||||
(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 collection = db.collection("owned-servers");
|
||||
const users = db.collection("claimed-users");
|
||||
|
||||
if ((await collection.findOne({ server: server })) === undefined) {
|
||||
if ((await users.findOne({ userId })) === null) {
|
||||
return res.status(401).json({ error: "Account not linked" });
|
||||
}
|
||||
|
||||
const minecraftUsername = (await users.findOne({ userId }))?.player;
|
||||
|
||||
if ((await collection.findOne({ server })) === null) {
|
||||
const mh = await fetch(
|
||||
process.env.MHSF_BACKEND_API_LOCATION ??
|
||||
"https://api.minehut.com/servers",
|
||||
@ -89,16 +90,17 @@ export default async function handler(
|
||||
},
|
||||
body: null,
|
||||
method: "GET",
|
||||
}
|
||||
},
|
||||
);
|
||||
const servers: Array<OnlineServer> = (await mh.json()).servers;
|
||||
const serverObj = servers.find((c) => c.staticInfo._id === server);
|
||||
|
||||
servers.forEach(async (c, i) => {
|
||||
if (c.name === server) {
|
||||
const MCUsername = (await (await clerkClient()).users.getUser(userId))
|
||||
.publicMetadata.player;
|
||||
if (serverObj === undefined)
|
||||
return res
|
||||
.status(400)
|
||||
.send({ message: "The server needs to be online." });
|
||||
|
||||
if (MCUsername === c.author) {
|
||||
if (minecraftUsername === serverObj.author) {
|
||||
await collection.insertOne({ server, author: userId });
|
||||
|
||||
// Close the database, but don't close this
|
||||
@ -107,23 +109,10 @@ export default async function handler(
|
||||
|
||||
res.send({ message: "Successfully owned server!" });
|
||||
} else {
|
||||
// Close the database, but don't close this
|
||||
// serverless instance until it happens
|
||||
waitUntil(client.close());
|
||||
|
||||
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());
|
||||
|
||||
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
|
||||
|
||||
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