release: 1.3.2

This commit is contained in:
dvelo 2024-10-04 21:06:02 -05:00
parent 07ec6cb89b
commit 6aff5751c6
15 changed files with 519 additions and 787 deletions

@ -30,17 +30,17 @@ The primary stack for MHSF is Next.js, a React framework, which you can start by
## Clerk
If you want to test out accounts, [you must create an Clerk key from their website.](https://clerk.com)
Set the following variables in the .env.local file:
```env
```dotenv
# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=<the token you saw>
CLERK_SECRET_KEY=<the token you saw>
IS_AUTH=true
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_..."
CLERK_SECRET_KEY="sk_..."
IS_AUTH="true"
```
## MongoDB
We use [Atlas](https://www.mongodb.com/atlas) to host our MongoDB database, but you do need one. Add the following to your .env.local with your database:
```env
MONGO_DB=
```dotenv
MONGO_DB="mongodb+srv://..."
```
## Smaller things (for production-ready servers)
@ -63,6 +63,18 @@ yarn run build
> You must have a `.env.local` with a MongoDB database (same `MONGO_DB` key) in the previous directory (for monorepo like management) running without Docker, and
> the same `.env.local` *but in the same directory when* using Docker.
### Reporting
Reporting inside MHSF uses [Linear](https://linear.app). Create an API key inside a team, and add it to the `LINEAR`
key inside of `.env.local`
```dotenv
LINEAR="ln_api_..."
```
MHSF will start adding issues automatically, displaying all information
about a server & the user reporting.
For more information about Linear's authentication, refer to the [API
documentation](https://developers.linear.app/docs/graphql/working-with-the-graphql-api#personal-api-keys).
### Inngest
Inngest also runs periodic tasks like Cron, but has less important tasks that may occur. Create an account on Inngest, or just use the dev server.
Do the following tasks and set the endpoint to `<server url>/api/inngest`

@ -4,6 +4,10 @@
"rules": {
"style": {
"useTemplate": "off"
},
"suspicious": {
"noExplicitAny": "off",
"noDoubleEquals": "warn"
}
}
}

@ -7,33 +7,32 @@ console.log(chalk.yellow(chalk.bold("MHSF crontab scripts")));
console.log(chalk.yellow(chalk.bold("by dvelo - licensed under MIT license")));
console.log();
import { MongoClient, WithId } from "mongodb";
import { MongoClient, type WithId } from "mongodb";
import { config } from "dotenv";
import {
import type {
OnlineServer,
OnlineServerExtended,
ServerResponse,
} from "./types/mh-server.js";
import { CronJob } from "cron";
import { Achievement } from "./types/achievement.js";
import type { Achievement } from "./types/achievement.js";
// set-up config
config({
path: process.env.MHC_DOCKER != "true" ? "../.env.local" : "./.env.local",
});
let mongo = new MongoClient(process.env.MONGO_DB as string);
const mongo = new MongoClient(process.env.MONGO_DB as string);
const SUCCESS = chalk.green("SUCCESS");
const ERROR = chalk.red("ERROR");
const WARN = chalk.red("WARN");
const INFO = chalk.blueBright("INFO");
console.log(INFO, "Starting cron job #1");
CronJob.from({
cronTime: "*/30 * * * *",
onTick: function () {
onTick: () => {
periodicCronJob().catch((e) => {
console.log(chalk.red("[CRON] " + ERROR + " Error while running: "));
console.error(e);
@ -46,7 +45,7 @@ CronJob.from({
console.log(INFO, "Starting cron job #2");
CronJob.from({
cronTime: "0 */12 * * *",
onTick: function () {
onTick: () => {
achievementTask().catch((e) => {
console.log(chalk.red("[CRON] " + ERROR + " Error while running: "));
console.error(e);

@ -12,7 +12,6 @@ const nextConfig = {
},
async redirects() {
return [
// Basic redirect
{
source: '/docs',
destination: '/docs/getting-started',

@ -17,6 +17,7 @@
"@biomejs/biome": "^1.8.3",
"@clerk/nextjs": "^5.1.3",
"@emotion/is-prop-valid": "^1.3.0",
"@linear/sdk": "^31.0.0",
"@monaco-editor/react": "^4.6.0",
"@radix-ui/react-hover-card": "^1.1.1",
"@radix-ui/react-icons": "^1.3.0",

@ -20,7 +20,7 @@ export default function ColorProvider({
getCustomization(server).then((v) =>
setColor(v != null ? v.colorScheme : "zinc")
);
else setColor(fetch != null ? fetch.colorScheme : "zinc");
else setColor(fetch.colorScheme);
}, []);
return <div className={`theme-${color}`}>{children}</div>;

@ -2,7 +2,7 @@
import { useState } from "react";
import { Spinner } from "./ui/spinner";
import { Card, CardHeader, CardTitle } from "./ui/card";
import { ServerResponse } from "@/lib/types/mh-server";
import type { ServerResponse } from "@/lib/types/mh-server";
import { useEffectOnce } from "@/lib/useEffectOnce";
import { Button } from "./ui/button";
import { Copy, Layers, XIcon } from "lucide-react";
@ -26,16 +26,16 @@ export default function FavoritesView() {
(b) =>
b.json().then((c) => {
num++;
var apiClone = apiFavorites;
const apiClone = apiFavorites;
apiClone.push(c.server);
setApiFavorites(apiClone);
if (num == d.length) {
if (num === d.length) {
setLoading(false);
}
})
);
});
if (d.length == 0) setLoading(false);
if (d.length === 0) setLoading(false);
});
});
@ -62,7 +62,7 @@ export default function FavoritesView() {
return (
<>
{apiFavorites.length == 0 && (
{apiFavorites.length === 0 && (
<div className="flex items-center justify-center">
<XIcon />
Your favorites are empty. Maybe favorite a server!

@ -7,7 +7,7 @@ import { ChevronRight } from "lucide-react";
import { useRouter } from "@/lib/useRouter";
import { AnimatePresence, motion } from "framer-motion";
export function Sidebar() {
export function sSidebar() {
return (
<>
{allFolders.map((docs) => (

@ -45,324 +45,339 @@ const handleClick = () => {
export const Changelog = () => {
const router = useRouter();
return (
<>
<div>
Running on commit{" "}
<code>
<a
href={`https://github.com/DeveloLongScript/mhsf/commit/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA}`}
<>
<div>
Running on commit{" "}
<code>
<a
href={`https://github.com/DeveloLongScript/mhsf/commit/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA}`}
>
{(
process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA || "unknown"
).substring(0, 7)}
</a>{" "}
{process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID != undefined &&
process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID != "" && (
<>
{" "}
| on PR{" "}
<a
href={`https://github.com/DeveloLongScript/MHSF/pull/${process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID}`}
>
{process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID}
</a>{" "}
by{" "}
<a
href={`https://github.com/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME}`}
>
{process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME}
</a>
</>
)}{" "}
{process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE != undefined &&
`| ${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE.substring(0, 24)}`}
</code>
</div>
<div className="md:grid md:grid-cols-3 gap-1.5">
<Button
className="text-sm hover:h-[60px] animate-all group block max-md:w-full max-md:mt-2"
onClick={() =>
window.open("https://discord.com/invite/cCyEeUs", "_blank")
}
>
{(
process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA || "unknown"
).substring(0, 7)}
</a>{" "}
{process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID != undefined &&
process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID != "" && (
<>
{" "}
| on PR{" "}
<a
href={`https://github.com/DeveloLongScript/MHSF/pull/${process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID}`}
>
{process.env.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID}
</a>{" "}
by{" "}
<a
href={`https://github.com/${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME}`}
>
{process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_AUTHOR_NAME}
</a>
</>
)}{" "}
{process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE != undefined &&
`| ${process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_MESSAGE.substring(0, 24)}`}
</code>
</div>
<div className="md:grid md:grid-cols-3 gap-1.5">
<Button
className="text-sm hover:h-[60px] animate-all group block max-md:w-full max-md:mt-2"
onClick={() =>
window.open("https://discord.com/invite/cCyEeUs", "_blank")
}
>
<span className="group-hover:underline flex items-center">
<Discord className="mr-2" /> Discord
<Discord className="mr-2"/> Discord
</span>
<Marquee
className="hidden group-hover:flex font-normal"
style={{ "--duration": "15s" }}
<Marquee
className="hidden group-hover:flex font-normal"
style={{"--duration": "15s"}}
>
Join the offical Minehut Discord server! Talk to people that like
MHSF too!
</Marquee>
</Button>
<Button
className="text-sm max-md:w-full max-md:mt-2 hover:h-[60px] animate-all group block "
onClick={() =>
window.open("https://github.com/DeveloLongScript/MHSF", "_blank")
}
>
Join the offical Minehut Discord server! Talk to people that like
MHSF too!
</Marquee>
</Button>
<Button
className="text-sm max-md:w-full max-md:mt-2 hover:h-[60px] animate-all group block "
onClick={() =>
window.open("https://github.com/DeveloLongScript/MHSF", "_blank")
}
>
<span className="group-hover:underline flex items-center">
<Github className="mr-2" fill={useDepTheme()} /> Star on GitHub
<Github className="mr-2" fill={useDepTheme()}/> Star on GitHub
</span>
<Marquee
className="hidden group-hover:flex font-normal"
style={{ "--duration": "10s" }}
<Marquee
className="hidden group-hover:flex font-normal"
style={{"--duration": "10s"}}
>
Support the development of MHSF by starring it on GitHub!
</Marquee>
</Button>
<Button
className="text-sm max-md:w-full max-md:mt-2 hover:h-[60px] animate-all group block "
onClick={() => window.open("/docs", "_blank")}
>
Support the development of MHSF by starring it on GitHub!
</Marquee>
</Button>
<Button
className="text-sm max-md:w-full max-md:mt-2 hover:h-[60px] animate-all group block "
onClick={() => window.open("/docs", "_blank")}
>
<span className="group-hover:underline flex items-center">
<BookIcon className="mr-2" size={16} /> See the docs
<BookIcon className="mr-2" size={16}/> See the docs
</span>
<Marquee
className="hidden group-hover:flex font-normal"
style={{ "--duration": "10s" }}
>
See more information about MHSF and how to use it
</Marquee>
</Button>
</div>
<br />
<div>
<strong className="flex items-center">
Version 1.3.0 (September 9th 2024)
</strong>
<ul>
<li>
<A alt="New documentation linking">Docs:Reading</A>
</li>
<li>
Achievements are here! See more at{" "}
<A alt="here">Docs:Advanced/Achievements</A>
</li>
<li> Finally fixed Cron actions for the final time</li>
<li> Overhauled account preferences</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version 1.2.0 (September 3rd 2024)
</strong>
<ul>
<li> Added documentation</li>
<li> Brand new linking of padding and server options</li>
<li> New system to ensure automatic Cron actions!</li>
<li> and alot more!</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version 1.1.0 (August 24rd 2024)
</strong>
<ul>
<li> Brand new hero page</li>
<li> New padding option on server list</li>
<li> New help guide</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version 1.0.0 (August 22nd 2024)
</strong>
<ul>
<li>
1.0!{" "}
<Button className="h-[25px] w-[50px] ml-2" onClick={handleClick}>
woah!
</Button>
</li>
<li> New hover card on server title hover</li>
<li> Moving to self-hosted cron jobs</li>
<li> Fixing some mobile issues</li>
</ul>
</div>
<br />
<Separator />
<Marquee
className="hidden group-hover:flex font-normal"
style={{"--duration": "10s"}}
>
See more information about MHSF and how to use it
</Marquee>
</Button>
</div>
<br/>
<div>
<strong className="flex items-center">
Version 1.3.2 (October 4th 2024)
</strong>
<ul>
<li>
Minor backend changes
</li>
<li>
<A alt="Please check on GitHub for statuses about this project.">Special:GitHub/releases/tag/1.3.2</A>
</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version 1.3.0 (September 9th 2024)
</strong>
<ul>
<li>
<A alt="New documentation linking">Docs:Reading</A>
</li>
<li>
Achievements are here! See more at{" "}
<A alt="here">Docs:Advanced/Achievements</A>
</li>
<li> Finally fixed Cron actions for the final time</li>
<li> Overhauled account preferences</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version 1.2.0 (September 3rd 2024)
</strong>
<ul>
<li> Added documentation</li>
<li> Brand new linking of padding and server options</li>
<li> New system to ensure automatic Cron actions!</li>
<li> and alot more!</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version 1.1.0 (August 24rd 2024)
</strong>
<ul>
<li> Brand new hero page</li>
<li> New padding option on server list</li>
<li> New help guide</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version 1.0.0 (August 22nd 2024)
</strong>
<ul>
<li>
1.0!{" "}
<Button className="h-[25px] w-[50px] ml-2" onClick={handleClick}>
woah!
</Button>
</li>
<li> New hover card on server title hover</li>
<li> Moving to self-hosted cron jobs</li>
<li> Fixing some mobile issues</li>
</ul>
</div>
<br/>
<Separator/>
<br />
<div>
<strong className="flex items-center">
Version b-0.10.7 (August 18th 2024)
</strong>
<ul>
<li> New server information tab on server pages</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.10.2 (August 18th 2024)
</strong>
<ul>
<li> Content fades-in on load</li>
<li> Instead of using spinners, now we are using Skeletons</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.10.0 (August 17th 2024)
</strong>
<ul>
<li> Revamped server list button list</li>
<li> Added welcome dialog when first launching</li>
<li>
Fixed an issue where servers were still able to be favorited
client-side when logged out
</li>
<li> Improved MOTD engine</li>
</ul>
<br />
<i>👀</i>
{/** Ensure Tailwind pre-renders all grid column types */}
<span className="grid-cols-6" />
<span className="grid-cols-5" />
<span className="grid-cols-4" />
</div>
<br />
<br />
<div>
<strong className="flex items-center">
Version b-0.9.0 (August 15th 2024)
</strong>
<ul>
<li> Adding favorites sorting option</li>
<li> Fixed right-click context menu on the server list</li>
<li> Fixed metadata bugs</li>
</ul>
<br />
<i>
Hey! Update on statistics. Recently, we have figured out the Minehut
API is blocked to Vercel servers (atleast the <code>/servers</code>{" "}
endpoint). I'm actively trying to find a loop-hole so that statistics
works correctly. Thank you {":)"}
</i>
<br />
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.8.0 (August 11th 2024)
</strong>
<ul>
<li> Fixing up command bar</li>
<li> Renaming "Short Term" to "Statistics"</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.7.2 (August 7th 2024)
</strong>
<ul>
<li> Adding new spinners to pages that needed it</li>
<li> Fixed lots of bugs</li>
<li> Moved from Inngest to Vercel Cron</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.7.0 (August 7th 2024)
</strong>
<ul>
<li> Added customization to servers</li>
<li> New button focus effect</li>
<li> Lots of bugfixes</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.6.0 (August 3rd 2024)
</strong>
<ul>
<li> Enhanced shortcuts</li>
<li> Added gradient beam to player count</li>
<li> Updated loading animations</li>
<li> Lots of bugfixes</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.4.5 (July 26th 2024):
</strong>
<ul>
<li> Made charts better</li>
<li> Sorted API endpoints</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.4 (July 25th 2024):
</strong>
<ul>
<li> Added Info button</li>
<li> Fixed Clerk in production</li>
<li> Added Turbo for faster builds</li>
<li>
<strong>Added historical data</strong>
</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.3 (July 23th 2024):
</strong>
<ul>
<li>
Fixed minor bugs described by <User user="@Tarna" />
</li>
</ul>
</div>
<br />
<div>
<strong className="flex items-center">
Version b-0.2 (July 23th 2024):
</strong>
<ul>
<li> Inital release!</li>
</ul>
</div>
<br />
<div>
<strong>All developers that helped out:</strong>
<Link href="https://dvelo.vercel.app">
<Image
src="/imgs/badge1.png"
alt="cool badge"
width={88}
height={31}
className="w-[88px] h-[31px]"
/>
</Link>
</div>
</>
<br/>
<div>
<strong className="flex items-center">
Version b-0.10.7 (August 18th 2024)
</strong>
<ul>
<li> New server information tab on server pages</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.10.2 (August 18th 2024)
</strong>
<ul>
<li> Content fades-in on load</li>
<li> Instead of using spinners, now we are using Skeletons</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.10.0 (August 17th 2024)
</strong>
<ul>
<li> Revamped server list button list</li>
<li> Added welcome dialog when first launching</li>
<li>
Fixed an issue where servers were still able to be favorited
client-side when logged out
</li>
<li> Improved MOTD engine</li>
</ul>
<br/>
<i>👀</i>
{/** Ensure Tailwind pre-renders all grid column types */}
<span className="grid-cols-6"/>
<span className="grid-cols-5"/>
<span className="grid-cols-4"/>
</div>
<br/>
<br/>
<div>
<strong className="flex items-center">
Version b-0.9.0 (August 15th 2024)
</strong>
<ul>
<li> Adding favorites sorting option</li>
<li> Fixed right-click context menu on the server list</li>
<li> Fixed metadata bugs</li>
</ul>
<br/>
<i>
Hey! Update on statistics. Recently, we have figured out the Minehut
API is blocked to Vercel servers (atleast the <code>/servers</code>{" "}
endpoint). I'm actively trying to find a loop-hole so that statistics
works correctly. Thank you {":)"}
</i>
<br/>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.8.0 (August 11th 2024)
</strong>
<ul>
<li> Fixing up command bar</li>
<li> Renaming "Short Term" to "Statistics"</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.7.2 (August 7th 2024)
</strong>
<ul>
<li> Adding new spinners to pages that needed it</li>
<li> Fixed lots of bugs</li>
<li> Moved from Inngest to Vercel Cron</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.7.0 (August 7th 2024)
</strong>
<ul>
<li> Added customization to servers</li>
<li> New button focus effect</li>
<li> Lots of bugfixes</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.6.0 (August 3rd 2024)
</strong>
<ul>
<li> Enhanced shortcuts</li>
<li> Added gradient beam to player count</li>
<li> Updated loading animations</li>
<li> Lots of bugfixes</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.4.5 (July 26th 2024):
</strong>
<ul>
<li> Made charts better</li>
<li> Sorted API endpoints</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.4 (July 25th 2024):
</strong>
<ul>
<li> Added Info button</li>
<li> Fixed Clerk in production</li>
<li> Added Turbo for faster builds</li>
<li>
<strong>Added historical data</strong>
</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.3 (July 23th 2024):
</strong>
<ul>
<li>
Fixed minor bugs described by <User user="@Tarna"/>
</li>
</ul>
</div>
<br/>
<div>
<strong className="flex items-center">
Version b-0.2 (July 23th 2024):
</strong>
<ul>
<li> Inital release!</li>
</ul>
</div>
<br/>
<div>
<strong>All developers that helped out:</strong>
<Link href="https://dvelo.vercel.app">
<Image
src="/imgs/badge1.png"
alt="cool badge"
width={88}
height={31}
className="w-[88px] h-[31px]"
/>
</Link>
</div>
</>
);
};
const Github = (props: SVGProps<SVGSVGElement>) => (
<svg
viewBox="0 0 256 250"
width="1em"
height="1em"
fill="#24292f"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
{...props}
>
<path d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z" />
<svg
viewBox="0 0 256 250"
width="1em"
height="1em"
fill="#24292f"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
{...props}
>
<path
d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z" />
</svg>
);

29
src/lib/linear.ts Normal file

@ -0,0 +1,29 @@
import { LinearClient, LinearFetch, User } from "@linear/sdk";
export async function createReportIssue(server: string, reportDescription: string, userId: string) {
const linearClient = new LinearClient({
apiKey: process.env.LINEAR
})
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 })
}
}
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.*
`

@ -1,455 +1,95 @@
import { OnlineServer } from "@/lib/types/mh-server";
import type { OnlineServer } from "@/lib/types/mh-server";
import { Inngest } from "inngest";
import { serve } from "inngest/next";
import { Document, MongoClient, ObjectId, WithId } from "mongodb";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
Client,
EmbedBuilder,
GatewayIntentBits,
ModalBuilder,
REST,
Routes,
SlashCommandBuilder,
SlashCommandStringOption,
TextChannel,
TextInputBuilder,
TextInputStyle,
} from "discord.js";
import {createReportIssue} from "@/lib/linear";
let reporting = true;
let initalizedYet = false;
const commands = [
new SlashCommandBuilder()
.setName("realive")
.setDescription("Re-alive an existing report that was glitched")
.addStringOption(
new SlashCommandStringOption()
.setName("id")
.setRequired(true)
.setDescription("Report ID")
),
];
let client: Client;
// this isn't entirely necessary, to run each time the server starts
async function init() {
try {
const rest = new REST({ version: "10" }).setToken(
process.env.DISCORD_TOKEN as string
);
try {
console.log("[REPORTING] Started refreshing application (/) commands.");
await rest.put(
Routes.applicationCommands(process.env.DISCORD_APP_ID as string),
{ body: commands }
);
console.log(
"[REPORTING] Successfully reloaded application (/) commands."
);
} catch (error) {
console.error(error);
}
} catch {
console.log(
"[REPORTING] Discord API token not found, skipping reporting..."
);
reporting = false;
}
client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
});
client.on("interactionCreate", async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === "realive") {
await interaction.reply({
embeds: [
new EmbedBuilder()
.setTitle("Re-creating report")
.setColor("Blurple")
.setDescription("This may take a few seconds.."),
],
ephemeral: true,
});
const mongo = new MongoClient(process.env.MONGO_DB as string);
const db = mongo.db("mhsf");
const collection = db.collection("reports");
let reportDoc: WithId<Document> | null;
try {
reportDoc = await collection.findOne({
_id: new ObjectId(interaction.options.getString("id") as string),
});
} catch {
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle("Report ID not valid")
.setColor("Red")
.setDescription("This report could not be found."),
],
});
return;
}
if (reportDoc === null) {
await interaction.editReply({
embeds: [
new EmbedBuilder()
.setTitle("Report not found")
.setColor("Red")
.setDescription("This report could not be found."),
],
});
return;
}
report({ data: { ...reportDoc, id: reportDoc._id.toString() } });
}
});
return;
}
// Create a client to send and receive events
export const inngest = new Inngest({ id: "mhsf" });
// Create an API that serves zero functions
export default serve({
client: inngest,
functions: [
inngest.createFunction(
{ id: "report" },
{ event: "report-server" },
async ({ event, step }) => {
if (!reporting) {
throw new Error("Cannot report server: Discord API token not found");
}
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);
if (!initalizedYet) {
await init();
initalizedYet = true;
await client.login(process.env.DISCORD_TOKEN);
return { event, body: "Done" }
},
),
inngest.createFunction(
{ id: "short-term-data" },
[{ cron: "*/30 * * * *" }, { event: "test/30-min" }],
async ({ event, step }) => {
const mongo = new MongoClient(process.env.MONGO_DB as string);
try {
const mh = await step.run("grab-servers-from-api", async () => {
return await (
await fetch("https://api.minehut.com/servers", {
headers: {
accept: "application/json",
"accept-language": Math.random().toString(),
priority: "u=1, i",
"sec-ch-ua": '"Not/A)Brand";v="8", "Chromium";v="126"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "cross-site",
"Content-Type": "application/json",
Referer: "http://localhost:3000/",
"Referrer-Policy": "strict-origin-when-cross-origin",
},
body: null,
method: "GET",
})
).json();
});
console.log(`[REPORTING] Waiting for bot to be ready`);
const mha = mongo.db("mhsf").collection("mh");
const meta = mongo.db("mhsf").collection("meta");
const dbl = mongo.db("mhsf").collection("history");
client.on("ready", () => {
console.log(
`[REPORTING] Bot logged in as ${client.user?.displayName}`
);
report(event);
});
return;
}
await mha.insertOne({
total_players: mh.total_players,
total_servers: mh.total_servers,
date: new Date(),
});
report(event);
}
),
inngest.createFunction(
{ id: "short-term-data" },
[{ cron: "*/30 * * * *" }, { event: "test/30-min" }],
async ({ event, step }) => {
const mongo = new MongoClient(process.env.MONGO_DB as string);
try {
const mh = await step.run("grab-servers-from-api", async () => {
return await (
await fetch("https://api.minehut.com/servers", {
headers: {
accept: "application/json",
"accept-language": Math.random().toString(),
priority: "u=1, i",
"sec-ch-ua": '"Not/A)Brand";v="8", "Chromium";v="126"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "cross-site",
"Content-Type": "application/json",
Referer: "http://localhost:3000/",
"Referrer-Policy": "strict-origin-when-cross-origin",
},
body: null,
method: "GET",
})
).json();
});
const completed = await step.run("listing-servers", async () => {
mh.servers.forEach(async (server: OnlineServer, i: number) => {
const serverFavoritesObject = await meta.findOne({
server: server.name,
});
let favorites = 0;
if (serverFavoritesObject != undefined)
favorites = serverFavoritesObject.favorites;
const mha = mongo.db("mhsf").collection("mh");
const meta = mongo.db("mhsf").collection("meta");
const dbl = mongo.db("mhsf").collection("history");
await dbl.insertOne({
player_count: server.playerData.playerCount,
favorites,
server: server.name,
date: new Date(),
});
console.log(i, mh.servers.length);
});
return true;
});
if (completed == true) {
return { event, body: "Finished!" };
}
} catch (e) {
await mongo.close();
await mha.insertOne({
total_players: mh.total_players,
total_servers: mh.total_servers,
date: new Date(),
});
const completed = await step.run("listing-servers", async () => {
mh.servers.forEach(async (server: OnlineServer, i: number) => {
const serverFavoritesObject = await meta.findOne({
server: server.name,
});
let favorites = 0;
if (serverFavoritesObject != undefined)
favorites = serverFavoritesObject.favorites;
await dbl.insertOne({
player_count: server.playerData.playerCount,
favorites,
server: server.name,
date: new Date(),
});
console.log(i, mh.servers.length);
});
return true;
});
if (completed == true) {
return { event, body: "Finished!" };
}
} catch (e) {
await mongo.close();
return { event, body: "Cloudflare.. aborting " + e };
}
}
),
],
return { event, body: "Cloudflare.. aborting " + e };
}
},
),
],
});
async function report(event: any) {
const isTextBased = client.channels.cache.get(
process.env.REPORTS_CHANNEL as string
)?.isTextBased;
if (!isTextBased) {
throw new Error(
"Cannot report server: Report channel not found or not a text channel."
);
}
const channel = client.channels.cache.get(
process.env.REPORTS_CHANNEL as string
) as TextChannel;
const goToServer = new ButtonBuilder()
.setLabel("Go to server")
.setStyle(ButtonStyle.Link)
.setURL("https://list.mlnehut.com/server/" + event.data.server);
const confirm = new ButtonBuilder()
.setCustomId("resolve")
.setLabel("Resolve")
.setStyle(ButtonStyle.Primary);
const typed = (name: string) =>
new ButtonBuilder()
.setCustomId("typed")
.setLabel(name + " is typing")
.setStyle(ButtonStyle.Secondary)
.setDisabled(true);
const cancel = new ButtonBuilder()
.setCustomId("cancel")
.setLabel("Cancel")
.setStyle(ButtonStyle.Secondary);
const undo = new ButtonBuilder()
.setCustomId("undo")
.setLabel("Undo")
.setStyle(ButtonStyle.Danger);
const undor = new ActionRowBuilder<ButtonBuilder>().addComponents(
undo,
goToServer
);
const rowN = new ActionRowBuilder<ButtonBuilder>().addComponents(
confirm,
cancel,
goToServer
);
const message = await channel.send({
embeds: [
new EmbedBuilder()
.setColor("Orange")
.setTitle("New report to server")
.setTimestamp()
.setDescription(
"A server has been reported by a player. \n Reason: " +
event.data.reason +
(event.data.reason == "" ? "*<empty>*" : "")
)
.setFields([
{ name: "User ID", value: `\`${event.data.userId}\`` },
{ name: "Server", value: `${event.data.server}` },
])
.setFooter({
text:
"Is this report glitched? Use the command /realive id:" +
event.data._id +
" to do actions on this report.",
}),
],
components: [rowN],
});
setTimeout(async () => {
await messageLoop();
async function messageLoop() {
const confirmation = await message.awaitMessageComponent({});
if (confirmation.customId == "undo") {
(await confirmation.reply({ content: "Done!" })).delete();
await message.edit({
embeds: [
new EmbedBuilder()
.setColor("Orange")
.setTitle("New report to server")
.setTimestamp()
.setDescription(
"A server has been reported by a player. \n Reason: " +
event.data.reason +
(event.data.reason == "" ? "*<empty>*" : "")
)
.setFields([
{ name: "User ID", value: `\`${event.data.userId}\`` },
{ name: "Server", value: `${event.data.server}` },
])
.setFooter({
text:
"Is this report glitched? Use the command /realive id:" +
event.data._id +
" to do actions on this report.",
}),
],
components: [rowN],
});
}
if (confirmation.customId == "resolve") {
await message.edit({
embeds: [
new EmbedBuilder()
.setColor("Green")
.setTitle("Server report resolved")
.setTimestamp()
.setDescription(
"The server report has been resolved by <@" +
confirmation.user.id +
"> \n Reason: " +
event.data.reason +
(event.data.reason == "" ? "*<empty>*" : "")
)
.setFields([
{ name: "User ID", value: `\`${event.data.userId}\`` },
{ name: "Server", value: `${event.data.server}` },
])
.setFooter({
text:
"Is this report glitched? Use the command /realive id:" +
event.data._id +
" to do actions on this report.",
}),
],
components: [undor],
});
(
await confirmation.reply({ content: "Done!", ephemeral: true })
).delete();
}
if (confirmation.customId == "cancel") {
const modal = new ModalBuilder().setCustomId("why").setTitle("MHSF");
const favoriteColorInput = new TextInputBuilder()
.setCustomId("whyToCancel")
.setLabel("Cancelation reason")
.setStyle(TextInputStyle.Short);
const row = new ActionRowBuilder<TextInputBuilder>().addComponents(
favoriteColorInput
);
modal.addComponents(row);
confirmation.showModal(modal);
try {
let reportedYet = false;
await message.edit({
components: [
new ActionRowBuilder<ButtonBuilder>().addComponents(
typed(
confirmation.user.globalName || confirmation.user.username
)
),
],
});
setTimeout(async () => {
if (reportedYet == false) {
await message.edit({
components: [rowN],
});
await messageLoop();
}
return;
}, 60_000);
const submission = await confirmation.awaitModalSubmit({
time: 60_000,
});
const text = submission.fields.getTextInputValue("whyToCancel");
if (text == "") {
await submission.reply({ content: "Done!", ephemeral: true });
await messageLoop();
}
reportedYet = true;
await message.edit({
embeds: [
new EmbedBuilder()
.setColor("Red")
.setTitle("Server report cancelled")
.setTimestamp()
.setDescription(
"The server report has been cancelled by <@" +
confirmation.user.id +
"> \n Reason of cancelation: " +
text +
"\nReason of report: " +
event.data.reason +
(event.data.reason == "" ? "*<empty>*" : "")
)
.setFields([
{ name: "User ID", value: `\`${event.data.userId}\`` },
{ name: "Server", value: `${event.data.server}` },
])
.setFooter({
text:
"Is this report glitched? Use the command /realive id:" +
event.data._id +
" to do actions on this report.",
}),
],
components: [undor],
});
await submission.reply({ content: "Done!", ephemeral: true });
} catch (e) {
await message.edit({
components: [rowN],
});
await confirmation.reply({
embeds: [
new EmbedBuilder()
.setTitle("You took too long!")
.setDescription("Please try again.")
.setColor("Red"),
],
ephemeral: true,
});
}
}
await messageLoop();
}
}, 0);
}

@ -45,3 +45,4 @@ export default async function handler(
});
res.status(200).send({ message: "Success" });
}

@ -2,17 +2,17 @@ import { MongoClient } from "mongodb";
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
req: NextApiRequest,
res: NextApiResponse,
) {
const { server } = req.query;
if (!server) return res.status(400).send({ error: "No server was provided" });
const { server } = req.query;
if (!server) return res.status(400).send({ error: "No server was provided" });
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
const db = client.db("mhsf");
const collection = db.collection("achievements");
const db = client.db("mhsf");
const collection = db.collection("achievements");
res.send({ result: await collection.find({ name: server }).toArray() });
res.send({ result: await collection.find({ name: server }).toArray() });
}

@ -1,21 +1,21 @@
import { NextApiRequest, NextApiResponse } from "next";
import { NextApiRequest, NextApiResponse } from "next";
import { MongoClient } from "mongodb";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.headers.Authentication != process.env.WEBHOOK_AUTH) {
return res.status(401).json({ error: "Unauthorized" });
}
const { id } = req.body.data;
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
if (req.headers.Authentication != process.env.WEBHOOK_AUTH) {
return res.status(401).json({ error: "Unauthorized" });
}
const { id } = req.body.data;
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
const db = client.db("mhsf");
const collection = db.collection("claimed-users");
await collection.findOneAndDelete({ userId: id });
const db = client.db("mhsf");
const collection = db.collection("claimed-users");
await collection.findOneAndDelete({ userId: id });
res.send({ message: "Done!" });
client.close();
res.send({ message: "Done!" });
client.close();
}

@ -649,6 +649,11 @@
resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz"
integrity sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==
"@graphql-typed-document-node/core@^3.1.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
"@grpc/grpc-js@^1.7.1":
version "1.11.1"
resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.11.1.tgz#a92f33e98f1959feffcd1b25a33b113d2c977b70"
@ -753,6 +758,15 @@
jsbi "^4.3.0"
tslib "^2.4.1"
"@linear/sdk@^31.0.0":
version "31.0.0"
resolved "https://registry.yarnpkg.com/@linear/sdk/-/sdk-31.0.0.tgz#05faac8bbb9ebd7b530760e9535f5bd7fe2f3618"
integrity sha512-51mKO/R5JBnQERDfg5uMryHGRyavUAkveIiHxxshItvEIwFVACJi8S7N2oabhSlqqNZf2rL0YlVokeRxd72s6A==
dependencies:
"@graphql-typed-document-node/core" "^3.1.0"
graphql "^15.4.0"
isomorphic-unfetch "^3.1.0"
"@mdx-js/esbuild@^2.0.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@mdx-js/esbuild/-/esbuild-2.3.0.tgz#97f2f1b854d904c50bcd0a219b3664657f4fe8c3"
@ -4145,6 +4159,11 @@ graphemer@^1.4.0:
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
graphql@^15.4.0:
version "15.9.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.9.0.tgz#4e8ca830cfd30b03d44d3edd9cac2b0690304b53"
integrity sha512-GCOQdvm7XxV1S4U4CGrsdlEN37245eC8P9zaYCMr6K1BG0IPGy5lUwmJsEOGyl1GD6HXjOtl2keCP9asRBwNvA==
gray-matter@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798"
@ -4735,6 +4754,14 @@ isexe@^2.0.0:
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
isomorphic-unfetch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f"
integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==
dependencies:
node-fetch "^2.6.1"
unfetch "^4.2.0"
iterator.prototype@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz"
@ -6165,7 +6192,7 @@ node-fetch-native@^1.6.3:
resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.4.tgz#679fc8fd8111266d47d7e72c379f1bed9acff06e"
integrity sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==
node-fetch@^2.6.12:
node-fetch@^2.6.1, node-fetch@^2.6.12:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
@ -7794,6 +7821,11 @@ undici@6.13.0:
resolved "https://registry.yarnpkg.com/undici/-/undici-6.13.0.tgz#7edbf4b7f3aac5f8a681d515151bf55cb3589d72"
integrity sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==
unfetch@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
unified@^10.0.0, unified@^10.1.2:
version "10.1.2"
resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df"