mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 16:54:59 -05:00
feat: custom filters
This commit is contained in:
parent
bba5504f5d
commit
7385705c8d
@ -49,6 +49,9 @@ const nextConfig = {
|
||||
},
|
||||
]
|
||||
},
|
||||
webpack: (config) => {
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
export default withContentlayer(nextConfig);
|
||||
|
||||
@ -55,6 +55,7 @@
|
||||
"mini-svg-data-uri": "^1.4.4",
|
||||
"minimessage-2-html": "1.6.0",
|
||||
"minimessage-js": "^1.1.3",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"mongodb": "^6.8.0",
|
||||
"next": "15.2.0",
|
||||
"next-contentlayer": "^0.3.4",
|
||||
|
||||
95
apps/www/src/app/(sl-modification-frame)/layout.tsx
Normal file
95
apps/www/src/app/(sl-modification-frame)/layout.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
import "../globals.css";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { Placeholder } from "@/components/ui/placeholder";
|
||||
import { X } from "lucide-react";
|
||||
import { IsScript } from "@/components/util/is-script";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import { NavBar } from "@/components/feat/navbar/navbar";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { ThemeProvider } from "@/components/util/theme-provider";
|
||||
import { FontBoundary } from "@/components/util/font-boundary";
|
||||
import { ClerkProvider } from "@/components/util/clerk-provider";
|
||||
import { Toaster } from "sonner";
|
||||
import { Footer } from "@/components/feat/footer/footer";
|
||||
import { NuqsAdapter } from "nuqs/adapters/next/app";
|
||||
import { IframeProtector } from "@/components/util/iframe-protector";
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const searchParams = useSearchParams();
|
||||
const search = searchParams?.get("theme") || "light";
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
<noscript>
|
||||
<main className="flex justify-center items-center text-center min-h-screen h-max">
|
||||
<Placeholder
|
||||
icon={<X />}
|
||||
title="JavaScript is required for MHSF"
|
||||
description="MHSF cannot grab servers or do other external requests without JavaScript."
|
||||
>
|
||||
<Link href="https://www.enable-javascript.com/">
|
||||
<Button>Here's how</Button>
|
||||
</Link>
|
||||
</Placeholder>
|
||||
</main>
|
||||
</noscript>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<ClerkProvider>
|
||||
<IsScript>
|
||||
<NuqsAdapter>
|
||||
<FontBoundary className="max-w-[800px]">
|
||||
<IframeProtector>
|
||||
<TooltipProvider>
|
||||
<Toaster richColors position="top-center" />
|
||||
<div className="overflow-x-hidden">{children}</div>
|
||||
</TooltipProvider>
|
||||
</IframeProtector>
|
||||
</FontBoundary>
|
||||
</NuqsAdapter>
|
||||
</IsScript>
|
||||
</ClerkProvider>
|
||||
</ThemeProvider>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
79
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/modification/[mod]/page.tsx
Normal file
79
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/modification/[mod]/page.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { ModificationAction } from "@/components/feat/server-list/modification/modification-action";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { serverModDB } from "@/config/sl-mod-db";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { useQueryState } from "nuqs";
|
||||
import { use } from "react";
|
||||
import Markdown from "react-markdown";
|
||||
|
||||
export default function ModificationPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ category: string; mod: string }>;
|
||||
}) {
|
||||
const { category, mod } = use(params);
|
||||
const [backRoute] = useQueryState("b", {
|
||||
defaultValue: "/servers/embedded/sl-modification-frame",
|
||||
});
|
||||
console.log(mod);
|
||||
const categoryObj = serverModDB.find(
|
||||
(c) => c.displayTitle === atob(decodeURIComponent(category))
|
||||
);
|
||||
let modObj = null;
|
||||
if (categoryObj !== undefined)
|
||||
modObj = categoryObj?.entries.find(
|
||||
(c) => c.name === atob(decodeURIComponent(mod))
|
||||
);
|
||||
|
||||
return (
|
||||
<main className="max-w-[800px] p-4">
|
||||
<div
|
||||
className="h-[150px] w-full rounded-xl p-2"
|
||||
style={{ backgroundColor: modObj?.color }}
|
||||
>
|
||||
<Link href={backRoute}>
|
||||
<ArrowLeft />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<span className="p-4">
|
||||
<h1 className="text-xl font-bold w-full">{modObj?.name}</h1>
|
||||
<Markdown className="text-wrap pt-2">{modObj?.description}</Markdown>
|
||||
<ModificationAction value={modObj?.value} />
|
||||
</span>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
91
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/page.tsx
Normal file
91
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/category/[category]/page.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { ModificationAction } from "@/components/feat/server-list/modification/modification-action";
|
||||
import { Material } from "@/components/ui/material";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { serverModDB } from "@/config/sl-mod-db";
|
||||
import { useRouter } from "@/lib/useRouter";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { use } from "react";
|
||||
import Markdown from "react-markdown";
|
||||
|
||||
export default function ServerListCategoryFrame({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ category: string }>;
|
||||
}) {
|
||||
const { category } = use(params);
|
||||
const categoryObj = serverModDB.find(
|
||||
(c) => c.displayTitle === atob(category)
|
||||
);
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<main className="max-w-[800px] p-4">
|
||||
<h1 className="text-xl font-bold w-full">
|
||||
<Link href="/servers/embedded/sl-modification-frame">
|
||||
<ArrowLeft />
|
||||
</Link>
|
||||
{categoryObj?.displayTitle}
|
||||
</h1>
|
||||
<Markdown className="text-wrap pt-2">{categoryObj?.description}</Markdown>
|
||||
|
||||
<div className="pt-10 p-4 grid grid-cols-6 gap-2">
|
||||
{categoryObj?.entries.map((m) => (
|
||||
<Material
|
||||
className="p-2 hover:drop-shadow-card-hover cursor-pointer"
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/servers/embedded/sl-modification-frame/category/${category}/modification/${btoa(m.name)}?b=${encodeURIComponent(`/servers/embedded/sl-modification-frame/category/${category}`)}`
|
||||
)
|
||||
}
|
||||
key={m.name}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"w-full h-[40px] mb-2 rounded-lg items-center text-center justify-center"
|
||||
)}
|
||||
style={{ backgroundColor: m.color }}
|
||||
>
|
||||
<m.icon className="relative top-[calc(50%-12px)] items-center w-full text-center justify-center" />
|
||||
</div>
|
||||
<span className="text-sm text-center w-full flex items-center justify-center">
|
||||
{m.name}
|
||||
</span>
|
||||
</Material>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
150
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/file/[filename]/page.tsx
Normal file
150
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/file/[filename]/page.tsx
Normal file
@ -0,0 +1,150 @@
|
||||
"use client";
|
||||
|
||||
import { use } from "react";
|
||||
import { useUser } from "@clerk/nextjs";
|
||||
import type { ClerkCustomModification } from "@/components/feat/server-list/modification/modification-file-creation-dialog";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import Editor from "@monaco-editor/react";
|
||||
import poimandres from "@/theme.json";
|
||||
|
||||
const typeDefs = `
|
||||
export interface Server {
|
||||
staticInfo: {
|
||||
_id: string;
|
||||
serverPlan: string;
|
||||
serviceStartDate: number;
|
||||
platform: string;
|
||||
planMaxPlayers: number;
|
||||
planRam: number;
|
||||
alwaysOnline: boolean;
|
||||
rawPlan: string;
|
||||
connectedServers: any[];
|
||||
};
|
||||
maxPlayers: number;
|
||||
name: string;
|
||||
motd: string;
|
||||
icon: string;
|
||||
playerData: {
|
||||
playerCount: number;
|
||||
timeNoPlayers: number;
|
||||
};
|
||||
connectable: boolean;
|
||||
visibility: boolean;
|
||||
allCategories: string[];
|
||||
usingCosmetics: boolean;
|
||||
author?: string;
|
||||
authorRank: string;
|
||||
}
|
||||
`;
|
||||
|
||||
export default function CustomFilePage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ filename: string }>;
|
||||
}) {
|
||||
const { filename } = use(params);
|
||||
const { user } = useUser();
|
||||
const file = (
|
||||
(user?.unsafeMetadata.customFiles as Array<ClerkCustomModification>) ?? []
|
||||
).find((c) => c.name === filename);
|
||||
|
||||
if (!file) {
|
||||
return <>Bruh.</>;
|
||||
}
|
||||
|
||||
const fileContents = file.contents;
|
||||
|
||||
return (
|
||||
<main className="max-w-[800px] p-4">
|
||||
<strong className="font-bold w-full">
|
||||
<Link href="/servers/embedded/sl-modification-frame/files">
|
||||
<ArrowLeft />
|
||||
</Link>
|
||||
{filename}.ts
|
||||
</strong>
|
||||
<div className="h-[400px]">
|
||||
<Editor
|
||||
height="100%"
|
||||
defaultLanguage="typescript"
|
||||
defaultValue={fileContents}
|
||||
theme="vs-dark"
|
||||
onMount={(editor, monaco) => {
|
||||
// Ensure TypeScript is properly configured
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
target: monaco.languages.typescript.ScriptTarget.Latest,
|
||||
allowNonTsExtensions: true, // This is important!
|
||||
moduleResolution:
|
||||
monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
||||
module: monaco.languages.typescript.ModuleKind.CommonJS,
|
||||
noEmit: true,
|
||||
esModuleInterop: true,
|
||||
jsx: monaco.languages.typescript.JsxEmit.React,
|
||||
reactNamespace: "React",
|
||||
allowJs: true,
|
||||
typeRoots: ["node_modules/@types"],
|
||||
});
|
||||
|
||||
// Create a virtual TS file for the types
|
||||
const libUri = "file:///node_modules/@types/mhsf/index.d.ts";
|
||||
|
||||
// Add typedefs as a library
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
typeDefs,
|
||||
libUri,
|
||||
);
|
||||
|
||||
// Create a model for the libUri file
|
||||
if (!monaco.editor.getModel(monaco.Uri.parse(libUri))) {
|
||||
monaco.editor.createModel(
|
||||
typeDefs,
|
||||
"typescript",
|
||||
monaco.Uri.parse(libUri),
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure the current file is using the correct language
|
||||
const currentModel = editor.getModel();
|
||||
if (currentModel) {
|
||||
monaco.editor.setModelLanguage(currentModel, "typescript");
|
||||
}
|
||||
|
||||
const currentUri = monaco.Uri.parse(`file:///${filename}.ts`);
|
||||
if (!monaco.editor.getModel(currentUri)) {
|
||||
monaco.editor.createModel(
|
||||
fileContents,
|
||||
"typescript",
|
||||
currentUri,
|
||||
);
|
||||
editor.setModel(monaco.editor.getModel(currentUri));
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
fontSize: 14,
|
||||
lineNumbers: "on",
|
||||
roundedSelection: false,
|
||||
scrollbar: {
|
||||
vertical: "visible",
|
||||
horizontal: "visible",
|
||||
},
|
||||
quickSuggestions: true,
|
||||
suggestOnTriggerCharacters: true,
|
||||
acceptSuggestionOnEnter: "on",
|
||||
tabCompletion: "on",
|
||||
wordBasedSuggestions: "currentDocument",
|
||||
parameterHints: {
|
||||
enabled: true,
|
||||
},
|
||||
hover: {
|
||||
enabled: true,
|
||||
delay: 300,
|
||||
sticky: true,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
54
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/files/page.tsx
Normal file
54
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/files/page.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { ClerkCustomModification } from "@/components/feat/server-list/modification/modification-file-creation-dialog";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { useUser } from "@clerk/nextjs";
|
||||
import { File, FileCode } from "lucide-react";
|
||||
|
||||
export default function ServerListModificationFrame() {
|
||||
const { user } = useUser();
|
||||
const files =
|
||||
(user?.unsafeMetadata.customFiles as Array<ClerkCustomModification>) ?? [];
|
||||
return (
|
||||
<main className="max-w-[800px] p-4">
|
||||
<h1 className="text-xl font-bold w-full">Files</h1>
|
||||
<div className="grid gap-1">
|
||||
{files.map((c) => (
|
||||
<Link href={`/servers/embedded/sl-modification-frame/file/${c.name}`} className="w-full py-1 px-2 rounded-xl flex items-center gap-1 hover:bg-slate-100" key={c.name}>
|
||||
<FileCode size={16}/>{c.name}.ts
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
101
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/page.tsx
Normal file
101
apps/www/src/app/(sl-modification-frame)/servers/embedded/sl-modification-frame/page.tsx
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { Material } from "@/components/ui/material";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { serverModDB } from "@/config/sl-mod-db";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useRouter } from "@/lib/useRouter";
|
||||
|
||||
export default function ServerListModificationFrame() {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<main className="max-w-[800px] p-4">
|
||||
<h1 className="text-xl font-bold w-full">Filters & Sorting</h1>
|
||||
<div className="flex items-center gap-2 my-2">
|
||||
<Button size="sm">Active modifications</Button>
|
||||
<Link href="/servers/embedded/sl-modification-frame/files">
|
||||
<Button size="sm">Custom files</Button>
|
||||
</Link>
|
||||
<Button size="sm">Settings</Button>
|
||||
</div>
|
||||
<span className="text-wrap pt-2">
|
||||
Pick out different filters & sorting systems to customize your server
|
||||
viewing experience. We frequently add new filters in accordance to new
|
||||
features, as well.
|
||||
</span>
|
||||
<Separator className="mt-4" />
|
||||
<div className="pt-10 p-4">
|
||||
{serverModDB.map((c) => (
|
||||
<span key={c.displayTitle}>
|
||||
<h2 className="text-lg font-bold pb-3 flex justify-between">
|
||||
{c.displayTitle}
|
||||
<Link
|
||||
href={`/servers/embedded/sl-modification-frame/category/${btoa(c.displayTitle)}`}
|
||||
className="flex gap-2 text-sm font-normal items-center"
|
||||
>
|
||||
<ArrowRight size={16} />
|
||||
View more
|
||||
</Link>
|
||||
</h2>
|
||||
<div className="grid grid-cols-6 gap-2">
|
||||
{c.entries.map((m) => (
|
||||
<Material
|
||||
className="p-2 hover:drop-shadow-card-hover cursor-pointer"
|
||||
key={m.name}
|
||||
onClick={() =>
|
||||
router.push(
|
||||
`/servers/embedded/sl-modification-frame/category/${btoa(c.displayTitle)}/modification/${btoa(m.name)}`
|
||||
)
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="w-full h-[40px] mb-2 rounded-lg items-center text-center justify-center"
|
||||
style={{ backgroundColor: m.color }}
|
||||
>
|
||||
<m.icon className="relative top-[calc(50%-12px)] items-center w-full text-center justify-center" />
|
||||
</div>
|
||||
<span className="text-sm text-center w-full flex items-center justify-center">
|
||||
{m.name}
|
||||
</span>
|
||||
</Material>
|
||||
))}
|
||||
</div>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { Filter } from "@/lib/types/filter";
|
||||
import type { Sort } from "@/lib/types/sort";
|
||||
import { ModificationFileCreationDialog } from "./modification-file-creation-dialog";
|
||||
|
||||
type Action = Filter | Sort | { customAction: string };
|
||||
|
||||
export function ModificationAction({ value }: { value?: Action }) {
|
||||
return (
|
||||
<>
|
||||
{value !== undefined && "customAction" in value ? (
|
||||
<ModificationFileCreationDialog
|
||||
type={value.customAction.endsWith("sort") ? "sort" : "filter"}
|
||||
>
|
||||
<Button size="sm" className="mt-1">
|
||||
{value.customAction === "custom-sort"
|
||||
? "Create Sort"
|
||||
: "Create Filter"}
|
||||
</Button>
|
||||
</ModificationFileCreationDialog>
|
||||
) : (
|
||||
<Button size="sm">Apply</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||
import { ModificationFrame } from "./modification-frame";
|
||||
|
||||
export function ModificationButton() {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>
|
||||
<Button>Filters & Sorting</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent className="p-0 h-[600px] w-[1000px] !max-w-[800px] overflow-x-hidden">
|
||||
<ModificationFrame />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
126
apps/www/src/components/feat/server-list/modification/modification-file-creation-dialog.tsx
Normal file
126
apps/www/src/components/feat/server-list/modification/modification-file-creation-dialog.tsx
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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 { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useRouter } from "@/lib/useRouter";
|
||||
import { useUser } from "@clerk/nextjs";
|
||||
import { useState, type ReactNode } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const sortTemplate = `import type { Server } from "mhsf";
|
||||
|
||||
export function sort(serverA: Server, serverB: Server): number {
|
||||
// Your code here
|
||||
// Use logic like \`Array.sort\` or <V>(a: V, b: V) => number
|
||||
}`;
|
||||
|
||||
const filterTemplate = `import type { Server } from "mhsf";
|
||||
|
||||
export function filter(server: Server): boolean {
|
||||
// Your code here
|
||||
// Returning true indicates the server will stay, while returning false will remove the server from the queue.
|
||||
}`;
|
||||
|
||||
export type ClerkCustomModification = {
|
||||
name: string; // Add .ts to the end
|
||||
active: boolean;
|
||||
contents: string;
|
||||
};
|
||||
|
||||
export function ModificationFileCreationDialog({
|
||||
children,
|
||||
type,
|
||||
}: {
|
||||
children?: ReactNode;
|
||||
type: "filter" | "sort";
|
||||
}) {
|
||||
const { user, isSignedIn } = useUser();
|
||||
const router = useRouter();
|
||||
const [fileName, setFileName] = useState("");
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger>{children}</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogTitle>Create new file</DialogTitle>
|
||||
<DialogDescription>
|
||||
Files can be a new filter or sort, made w/ TypeScript.
|
||||
</DialogDescription>
|
||||
<div className="flex items-center w-full">
|
||||
<Input
|
||||
className="rounded-r-none w-full"
|
||||
placeholder="you-should-use-this-format-for-typescript-files-please"
|
||||
onChange={(e) => setFileName(e.target.value)}
|
||||
value={fileName}
|
||||
/>
|
||||
<span className="px-4 text-sm py-2 border border-l-none rounded-r-md">
|
||||
.ts
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<DialogTrigger>
|
||||
<Button
|
||||
className="w-full"
|
||||
onClick={(e) => {
|
||||
if (!isSignedIn) return toast.error("Please login.");
|
||||
user?.update({
|
||||
unsafeMetadata: {
|
||||
customFiles: [
|
||||
...((user.unsafeMetadata
|
||||
.customFiles as Array<ClerkCustomModification>) ?? []),
|
||||
{
|
||||
name: fileName,
|
||||
active: false,
|
||||
contents:
|
||||
type === "filter" ? filterTemplate : sortTemplate,
|
||||
},
|
||||
] satisfies Array<ClerkCustomModification>,
|
||||
},
|
||||
});
|
||||
toast.success("Created file!")
|
||||
router.push(`/servers/embedded/sl-modification-frame/file/${fileName}`)
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { useIframeCommunication } from "@/lib/hooks/use-iframe-communication"
|
||||
import { useEffectOnce } from "@/lib/useEffectOnce"
|
||||
import { useEffect, useRef } from "react"
|
||||
|
||||
export function ModificationFrame() {
|
||||
const ref = useRef<HTMLIFrameElement>(null)
|
||||
const communication = useIframeCommunication(ref);
|
||||
|
||||
useEffect(() => {
|
||||
communication.toIframe.handle("ping", (c) => {
|
||||
if (c.from === "iframe")
|
||||
communication.toIframe.send("ping", {from: "top-layer"})
|
||||
})
|
||||
}, [ref])
|
||||
|
||||
return <iframe ref={ref} src="/servers/embedded/sl-modification-frame" height={800} width={800} title="Server-list Modification Frame" />
|
||||
}
|
||||
@ -62,11 +62,11 @@ export default function ServerCard({ server }: { server: OnlineServer }) {
|
||||
return (
|
||||
<Material
|
||||
className="min-h-[250px] max-h-[250px] cursor-pointer outline-0 group hover:drop-shadow-card-hover focus:drop-shadow-card-hover transition-all"
|
||||
onClick={() => router.push(`/server/${server.staticInfo._id}`)}
|
||||
onClick={() => router.push(`/server/v2/minehut/${server.staticInfo._id}`)}
|
||||
tabIndex={0}
|
||||
onKeyDown={(e) => {
|
||||
// Only send user when they hit "Enter"
|
||||
if (e.key === "Enter") router.push(`/server/${server.staticInfo._id}`);
|
||||
if (e.key === "Enter") router.push(`/server/v2/minehut/${server.staticInfo._id}`);
|
||||
}}
|
||||
>
|
||||
<span className="text-sm hidden group-focus-visible:block text-muted-foreground mb-2">
|
||||
|
||||
@ -37,15 +37,12 @@ import { Statistics } from "./statistics";
|
||||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import { useInfiniteScrolling } from "@/lib/hooks/use-infinite-scrolling";
|
||||
import { useMHSFServer } from "@/lib/hooks/use-mhsf-multiple";
|
||||
import { ModificationButton } from "./modification/modification-button";
|
||||
|
||||
export function ServerList() {
|
||||
const { servers, loading, serverCount, playerCount } = useServers();
|
||||
const { itemsLength, fetchMoreData, hasMoreData, data } =
|
||||
useInfiniteScrolling(servers);
|
||||
const mhsfServers = useMHSFServer(
|
||||
servers.map((s) => s.staticInfo._id),
|
||||
true
|
||||
);
|
||||
|
||||
if (loading)
|
||||
return (
|
||||
@ -68,6 +65,7 @@ export function ServerList() {
|
||||
<h1 className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl">
|
||||
Servers
|
||||
</h1>
|
||||
<ModificationButton />
|
||||
<InfiniteScroll
|
||||
dataLength={itemsLength}
|
||||
next={fetchMoreData}
|
||||
|
||||
@ -4,6 +4,11 @@ import { TextArea } from "@/components/ui/text-area";
|
||||
import { Link } from "@/components/util/link";
|
||||
import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server";
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { SignedIn, SignedOut, useUser } from "@clerk/nextjs";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export function ReportingDialog({
|
||||
server,
|
||||
@ -14,7 +19,9 @@ export function ReportingDialog({
|
||||
open: boolean;
|
||||
setOpen: (newState: boolean) => void;
|
||||
}) {
|
||||
const { user, isSignedIn } = useUser();
|
||||
const [reason, setReason] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
return (
|
||||
<Drawer direction="left" open={open} onOpenChange={setOpen}>
|
||||
@ -52,9 +59,50 @@ export function ReportingDialog({
|
||||
</ul>
|
||||
</Alert>
|
||||
<br />
|
||||
<TextArea label="Reason for reporting" />
|
||||
<TextArea
|
||||
label="Reason for reporting"
|
||||
value={reason}
|
||||
onChange={(e) => setReason(e.target.value)}
|
||||
/>
|
||||
<br />
|
||||
<span></span>
|
||||
<SignedIn>
|
||||
<span className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Image
|
||||
alt="Clerk Image"
|
||||
src={
|
||||
user?.imageUrl === undefined
|
||||
? "https://img.clerk.com/preview.png?size=144&seed=seed&initials=AD&isSquare=true&bgType=marble&bgColor=6c47ff&fgType=silhouette&fgColor=FFFFFF&type=user&w=48&q=75"
|
||||
: user?.imageUrl
|
||||
}
|
||||
width={16}
|
||||
height={16}
|
||||
className="rounded-full"
|
||||
/>
|
||||
Signed in as @{user?.username}
|
||||
</span>
|
||||
</SignedIn>
|
||||
<SignedOut>
|
||||
<span className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
You must be signed in to perform this action.
|
||||
</span>
|
||||
</SignedOut>
|
||||
<br />
|
||||
<Button
|
||||
disabled={!isSignedIn || loading}
|
||||
className="flex items-center gap-2"
|
||||
onClick={async () => {
|
||||
if (reason === "" || reason === " ") {
|
||||
toast.error("The reason cannot be empty.");
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
await server.reportServer(reason);
|
||||
setLoading(false);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
Report Server {loading && <Spinner />}
|
||||
</Button>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
|
||||
@ -40,6 +40,7 @@ import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
@ -103,6 +104,9 @@ export function ServerPageButtons({
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuSeparator>
|
||||
Destructive
|
||||
</DropdownMenuSeparator>
|
||||
<DropdownMenuItem
|
||||
className="text-red-400 flex items-center gap-2"
|
||||
onClick={() => {
|
||||
|
||||
@ -66,6 +66,7 @@ const TextArea: React.FC<TextAreaProps> = ({
|
||||
rows = 4,
|
||||
id = generateID(),
|
||||
className = "",
|
||||
onChange,
|
||||
...restProps
|
||||
}) => {
|
||||
const elementRef = useRef<HTMLTextAreaElement | null>(null);
|
||||
@ -93,7 +94,7 @@ const TextArea: React.FC<TextAreaProps> = ({
|
||||
disabled={disabled}
|
||||
rows={rows}
|
||||
value={value}
|
||||
onChange={(e) => restProps.onChange?.(e)}
|
||||
onChange={onChange}
|
||||
ref={elementRef}
|
||||
className={`${sizeClass[size]} ${borderClass} focus:border-slate-800 dark:focus:border-zinc-200 bg-white dark:bg-zinc-950 focus:outline-hidden focus:ring-2 ring-slate-800/50 rounded-xl dark:ring-zinc-200/50 transition-all text-sm w-full disabled:bg-slate-100 disabled:cursor-not-allowed invalid:border-red-500! peer invalid:text-red-500 z-10 ${className}`}
|
||||
/>
|
||||
|
||||
@ -44,8 +44,10 @@ const overflowXHiddenPages = ["/home"];
|
||||
|
||||
export function FontBoundary({
|
||||
children,
|
||||
className
|
||||
}: {
|
||||
children?: ReactNode | ReactNode[];
|
||||
className?: string;
|
||||
}) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const [fontFamily, setFontFamily] = useState("inter");
|
||||
@ -71,7 +73,7 @@ export function FontBoundary({
|
||||
default:
|
||||
return "system-ui-font--font-boundary";
|
||||
}
|
||||
})()} ${pathname !== null && overflowXHiddenPages.includes(pathname) ? "overflow-x-hidden" : ""}`}
|
||||
})()} ${pathname !== null && overflowXHiddenPages.includes(pathname) ? "overflow-x-hidden" : ""} ${className}`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
|
||||
@ -28,41 +28,32 @@
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { LinearClient, LinearFetch, User } from "@linear/sdk";
|
||||
"use client";
|
||||
|
||||
export async function createReportIssue(
|
||||
server: string,
|
||||
reportDescription: string,
|
||||
userId: string,
|
||||
) {
|
||||
const linearClient = new LinearClient({
|
||||
apiKey: process.env.LINEAR,
|
||||
import { useIframeCommunication } from "@/lib/hooks/use-iframe-communication";
|
||||
import { useEffectOnce } from "@/lib/useEffectOnce";
|
||||
import { type ReactNode, useState } from "react";
|
||||
import { Spinner } from "../ui/spinner";
|
||||
|
||||
export function IframeProtector({ children }: { children: ReactNode }) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const iframeCommunication = useIframeCommunication();
|
||||
|
||||
useEffectOnce(() => {
|
||||
// Make sure top layer frames are actually coming from MHSF
|
||||
iframeCommunication.fromIframe.send("ping", { from: "iframe" });
|
||||
iframeCommunication.fromIframe.handle("ping", (obj) => {
|
||||
if (obj.from === "top-layer") setLoading(false);
|
||||
});
|
||||
});
|
||||
|
||||
const allTeams = await linearClient.teams();
|
||||
// Always grabs the first issue category.
|
||||
const team = allTeams.nodes[0];
|
||||
|
||||
// Ensure there *actually* is a team there
|
||||
if (team.id) {
|
||||
await linearClient.createIssue({
|
||||
teamId: team.id,
|
||||
title: `Issue against server \`${server}\``,
|
||||
description: desc(userId, server, reportDescription),
|
||||
assigneeId: (await team.members()).nodes[0].id,
|
||||
});
|
||||
}
|
||||
if (loading)
|
||||
return (
|
||||
<div className="max-w-[800px]">
|
||||
<div className="absolute top-[50%] left-[50%]">
|
||||
<Spinner />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return children;
|
||||
}
|
||||
|
||||
const desc = (user: string, server: string, reason: string) => `There was a report against the server, submitted by a user.
|
||||
Every issue must be [considered with care](https://list.mlnehut.com/docs/legal/external-content-agreement) before evaluating its outcome.
|
||||
|
||||
|
||||
**User**: \`${user}\`
|
||||
**Server**: \`${server}\`
|
||||
**For reason**:
|
||||
${reason}
|
||||
|
||||
|
||||
*This was an automatically added issue by the report bot. Add the canceled status to remove the issue from the active issues, along with the labels Not Controllable & Spam for their respective values.*
|
||||
`;
|
||||
76
apps/www/src/config/sl-mod-db.ts
Normal file
76
apps/www/src/config/sl-mod-db.ts
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 {
|
||||
ArrowDownUpIcon,
|
||||
SlidersHorizontal,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
import type { Filter } from "../lib/types/filter";
|
||||
import type { Sort } from "../lib/types/sort";
|
||||
|
||||
type ModDBCategory = {
|
||||
displayTitle: string;
|
||||
description: string;
|
||||
__custom?: boolean;
|
||||
entries: {
|
||||
name: string;
|
||||
icon: LucideIcon;
|
||||
color: string;
|
||||
value: Filter | Sort | { customAction: string };
|
||||
description: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export const serverModDB: ModDBCategory[] = [
|
||||
{
|
||||
displayTitle: "Custom Files",
|
||||
__custom: true,
|
||||
description:
|
||||
`Create custom TypeScript-based filter or sorting systems, completely from the comfort of your own browser.
|
||||
Types used are *builtin* and you can see live type definitions and IntelliSense in the editor.`,
|
||||
entries: [
|
||||
{
|
||||
name: "Create Sort",
|
||||
icon: ArrowDownUpIcon,
|
||||
value: { customAction: "custom-sort" },
|
||||
color: "#a3a68b",
|
||||
description: "Create a new custom sort system using TypeScript, completely from the comfort of your own browser."
|
||||
},
|
||||
{
|
||||
name: "Create Filter",
|
||||
icon: SlidersHorizontal,
|
||||
value: { customAction: "custom-filter" },
|
||||
color: "#a3a68b",
|
||||
description: "Create a new custom filtering system using TypeScript, completely from the comfort of your own browser."
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
98
apps/www/src/lib/discord.ts
Normal file
98
apps/www/src/lib/discord.ts
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 { clerkClient } from "@clerk/nextjs/server";
|
||||
import { MongoClient } from "mongodb";
|
||||
|
||||
const reportObj = (
|
||||
serverName: string,
|
||||
userId: string,
|
||||
userAvatar: string,
|
||||
username: string,
|
||||
reason: string,
|
||||
serverHasCustomizationData: boolean
|
||||
) => {
|
||||
return {
|
||||
content: "<@&1283912654536314961>",
|
||||
embeds: [
|
||||
{
|
||||
title: `Report on server \`${serverName}\``,
|
||||
description: `There was a report on server \`${serverName}\` by user \`${userId}\`.`,
|
||||
color: 16759796,
|
||||
fields: [
|
||||
{
|
||||
name: "Reason",
|
||||
value: reason,
|
||||
},
|
||||
{
|
||||
name: "Server has customization data?",
|
||||
value: serverHasCustomizationData ? "Yes" : "No",
|
||||
},
|
||||
],
|
||||
author: {
|
||||
name: username,
|
||||
url: `${process.env.CLERK_USER_PREFIX}/${userId}`,
|
||||
icon_url: userAvatar,
|
||||
},
|
||||
},
|
||||
],
|
||||
attachments: [],
|
||||
};
|
||||
};
|
||||
|
||||
export async function sendDiscordReport(
|
||||
serverName: string,
|
||||
userId: string,
|
||||
reason: string
|
||||
) {
|
||||
const client = await clerkClient();
|
||||
const user = await client.users.getUser(userId);
|
||||
|
||||
const mongo = new MongoClient(process.env.MONGO_DB as string);
|
||||
await mongo.connect();
|
||||
|
||||
const collection = mongo.db("mhsf").collection("customization");
|
||||
const server = await collection.findOne({ server: serverName });
|
||||
|
||||
await fetch(process.env.DISCORD_WEBHOOK as string, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(
|
||||
reportObj(
|
||||
serverName,
|
||||
userId,
|
||||
user.imageUrl,
|
||||
user.username ?? "",
|
||||
reason,
|
||||
server !== null
|
||||
)
|
||||
),
|
||||
});
|
||||
}
|
||||
64
apps/www/src/lib/hooks/use-iframe-communication.tsx
Normal file
64
apps/www/src/lib/hooks/use-iframe-communication.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 { RefObject } from "react";
|
||||
|
||||
export function useIframeCommunication(bottomIframe?: RefObject<HTMLIFrameElement | null>) {
|
||||
return {
|
||||
toIframe: {
|
||||
send: (key: string, object: any) => {
|
||||
if (!bottomIframe) {
|
||||
throw new Error("No hook Iframe")
|
||||
}
|
||||
bottomIframe.current?.contentWindow?.postMessage({__key: key, ...object}, '*');
|
||||
},
|
||||
handle: (key: string, callback: (object: any) => void) => {
|
||||
window.addEventListener('message', (e) => {
|
||||
if (e.data.__key === key) {
|
||||
callback(e.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
fromIframe: {
|
||||
send: (key: string, object: any) => {
|
||||
window.top?.postMessage({__key: key, ...object}, '*')
|
||||
},
|
||||
handle: (key: string, callback: (object: any) => void) => {
|
||||
window.addEventListener('message', (e) => {
|
||||
console.log(e);
|
||||
if (e.data.__key === key) {
|
||||
callback(e.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -109,6 +109,7 @@ export function useMHSFServer(id: string) {
|
||||
|
||||
const response = await fetch(server.actions.report, {
|
||||
body: JSON.stringify({ reason }),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
|
||||
37
apps/www/src/lib/types/filter.ts
Normal file
37
apps/www/src/lib/types/filter.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 { OnlineServer } from "./mh-server";
|
||||
|
||||
export interface Filter {
|
||||
toIdentifier(): string;
|
||||
fromIdentifier(identifier: string): Filter;
|
||||
applyToServer(server: OnlineServer): boolean;
|
||||
}
|
||||
27
apps/www/src/lib/types/mh-server.d.ts
vendored
Normal file
27
apps/www/src/lib/types/mh-server.d.ts
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
declare type Server = {
|
||||
staticInfo: {
|
||||
_id: string;
|
||||
serverPlan: string;
|
||||
serviceStartDate: number;
|
||||
platform: string;
|
||||
planMaxPlayers: number;
|
||||
planRam: number;
|
||||
alwaysOnline: boolean;
|
||||
rawPlan: string;
|
||||
connectedServers: any[];
|
||||
};
|
||||
maxPlayers: number;
|
||||
name: string;
|
||||
motd: string;
|
||||
icon: string;
|
||||
playerData: {
|
||||
playerCount: number;
|
||||
timeNoPlayers: number;
|
||||
};
|
||||
connectable: boolean;
|
||||
visibility: boolean;
|
||||
allCategories: string[];
|
||||
usingCosmetics: boolean;
|
||||
author?: string;
|
||||
authorRank: string;
|
||||
}
|
||||
@ -78,6 +78,36 @@ export interface Deletion {
|
||||
storage_completed_at: number;
|
||||
}
|
||||
|
||||
export const globalType = `
|
||||
export interface OnlineServer {
|
||||
staticInfo: {
|
||||
_id: string;
|
||||
serverPlan: string;
|
||||
serviceStartDate: number;
|
||||
platform: string;
|
||||
planMaxPlayers: number;
|
||||
planRam: number;
|
||||
alwaysOnline: boolean;
|
||||
rawPlan: string;
|
||||
connectedServers: any[];
|
||||
};
|
||||
maxPlayers: number;
|
||||
name: string;
|
||||
motd: string;
|
||||
icon: string;
|
||||
playerData: {
|
||||
playerCount: number;
|
||||
timeNoPlayers: number;
|
||||
};
|
||||
connectable: boolean;
|
||||
visibility: boolean;
|
||||
allCategories: string[];
|
||||
usingCosmetics: boolean;
|
||||
author?: string;
|
||||
authorRank: string;
|
||||
}
|
||||
`
|
||||
|
||||
export interface OnlineServer {
|
||||
staticInfo: StaticInfo;
|
||||
maxPlayers: number;
|
||||
|
||||
29
apps/www/src/lib/types/mhsf.d.ts
vendored
Normal file
29
apps/www/src/lib/types/mhsf.d.ts
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
declare namespace MHSF {
|
||||
export type Server = {
|
||||
staticInfo: {
|
||||
_id: string;
|
||||
serverPlan: string;
|
||||
serviceStartDate: number;
|
||||
platform: string;
|
||||
planMaxPlayers: number;
|
||||
planRam: number;
|
||||
alwaysOnline: boolean;
|
||||
rawPlan: string;
|
||||
connectedServers: any[];
|
||||
};
|
||||
maxPlayers: number;
|
||||
name: string;
|
||||
motd: string;
|
||||
icon: string;
|
||||
playerData: {
|
||||
playerCount: number;
|
||||
timeNoPlayers: number;
|
||||
};
|
||||
connectable: boolean;
|
||||
visibility: boolean;
|
||||
allCategories: string[];
|
||||
usingCosmetics: boolean;
|
||||
author?: string;
|
||||
authorRank: string;
|
||||
};
|
||||
}
|
||||
37
apps/www/src/lib/types/sort.ts
Normal file
37
apps/www/src/lib/types/sort.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 { OnlineServer } from "./mh-server";
|
||||
|
||||
export interface Sort {
|
||||
toIdentifier(): string;
|
||||
fromIdentifier(identifier: string): Sort;
|
||||
sortToServers(serverA: OnlineServer, serverB: OnlineServer): number;
|
||||
}
|
||||
@ -28,22 +28,63 @@
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import {
|
||||
clerkClient,
|
||||
clerkMiddleware,
|
||||
createRouteMatcher,
|
||||
} from "@clerk/nextjs/server";
|
||||
import { type NextRequest, NextResponse } from "next/server";
|
||||
import { ServerResponse } from "./lib/types/mh-server";
|
||||
|
||||
// Thanks for the router matcher API Clerk <3
|
||||
const isRootRoute = createRouteMatcher(["/"]);
|
||||
const isOldServerRoute = createRouteMatcher([
|
||||
"/server/:serverName",
|
||||
"/server/:serverName/statistics",
|
||||
]);
|
||||
const apiRoute = createRouteMatcher(["/api/(.*)"]);
|
||||
|
||||
export default process.env.NEXT_PUBLIC_IS_AUTH === "true"
|
||||
? clerkMiddleware(async (auth, req) => {
|
||||
const authRes = await auth();
|
||||
const client = await clerkClient();
|
||||
|
||||
if (isRootRoute(req)) {
|
||||
switch ((await auth()).userId === null) {
|
||||
switch (authRes.userId === null) {
|
||||
case false:
|
||||
return NextResponse.redirect(new URL("/servers", req.url));
|
||||
case true:
|
||||
return NextResponse.redirect(new URL("/home", req.url));
|
||||
}
|
||||
}
|
||||
// If user is banned, disable all API routes
|
||||
if (authRes.userId !== null) {
|
||||
// User exists
|
||||
const user = await client.users.getUser(authRes.userId);
|
||||
const userBannedMetadata = user.publicMetadata.banned;
|
||||
|
||||
if (userBannedMetadata !== undefined) {
|
||||
// User is banned
|
||||
if (apiRoute(req)) {
|
||||
return NextResponse.json({
|
||||
banned:
|
||||
"You were banned. (and I'm not telling you why) Why are you trying to use the API. Huh? Tell me. Now. You're not funny.",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isOldServerRoute(req)) {
|
||||
const minehut = await fetch(
|
||||
`https://api.minehut.com/server/${req.url.split("/server/")[1].split("/")[0]}?byName=true`,
|
||||
);
|
||||
const minehutRes: { server: ServerResponse | null } =
|
||||
await minehut.json();
|
||||
|
||||
if (minehutRes.server !== null)
|
||||
return NextResponse.redirect(
|
||||
new URL(`/server/v2/minehut/${minehutRes.server._id}`, req.url),
|
||||
);
|
||||
}
|
||||
})
|
||||
: (request: NextRequest) => {};
|
||||
|
||||
|
||||
@ -32,7 +32,6 @@ import type { OnlineServer } from "@/lib/types/mh-server";
|
||||
import { Inngest } from "inngest";
|
||||
import { serve } from "inngest/next";
|
||||
import { MongoClient } from "mongodb";
|
||||
import { createReportIssue } from "@/lib/linear";
|
||||
|
||||
// Create a client to send and receive events
|
||||
export const inngest = new Inngest({ id: "mhsf" });
|
||||
@ -41,20 +40,6 @@ export const inngest = new Inngest({ id: "mhsf" });
|
||||
export default serve({
|
||||
client: inngest,
|
||||
functions: [
|
||||
inngest.createFunction(
|
||||
{ id: "report" },
|
||||
{ event: "report-server" },
|
||||
async ({ event, step }) => {
|
||||
// by the way, I bombed the Discord stuff
|
||||
await createReportIssue(
|
||||
event.data.server,
|
||||
event.data.reason,
|
||||
event.data.userId
|
||||
);
|
||||
|
||||
return { event, body: "Done" };
|
||||
}
|
||||
),
|
||||
inngest.createFunction(
|
||||
{ id: "short-term-data" },
|
||||
[{ cron: "*/30 * * * *" }, { event: "test/30-min" }],
|
||||
|
||||
@ -31,8 +31,9 @@
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getAuth } from "@clerk/nextjs/server";
|
||||
import { MongoClient } from "mongodb";
|
||||
import { inngest } from "@/pages/api/inngest";
|
||||
import { waitUntil } from "@vercel/functions";
|
||||
import { getServerName } from "@/lib/history-util";
|
||||
import { sendDiscordReport } from "@/lib/discord";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
@ -65,16 +66,11 @@ export default async function handler(
|
||||
reason: reason,
|
||||
userId: userId,
|
||||
});
|
||||
|
||||
// Don't wait for this to finish, just continue anyway
|
||||
inngest.send({
|
||||
name: "report-server",
|
||||
data: {
|
||||
_id: entry.insertedId.toString(),
|
||||
server,
|
||||
reason,
|
||||
userId,
|
||||
},
|
||||
});
|
||||
waitUntil(
|
||||
sendDiscordReport(await getServerName(server as string), userId, reason)
|
||||
);
|
||||
|
||||
// Close the database, but don't close this
|
||||
// serverless instance until it happens
|
||||
|
||||
1406
apps/www/src/poimandres-monaco.json
Normal file
1406
apps/www/src/poimandres-monaco.json
Normal file
File diff suppressed because it is too large
Load Diff
1393
apps/www/src/theme.json
Normal file
1393
apps/www/src/theme.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -9386,6 +9386,11 @@ mlly@^1.7.1, mlly@^1.7.4:
|
||||
pkg-types "^1.3.0"
|
||||
ufo "^1.5.4"
|
||||
|
||||
monaco-editor@^0.52.2:
|
||||
version "0.52.2"
|
||||
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.52.2.tgz#53c75a6fcc6802684e99fd1b2700299857002205"
|
||||
integrity sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==
|
||||
|
||||
mongodb-connection-string-url@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz#e223089dfa0a5fa9bf505f8aedcbc67b077b33e7"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user