Compare commits

...

34 Commits

Author SHA1 Message Date
dependabot[bot]
97891aefeb
Merge d1166c44110df403a53132cd7d67f521b913c139 into fa7e1b1278a736a44346ed914e980850c4df6c92 2025-01-25 10:46:06 -06:00
dvelo
fa7e1b1278
feat: remove snow 2025-01-25 10:45:03 -06:00
dvelo
e51a2ecd16 fix: remove extra padding on the topbar 2025-01-25 10:44:35 -06:00
dvelo
00f8a1f33e fix: remove snow topbar items as well 2025-01-25 10:40:56 -06:00
dvelo
13489a4145 feat: remove snow 2025-01-25 10:28:21 -06:00
dvelo
185bd77d5f
fix: google not crawling (again) 2025-01-24 20:26:10 -06:00
dvelo
48c038df0f fix: google not crawling (again) 2025-01-24 19:37:10 -06:00
dvelo
49c0a599a4
fix: google crawling 2025-01-24 17:25:39 -06:00
dvelo
7caf2c1b4e fix: google crawling 2025-01-24 17:24:58 -06:00
dvelo
c19f56b685
feat: new auto-link redirect (release) 2025-01-19 17:46:39 -06:00
dvelo
f1fa1d98c1 fix: more type issues 2025-01-19 17:35:05 -06:00
dvelo
51b3050528 fix: type issues 2025-01-19 17:31:01 -06:00
dvelo
2589552592 feat: new auto-link redirect 2025-01-19 17:28:24 -06:00
dvelo
742a95a9db
feat: add coreboxx as a minecraft account verifier 2025-01-18 18:40:31 -06:00
dvelo
9b5d149b35 fix: add alert 2025-01-18 18:39:27 -06:00
dvelo
1b1a9152c5 feat: add coreboxx as a minecraft account verifier 2025-01-18 18:38:19 -06:00
dvelo
4150d86ec7
fix: POST when unlinking is unnecessary 2025-01-18 09:10:17 -06:00
dvelo
67bdca8516 fix: POST when unlinking is unnecessary 2025-01-18 09:05:22 -06:00
dvelo
9afc39f4d6
fix: unlinking doesn't work 2025-01-18 08:53:39 -06:00
dvelo
ceedade382 fix: unlinking doesn't work 2025-01-18 08:51:03 -06:00
dvelo
0eef4ed4f6
fix: linking is broken 2025-01-17 21:54:51 -06:00
dvelo
a319f53381 fix: linking is broken 2025-01-17 21:52:04 -06:00
dvelo
9b6c1f07f9
fix: crash 2025-01-15 21:17:41 -06:00
dvelo
f665cf0373 fix: crash 2025-01-15 21:16:57 -06:00
dvelo
003ee16668
feat: integrated status 2025-01-15 21:04:25 -06:00
dvelo
df959f063e feat: integrated status 2025-01-15 21:01:36 -06:00
dvelo
4f60dde126
fix: undefined issue 2025-01-13 21:26:02 -06:00
dvelo
2dda77e9ee fix: undefined issue 2025-01-13 21:25:07 -06:00
dvelo
00652cc4fb
feat: sponsor tag & seperate server 2025-01-13 21:22:09 -06:00
dvelo
e9b448658a chore: exclude vercel from deploying backend servers 2025-01-13 21:20:54 -06:00
dvelo
7fe52d6c47 feat: add a partner tag for CoreBoxx 2025-01-12 10:29:09 -06:00
dvelo
9765548277 fix: make separate server for minehut api 2025-01-12 10:26:14 -06:00
dependabot[bot]
d1166c4411
build(deps): bump cross-spawn from 7.0.3 to 7.0.6
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-24 00:03:41 +00:00
dvelo
cbdcc74ce8
chore: move dev branch commits -> main
chore: move dev branch commits -> main
2024-11-23 18:01:43 -06:00
42 changed files with 3311 additions and 1907 deletions

2
.vercelignore Normal file

@ -0,0 +1,2 @@
backend-mh-api/
cron/

3
backend-mh-api/.gitignore vendored Normal file

@ -0,0 +1,3 @@
dist/
node_modules/
.env

17
backend-mh-api/README.md Normal file

@ -0,0 +1,17 @@
# MHSF Backend API
This has only one function, which is to get the server list from Minehut's API, but locally so it doesn't get blocked by Cloudflare.
To set it up, there is only one API key:
```dotenv
# Make a secret token to put in here
MHSF_SECRET="..."
```
And make sure its also set up in the Next.js server:
```dotenv
# MHSF Backend API
MHSF_BACKEND_API_LOCATION="http://..."
MHSF_BACKEND_SECRET="..."
```
You shouldn't ever really need this unless your server is blocked by Cloudflare.

@ -0,0 +1,16 @@
{
"name": "backend-mh-api",
"version": "1.0.0",
"main": "dist/index.js",
"author": "DeveloLongScript",
"license": "MIT",
"dependencies": {
"@types/express": "^5.0.0",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"typescript": "^5.7.2"
},
"scripts": {
"build": "npx tsc -p ./tsconfig.json"
}
}

@ -0,0 +1,27 @@
import * as express from "express";
import { config } from "dotenv";
config();
const app = express();
app.get("/", (req, res) => {
res.send({ status: "up" });
});
app.get("/servers", (req, res) => {
if (
req.headers.Authentication !==
`MHSF-Backend-Server ${process.env.MHSF_SECRET}`
)
res.status(401).send({ error: "Unauthorized" });
else
fetch("https://api.minehut.com/servers").then((c) => {
c.json().then((v) => {
res.send(v);
});
});
});
app.listen(6080, () => {
console.log("Backend API listening on port 6080");
});

@ -0,0 +1,12 @@
{
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"moduleResolution": "Node",
"target": "es6",
"outDir": "dist"
},
"include": ["src/**/*"]
}

594
backend-mh-api/yarn.lock Normal file

@ -0,0 +1,594 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/body-parser@*":
version "1.19.5"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4"
integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==
dependencies:
"@types/connect" "*"
"@types/node" "*"
"@types/connect@*":
version "3.4.38"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858"
integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
dependencies:
"@types/node" "*"
"@types/express-serve-static-core@^5.0.0":
version "5.0.3"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.3.tgz#04174d3f0836863467b7fbcbbbcd69441d205715"
integrity sha512-JEhMNwUJt7bw728CydvYzntD0XJeTmDnvwLlbfbAhE7Tbslm/ax6bdIiUwTgeVlZTsJQPwZwKpAkyDtIjsvx3g==
dependencies:
"@types/node" "*"
"@types/qs" "*"
"@types/range-parser" "*"
"@types/send" "*"
"@types/express@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c"
integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==
dependencies:
"@types/body-parser" "*"
"@types/express-serve-static-core" "^5.0.0"
"@types/qs" "*"
"@types/serve-static" "*"
"@types/http-errors@*":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f"
integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==
"@types/mime@^1":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690"
integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==
"@types/node@*":
version "22.10.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.5.tgz#95af89a3fb74a2bb41ef9927f206e6472026e48b"
integrity sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==
dependencies:
undici-types "~6.20.0"
"@types/qs@*":
version "6.9.17"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.17.tgz#fc560f60946d0aeff2f914eb41679659d3310e1a"
integrity sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==
"@types/range-parser@*":
version "1.2.7"
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb"
integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
"@types/send@*":
version "0.17.4"
resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==
dependencies:
"@types/mime" "^1"
"@types/node" "*"
"@types/serve-static@*":
version "1.15.7"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714"
integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==
dependencies:
"@types/http-errors" "*"
"@types/node" "*"
"@types/send" "*"
accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
body-parser@1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind-apply-helpers@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840"
integrity sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
call-bound@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.3.tgz#41cfd032b593e39176a71533ab4f384aa04fd681"
integrity sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==
dependencies:
call-bind-apply-helpers "^1.0.1"
get-intrinsic "^1.2.6"
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
cookie@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9"
integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
dotenv@^16.4.7:
version "16.4.7"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26"
integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-errors "^1.3.0"
gopd "^1.2.0"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
encodeurl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-object-atoms@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941"
integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==
dependencies:
es-errors "^1.3.0"
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
express@^4.21.2:
version "4.21.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32"
integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.7.1"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.3.1"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.12"
proxy-addr "~2.0.7"
qs "6.13.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.19.0"
serve-static "1.16.2"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
finalhandler@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019"
integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==
dependencies:
debug "2.6.9"
encodeurl "~2.0.0"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
get-intrinsic@^1.2.5, get-intrinsic@^1.2.6:
version "1.2.7"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.7.tgz#dcfcb33d3272e15f445d15124bc0a216189b9044"
integrity sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-define-property "^1.0.1"
es-errors "^1.3.0"
es-object-atoms "^1.0.0"
function-bind "^1.1.2"
get-proto "^1.0.0"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
math-intrinsics "^1.1.0"
get-proto@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
inherits@2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
merge-descriptors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
object-inspect@^1.13.3:
version "1.13.3"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a"
integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-to-regexp@0.1.12:
version "0.1.12"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7"
integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
qs@6.13.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
dependencies:
side-channel "^1.0.6"
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
safe-buffer@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
send@0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serve-static@1.16.2:
version "1.16.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296"
integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==
dependencies:
encodeurl "~2.0.0"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.19.0"
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
side-channel-list@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad"
integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.3"
side-channel-map@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-weakmap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
dependencies:
call-bound "^1.0.2"
es-errors "^1.3.0"
get-intrinsic "^1.2.5"
object-inspect "^1.13.3"
side-channel-map "^1.0.1"
side-channel@^1.0.6:
version "1.1.0"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9"
integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
dependencies:
es-errors "^1.3.0"
object-inspect "^1.13.3"
side-channel-list "^1.0.0"
side-channel-map "^1.0.1"
side-channel-weakmap "^1.0.2"
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
typescript@^5.7.2:
version "5.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
undici-types@~6.20.0:
version "6.20.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==

5
next-sitemap.config.js Normal file

@ -0,0 +1,5 @@
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: "https://mhsf.app",
generateRobotsTxt: true
}

@ -48,6 +48,7 @@
"next": "14.2.10",
"next-contentlayer": "^0.3.4",
"next-css-obfuscator": "^2.2.16",
"next-sitemap": "^4.2.3",
"next-themes": "^0.4.3",
"nextjs-toploader": "^1.6.12",
"nprogress": "^0.2.0",

@ -85,7 +85,7 @@ export default function Settings() {
<br />
<strong className="font-bold">Unlink Account</strong>
<div className="flex items-center">
<p>Unlink your Minecraft acconut if you have already linked one.</p>
<p>Unlink your Minecraft account if you have already linked one.</p>
{!linked && (
<Button className="h-[30px] ml-2" disabled>

@ -43,6 +43,7 @@ import type { Metadata, Viewport } from "next";
import { Inter as interFont } from "next/font/google";
import LayoutPart from "@/components/feat/LayoutPart";
import AllBanners from "@/components/feat/AllBanners";
import Footer from "@/components/misc/Footer";
export const extraMetadata = {
twitter: {
@ -84,6 +85,7 @@ export default async function RootLayout({
<Analytics />
<NewDomainDialog />
<UnofficalDialog />
<Footer />
</TooltipProvider>
</ThemeProvider>
</ClerkThemeProvider>

@ -132,16 +132,18 @@ export default function ServerPage({ params }: { params: { server: string } }) {
<ColorProvider server={params.server}>
<div className={"pt-[300px] xl:px-[100px]"}>
<Banner server={params.server} />
<div className="pt-8 z-10 relative">
<div className="pt-8 z-8 relative">
<ServerView server={params.server} />
</div>
<StickyTopbar scrollElevation={100} className="pt-4">
<StickyTopbar scrollElevation={100} className="pt-4 z-10">
<TabServer server={params.server} tabDef="general" />
</StickyTopbar>
<br />
<div className="z-8 relative">
<AfterServerView server={params.server} />
</div>
</div>
</ColorProvider>
</main>
);

@ -118,7 +118,7 @@ export default function AfterServerView({ server }: { server: string }) {
<QRCodeGenerator server={server} />
</DrawerContent>
</Drawer>
<FadeIn className="relative z-10">
<FadeIn className="relative z-8">
<div className="grid sm:grid-cols-6 h-full pl-4 pr-4 ">
<div className="ml-5 mb-2 flex items-center sm:hidden overflow-auto w-[calc(100vw-5rem)]">
{(description != "" || discord != "") && (

@ -29,28 +29,48 @@
*/
"use client";
import { getCustomization } from "@/lib/api";
import { useEffect, useState } from "react";
import "@/themes.css";
import { toast } from "sonner";
import { useRouter } from "@/lib/useRouter";
import { useEffectOnce } from "@/lib/useEffectOnce";
export default function ColorProvider({
server,
children,
fetch,
fetchV,
}: {
server: string;
children: any;
fetch?: any;
fetchV?: any;
}) {
const [color, setColor] = useState("zinc");
const nav = useRouter();
useEffectOnce(() => {
fetch("https://api.minehut.com/server/" + server + "?byName=true")
.then((c) => c.json())
.then((c: any) => {
console.log(c.server.name, server);
if (c.server.name !== server) {
toast.warning(
"The capitalization of this server was incorrect. If your using a permanent link resource, please change it to account for a new name. (" +
c.server.name +
") Redirecting now.",
{ duration: 15000 }
);
nav.replace("/server/" + c.server.name);
}
});
});
useEffect(() => {
if (!fetch)
if (!fetchV)
getCustomization(server).then((v) =>
setColor(v != null ? v.colorScheme : "zinc")
);
else setColor(fetch.colorScheme);
else setColor(fetchV.colorScheme);
}, []);
return <div className={`theme-${color}`}>{children}</div>;

@ -68,7 +68,6 @@ import {
} from "@/lib/api";
import IconDisplay from "./IconDisplay";
import ServerSingle from "@/lib/single";
import SnowfallController from "./misc/SnowfallController";
import { toast } from "sonner";
import { ServerResponse, OnlineServer } from "@/lib/types/mh-server";
import {
@ -856,7 +855,6 @@ export function CommandBarer() {
<ServerCommandBar />
<OfflineServerCB />
<RandomServerDialog />
<SnowfallController />
</>
);
}

@ -50,7 +50,7 @@ export function Sidebar({
return (
<ResizablePanelGroup
direction="horizontal"
className="min-h-[calc(100vh-70px)] pt-[70px]"
className="min-h-[calc(100vh-70px)] pt-[70px] xl:px-[100px] "
>
<ResizablePanel className="max-md:hidden min-w-[285px] max-w-[285px] w-[285px]">
<div className="w-[300px] ml-[10px]">

@ -49,7 +49,13 @@ import {
} from "./ui/card";
import { TooltipContent, TooltipTrigger } from "./ui/tooltip";
export default function ServerCard({ b, motd, mini, favs }: any) {
export default function ServerCard({
b,
motd,
mini,
favs,
selectedProperties,
}: any) {
const router = useRouter();
const clipboard = useClipboard();
const [favoriteStar, setFavoriteStar] = useState(false);
@ -165,17 +171,21 @@ export default function ServerCard({ b, motd, mini, favs }: any) {
</DrawerFooter>
</DrawerContent>
</Drawer>
{b.author != undefined ? (
{selectedProperties.includes("Author") &&
b.author != undefined ? (
<div className="text-sm text-muted-foreground font-normal tracking-normal">
by {b.author}
</div>
) : (
<br />
)}
<TagShower server={b} />
{selectedProperties.includes("Tags") && <TagShower server={b} />}
</CardTitle>
<CardDescription className="float-left inline ">
<span className="flex items-center">
{selectedProperties.includes("Players Online") && (
<>
{b.playerData.playerCount == 0 ? (
<div
className="items-center border"
@ -196,14 +206,19 @@ export default function ServerCard({ b, motd, mini, favs }: any) {
}}
/>
)}
</>
)}
{selectedProperties.includes("Players Online") && (
<span className="pl-1">
{b.playerData.playerCount}{" "}
{b.playerData.playerCount == 1 ? "player" : "players"}{" "}
currently online {favs && <> {favs} favorited</>}
</span>
)}
</span>
{selectedProperties.includes("Actions") && (
<ContextMenu>
<ContextMenuTrigger>
<>
@ -256,9 +271,10 @@ export default function ServerCard({ b, motd, mini, favs }: any) {
</Link>
</ContextMenuContent>
</ContextMenu>
)}
</CardDescription>
<CardContent>
{motd && (
<CardContent className="p-0">
{motd && selectedProperties.includes("MOTD") && (
<span
dangerouslySetInnerHTML={{ __html: motd }}
className="w-[30px] text-center break-all overflow-hidden"

@ -140,7 +140,7 @@ export default function ServerCustomize({
});
});
});
}, [isSignedIn]);
}, [isSignedIn, server]);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {

@ -44,34 +44,16 @@ import {
AllCommunityModule,
ModuleRegistry,
colorSchemeDarkBlue,
colorSchemeDarkWarm,
colorSchemeLightCold,
colorSchemeLightWarm,
themeQuartz,
} from "ag-grid-community";
import {
Menubar,
MenubarCheckboxItem,
MenubarContent,
MenubarItem,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarShortcut,
MenubarSub,
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@/components/ui/menubar";
import { Separator } from "@/components/ui/separator";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { allCategories, allTags } from "@/config/tags";
import events from "@/lib/commandEvent";
import { allTags } from "@/config/tags";
import ServersList from "@/lib/list";
import { OnlineServer } from "@/lib/types/mh-server";
import useClipboard from "@/lib/useClipboard";
@ -81,6 +63,7 @@ import { cn } from "@/lib/utils";
import { SignedIn, SignedOut, useUser } from "@clerk/nextjs";
import { ChatBubbleIcon, InputIcon } from "@radix-ui/react-icons";
import {
ArrowDown,
ArrowDownZA,
Check,
CircleUser,
@ -93,7 +76,6 @@ import {
Sun,
XIcon,
} from "lucide-react";
import { CommandIcon } from "lucide-react";
import { useTheme } from "next-themes";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
@ -112,12 +94,13 @@ import { pageFind } from "./misc/Link";
import { Badge } from "./ui/badge";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Skeleton } from "./ui/skeleton";
import { affiliates } from "@/config/affiliates";
import { LoadingSpinner } from "./ui/loading-spinner";
import StickyTopbar from "./misc/StickyTopbar";
import { HoverCard } from "@radix-ui/react-hover-card";
import { HoverCardTrigger } from "./ui/hover-card";
import { ExampleChart } from "./charts/ExampleChart";
import ServerListInterface from "./misc/ServerListInterface";
import NoItems from "./misc/NoItems";
// ag-grid
ModuleRegistry.registerModules([AllCommunityModule]);
@ -184,16 +167,23 @@ export default function ServerList() {
const router = useRouter();
const { user, isSignedIn } = useUser();
const [pOS, setpOS] = useState(false);
const [selectedProperties, setSelectedProperties] = useState<string[]>([
"Author",
"MOTD",
"Tags",
"Players Online",
"Actions",
]);
const [ipr, setIPR] = useState<string>("4");
const [presentationMode, setPresentationMode] = useState<"table" | "grid">(
"grid",
"grid"
);
const [am, setAM] = useState(false);
const [filters, setFilters] = useState<
Array<(server: OnlineServer) => Promise<boolean>>
>([]);
const [randomData, setRandomData] = useState<OnlineServer | undefined>(
undefined,
undefined
);
const { resolvedTheme } = useTheme();
const [color, setColor] = useState("#ffffff");
@ -375,7 +365,7 @@ export default function ServerList() {
className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " +
"border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " +
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]",
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]"
)}
onClick={() =>
router.push(pageFind(`Server:${server.name}`))
@ -409,7 +399,7 @@ export default function ServerList() {
className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " +
"border-gray-950/[.1] bg-gray-950/[.01] hover:bg-gray-950/[.05] " +
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]",
"dark:border-gray-50/[.1] dark:bg-gray-50/[.10] dark:hover:bg-gray-50/[.15]"
)}
onClick={() => router.push(`/server/${server.name}`)}
>
@ -464,10 +454,15 @@ export default function ServerList() {
Monitor your success
</h1>
<p className="animate-fade-in mb-12 -translate-y-4 text-balance text-lg tracking-tight text-gray-400 opacity-0 [--animation-delay:400ms] md:text-xl">
Ever wondered how a server was doing? MHSF constantly monitors servers
and shows you statistics about how a server is doing at any point of time.
Ever wondered how a server was doing? MHSF constantly monitors
servers and shows you statistics about how a server is doing at
any point of time.
</p>
<ExampleChart />
<br className="mb-8" />
<span className="text-sm flex items-center justify-center mt-32">
<ArrowDown className="mr-2" size={16} /> Check it out below!
</span>
</div>
)}
<br />
@ -555,30 +550,51 @@ export default function ServerList() {
<Separator />
<ClientFadeIn delay={100}>
<StickyTopbar scrollElevation={250} className="p-2">
<Menubar className="mt-3 border rounded shadow">
<MenubarMenu>
<MenubarTrigger>Servers</MenubarTrigger>
<MenubarContent>
<MenubarItem
onSelect={() => events.emit("search-request-event")}
>
Search Servers
<MenubarShortcut className="flex items-center ml-3">
<CommandIcon size={14} />
+Shift+K
</MenubarShortcut>
</MenubarItem>
<MenubarItem
onSelect={() => {
<ServerListInterface
viewProps={{
setPresentationMode,
presentationMode,
selectedProperties,
setSelectedProperties,
hero,
setHero,
padding,
paddingChangerCallback: (v: any) => {
if (am)
toast.warning(
"These settings will not change over reloads because you have account specific options enabled",
{
action: {
label: "Check settings",
onClick: () =>
router.push("/account/settings/options"),
},
}
);
setPadding(v);
},
am,
iprChangerCallback: (v: any) => {
if (am)
toast.warning(
"These settings will not change over reloads because you have account specific options enabled",
{
action: {
label: "Check settings",
onClick: () =>
router.push("/account/settings/options"),
},
}
);
setIPR(v);
},
ipr,
}}
pickRandomServerCallback={() => {
setRandomData(serverList.getRandomServer());
setRandom(true);
}}
>
Pick Random Server
</MenubarItem>
<MenubarSeparator />
<MenubarItem
onSelect={() => {
refreshCallback={() => {
toast.promise(
new Promise((s, e) => {
setLoading(true);
@ -602,11 +618,9 @@ export default function ServerList() {
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach(
(b: { server: string; motd: string }) => {
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
},
);
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
setLoading(false);
@ -618,22 +632,107 @@ export default function ServerList() {
});
}),
{
success: "Succesfully refreshed servers",
loading: "Refreshing...",
success: "Succesfully reloaded servers",
loading: "Reloading...",
error: "Error while refreshing",
},
}
);
}}
>
Refresh
</MenubarItem>
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>Filter</MenubarTrigger>
<MenubarContent className="max-h-[400px] overflow-auto">
<MenubarRadioGroup
onValueChange={(v) => {
linksProps={{
templateFilter,
tagChangerValueCallback: (tag: any) => {
return nameFilters["t-" + tag.docsName];
},
categoryChangerValueCallback: (categorie: any) => {
return nameFilters["c-" + categorie.name];
},
categoryChangerCallback: (categorie: any) => async (b: any) => {
var filt = nameFilters;
filt["c-" + categorie.name] = b;
setNameFilters(filt);
if (b) {
var filt2 = filters;
filt2.push(categorie.condition);
setFilters(filt2);
} else {
var filt2 = filters;
filt2.splice(filt2.indexOf(categorie.condition), 1);
setFilters(filt2);
}
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{
server: string;
motd: string;
}> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
});
});
},
tagChangerCallback: (tag: any) => async (b: any) => {
var filt = nameFilters;
filt["t-" + tag.docsName] = b;
setNameFilters(filt);
if (b) {
var filt2 = filters;
filt2.push(tag.condition);
setFilters(filt2);
} else {
var filt2 = filters;
filt2.splice(filt2.indexOf(tag.condition), 1);
setFilters(filt2);
}
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{
server: string;
motd: string;
}> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({
motd: b.motd,
server: b.name,
});
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
});
});
},
serverSizeChangerValueCallback: () => {
if (nameFilters["smaller-tf"]) {
return "smaller";
}
if (nameFilters["bigger-tf"]) {
return "bigger";
}
return "none";
},
serverSizeChangerCallback: (v: any) => {
toast.promise(
new Promise((g, b) => {
if (v == "smaller") {
@ -668,11 +767,9 @@ export default function ServerList() {
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach(
(b: { server: string; motd: string }) => {
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
},
);
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
g(undefined);
@ -710,11 +807,9 @@ export default function ServerList() {
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach(
(b: { server: string; motd: string }) => {
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
},
);
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
g(undefined);
@ -752,11 +847,9 @@ export default function ServerList() {
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach(
(b: { server: string; motd: string }) => {
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
},
);
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
g(undefined);
@ -768,283 +861,11 @@ export default function ServerList() {
error: "Error while changing filters",
loading: "Changing filters...",
success: "Changed filters!",
},
);
}}
value={(() => {
if (nameFilters["smaller-tf"]) {
return "smaller";
} else if (nameFilters["bigger-tf"]) {
return "bigger";
} else {
return "none";
}
})()}
>
<MenubarRadioItem value="smaller">
<div className="block">
Only allow smaller servers
<br />
<span className="text-sm text-muted-foreground">
Only allow servers that have the player range 7-15,
and cannot <br />
be Always Online.
</span>
</div>
</MenubarRadioItem>
<MenubarRadioItem value="bigger">
<div className="block">
Only allow bigger servers
<br />
<span className="text-sm text-muted-foreground">
Only allow servers with more than 15 players.
</span>
</div>
</MenubarRadioItem>
<MenubarRadioItem value="none">
No/custom requirements
</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator />
<MenubarSub>
<span className="text-sm text-muted-foreground ml-2">
Tags
</span>
</MenubarSub>
{allTags.map((tag) => (
<div key={tag.docsName}>
{tag.docsName && tag.__filter == undefined && (
<MenubarCheckboxItem
disabled={templateFilter && tag.__disab != undefined}
id={tag.docsName}
checked={(() => {
return nameFilters["t-" + tag.docsName];
})()}
onCheckedChange={async (b) => {
var filt = nameFilters;
filt["t-" + tag.docsName] = b;
setNameFilters(filt);
if (b) {
var filt2 = filters;
filt2.push(tag.condition);
setFilters(filt2);
} else {
var filt2 = filters;
filt2.splice(filt2.indexOf(tag.condition), 1);
setFilters(filt2);
}
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{
server: string;
motd: string;
}> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({
motd: b.motd,
server: b.name,
});
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach(
(b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
},
);
setMotdList(updatedSL);
setServers(serverList.currentServers);
});
});
}}
>
<Badge variant={tag.role} className="mr-1">
{tag.docsName}
</Badge>
</MenubarCheckboxItem>
)}
</div>
))}
<MenubarSeparator />
<MenubarSub>
<span className="text-sm text-muted-foreground ml-2">
Categories
</span>
</MenubarSub>
{allCategories.map((categorie) => (
<MenubarCheckboxItem
id={categorie.name}
key={categorie.name}
onCheckedChange={async (b) => {
var filt = nameFilters;
filt["c-" + categorie.name] = b;
setNameFilters(filt);
if (b) {
var filt2 = filters;
filt2.push(categorie.condition);
setFilters(filt2);
} else {
var filt2 = filters;
filt2.splice(filt2.indexOf(categorie.condition), 1);
setFilters(filt2);
}
serverList.editFilters(filters);
serverList.fetchDataAndFilter().then(() => {
serverList.moveListDown();
let stringList: Array<{
server: string;
motd: string;
}> = [];
let obj: any = {};
serverList.currentServers.forEach((b) => {
stringList.push({ motd: b.motd, server: b.name });
});
serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd;
});
setMotdList(updatedSL);
setServers(serverList.currentServers);
});
});
}}
checked={(() => {
return nameFilters["c-" + categorie.name];
})()}
>
<Badge variant={categorie.role} className="mr-1">
{categorie.name}
</Badge>
</MenubarCheckboxItem>
))}
</MenubarContent>
</MenubarMenu>
<MenubarMenu>
<MenubarTrigger>View</MenubarTrigger>
<MenubarContent>
<MenubarSub>
<MenubarSubTrigger>Mode</MenubarSubTrigger>
<MenubarSubContent>
<MenubarRadioGroup
value={presentationMode}
onValueChange={(v) =>
setPresentationMode(v as "grid" | "table")
}
>
<MenubarRadioItem value="grid">Grid</MenubarRadioItem>
<MenubarRadioItem value="table">Table</MenubarRadioItem>
</MenubarRadioGroup>
</MenubarSubContent>
</MenubarSub>
<MenubarSub>
<MenubarSubTrigger disabled={presentationMode === "table"} className={presentationMode === "table" ? "text-muted-foreground" : ""}>
Grid
</MenubarSubTrigger>
<MenubarSubContent>
<MenubarRadioGroup
value={ipr}
onValueChange={(v) => {
if (am)
toast.warning(
"These settings will not change over reloads because you have account specific options enabled",
{
action: {
label: "Check settings",
onClick: () =>
router.push("/account/settings/options"),
},
},
);
setIPR(v);
}}
>
<MenubarRadioItem value="4">
4 items per row
</MenubarRadioItem>
<MenubarRadioItem value="5">
5 items per row
</MenubarRadioItem>
<MenubarRadioItem value="6">
6 items per row
</MenubarRadioItem>
</MenubarRadioGroup>
</MenubarSubContent>
</MenubarSub>
<MenubarSub>
<MenubarSubTrigger>Padding</MenubarSubTrigger>
<MenubarSubContent>
<MenubarRadioGroup
value={padding.toString()}
onValueChange={(v) => {
if (am)
toast.warning(
"These settings will not change over reloads because you have account specific options enabled",
{
action: {
label: "Check settings",
onClick: () =>
router.push("/account/settings/options"),
},
},
);
setPadding(v);
}}
>
<MenubarRadioItem value="0">Default</MenubarRadioItem>
<MenubarSeparator />
<MenubarRadioItem value="15">15px</MenubarRadioItem>
<MenubarRadioItem value="30">30px</MenubarRadioItem>
<MenubarRadioItem value="40">40px</MenubarRadioItem>
<MenubarRadioItem value="60">60px</MenubarRadioItem>
<MenubarRadioItem value="100">100px</MenubarRadioItem>
<MenubarRadioItem value="200">200px</MenubarRadioItem>
</MenubarRadioGroup>
<MenubarSeparator />
<MenubarCheckboxItem
checked={pOS}
onCheckedChange={setpOS}
>
Only use padding on servers
</MenubarCheckboxItem>
</MenubarSubContent>
</MenubarSub>
<MenubarSeparator />
<SignedIn>
<MenubarCheckboxItem
checked={hero}
onCheckedChange={setHero}
>
Show Hero
</MenubarCheckboxItem>
</SignedIn>
<MenubarItem onClick={() => router.push("/docs")}>
View the docs
</MenubarItem>
{am && (
<MenubarItem
onClick={() => router.push("/account/settings")}
className="block"
>
Using saved settings in Preferences
<br />
<span className="text-muted-foreground text-xs">
Your using settings stored on your account, that are not
temporary.
</span>
</MenubarItem>
)}
</MenubarContent>
</MenubarMenu>
</Menubar>
/>
</StickyTopbar>
</ClientFadeIn>
@ -1107,7 +928,7 @@ export default function ServerList() {
onClick={() => {
setTextCopied(true);
clipboard.writeText(
randomData.name + ".mshf.minehut.gg",
randomData.name + ".mshf.minehut.gg"
);
toast.success("Copied!");
setTimeout(() => setTextCopied(false), 1000);
@ -1150,13 +971,9 @@ export default function ServerList() {
}}
loader={<LoadingSpinner className="flex items-center" />}
endMessage={
<p
style={{ textAlign: "center" }}
dangerouslySetInnerHTML={{
__html:
randomText + "<br /> <strong>You've seen it all</strong>",
}}
/>
<p style={{ textAlign: "center" }}>
<NoItems title="You've scrolled to the very bottom!" />
</p>
}
style={{
overflow: "hidden !important",
@ -1219,7 +1036,11 @@ export default function ServerList() {
))}
</div>
)} </> */}
<ServerCard b={b} motd={motdList[b.name]} />
<ServerCard
b={b}
motd={motdList[b.name]}
selectedProperties={selectedProperties}
/>
</>
))}
</div>
@ -1319,7 +1140,7 @@ export default function ServerList() {
className="md:min-w-[128px] md:max-w-[328px] h-[32px] mt-1 ml-2"
onClick={() => {
clipboard.writeText(
c.data.name + ".mshf.minehut.gg",
c.data.name + ".mshf.minehut.gg"
);
toast.success("Copied IP to clipboard");
}}

@ -39,7 +39,6 @@ import { CloudSnow, InfoIcon } from "lucide-react";
import LoggedInPopover from "./LoggedInPopover";
import SignInPopoverButton from "./SignInPopoverButton";
import InfoPopover from "../misc/InfoPopover";
import { snowfallEvents } from "../misc/SnowfallController";
export default function TopBar({ inter }: { inter: string }) {
const clerk = useClerk();
@ -48,16 +47,8 @@ export default function TopBar({ inter }: { inter: string }) {
return (
<>
<SignedOut>
<div className="mt-1 gap-1 grid grid-cols-6">
<div className="mt-1 gap-1 grid grid-cols-5">
<SignInPopoverButton className="col-span-2" variant="outline" />
<Button
size="icon"
variant="ghost"
className="mb-1"
onClick={() => snowfallEvents.emit("toggle")}
>
<CloudSnow className="h-[1.2rem] w-[1.2rem]" />
</Button>
<Popover>
<PopoverTrigger>
<Button size="icon" variant="ghost">
@ -91,7 +82,7 @@ export default function TopBar({ inter }: { inter: string }) {
</div>
</SignedOut>
<SignedIn>
<div className="mt-1 grid grid-cols-5 gap-1">
<div className="mt-1 grid grid-cols-4 gap-1">
<Popover>
<PopoverTrigger>
<Button size="icon" variant="ghost" className="mb-1">
@ -112,14 +103,6 @@ export default function TopBar({ inter }: { inter: string }) {
<LoggedInPopover />
</PopoverContent>
</Popover>
<Button
size="icon"
variant="ghost"
className="mb-1"
onClick={() => snowfallEvents.emit("toggle")}
>
<CloudSnow className="h-[1.2rem] w-[1.2rem]" />
</Button>
<Popover>
<PopoverTrigger>
<Button size="icon" variant="ghost" className="mb-1">

@ -13,6 +13,7 @@ import NextTopLoader from "@/lib/top-loader";
import Link from "next/link";
import BannerContainer from "@/components/feat/BannerContainer";
import { Inter } from "next/font/google";
import useStatus from "@/lib/hooks/use-status";
const inter = Inter({ variable: "--font-inter", subsets: ["latin"] });
export default function LayoutPart({
@ -20,15 +21,22 @@ export default function LayoutPart({
}: {
children: React.ReactNode;
}) {
const { loading, incidents, statusURL } = useStatus();
return (
<>
<BannerContainer
className={"w-screen h-[3rem] border-b fixed backdrop-blur flex z-10"}
className={
"w-screen h-[3rem] border-b grid-cols-3 fixed backdrop-blur z-10 " +
(!loading && (incidents as never as Array<any>).length > 0
? "grid"
: "flex")
}
style={(size: number) => ({
marginTop: `${2 * size}rem`,
})}
>
<div className="items-center me-auto mt-2 pl-7 max-sm:mt-3">
<div className="items-center justify-self-start me-auto mt-2 pl-7 max-sm:mt-3 flex-1">
<Breadcrumb>
<BreadcrumbList>
<Link href="/">
@ -40,7 +48,30 @@ export default function LayoutPart({
</BreadcrumbList>
</Breadcrumb>
</div>
{!loading &&
incidents !== null &&
(incidents as Array<any>).length > 0 && (
<div className="justify-self-center items-center text-center pb-2 pt-2 rounded border my-1 px-2 bg-red-500 text-white">
<strong className="text-sm">
{
(
incidents[0] as {
attributes: { title: string };
}
).attributes.title
}{" "}
</strong>{" "}
<span className="text-sm">- MHSF may be down</span>
<br />
<Link href={"https://" + statusURL} className="text-sm">
View MHSF Status
</Link>
</div>
)}
<div className="justify-self-end">
<TopBar inter={inter.className} />
</div>
</BannerContainer>
<BannerContainer
style={(size: number) => ({

@ -0,0 +1,29 @@
/*
* 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) 2024 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.
*/

@ -1,4 +1,4 @@
"use client";;
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
@ -43,7 +43,7 @@ export function BannerPopover({ server, get }: { server: string; get: any }) {
}
return (
<ColorProvider server={server} fetch={get}>
<ColorProvider server={server} fetchV={get}>
<div>
<span className="text-sm">
All images that are in a web supported format can be used as the

@ -78,7 +78,7 @@ export function DiscordPopover({ server, get }: { server: string; get: any }) {
}, [get]);
return (
<ColorProvider server={server} fetch={get}>
<ColorProvider server={server} fetchV={get}>
<div>
<span className="text-sm">
To embed a Discord server into your server page, first enable the

@ -0,0 +1,200 @@
"use client";
import { CircleAlert, LayoutGrid, List, Phone } from "lucide-react";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
import { Select } from "@radix-ui/react-select";
import {
SelectContent,
SelectGroup,
SelectItem,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "../ui/select";
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
import { SignedIn } from "@clerk/nextjs";
export function DisplaySettings({
presentationMode,
setPresentationMode,
selectedProperties,
setSelectedProperties,
hero,
setHero,
ipr,
am,
iprChangerCallback,
padding,
paddingChangerCallback,
}: any) {
const toggleProperty = (property: string) => {
setSelectedProperties((prev: any) =>
prev.includes(property)
? prev.filter((p: any) => p !== property)
: [...prev, property]
);
};
return (
<div className="w-full space-y-6 bg-background">
<Tabs
defaultValue="cards"
className="w-full"
onValueChange={setPresentationMode}
value={presentationMode}
>
<div className="border-b">
<TabsList className="grid w-full grid-cols-2 bg-background p-0">
<TabsTrigger
value="grid"
className="flex items-center gap-2 py-2.5 px-4 data-[state=active]:bg-background data-[state=active]:shadow-none rounded-none border-r"
>
<LayoutGrid className="h-4 w-4" />
Grid
</TabsTrigger>
<TabsTrigger
value="table"
className="flex items-center gap-2 py-2.5 px-4 data-[state=active]:bg-background data-[state=active]:shadow-none rounded-none"
>
<List className="h-4 w-4" />
Table
</TabsTrigger>
</TabsList>
</div>
<TabsContent value="grid" className="space-y-6 mt-0 ">
<SignedIn>
<div className="flex items-center justify-between pt-5 pb-1 p-4">
<Label htmlFor="set-hero" className="font-normal">
Show hero at the top of the page
</Label>
<Switch id="set-hero" value={hero} onCheckedChange={setHero} />
</div>
</SignedIn>
<Separator />
<div
className={
"flex items-center justify-between py-1 " +
(am ? "border border-orange-500 rounded px-2 mx-2" : "mx-4")
}
>
<Label
htmlFor="grid-columns"
className="font-normal flex items-center"
>
{am && (
<Tooltip>
<TooltipTrigger>
<CircleAlert size={16} className="mr-2 text-orange-500" />
</TooltipTrigger>
<TooltipContent>
If you change this setting, it will take priority over your{" "}
<br />
account settings. These settings will not save over reloads.
</TooltipContent>
</Tooltip>
)}
Grid items p/ row
</Label>
<Select value={ipr} onValueChange={iprChangerCallback}>
<SelectTrigger className="w-[125px]">
<SelectValue placeholder="" id="grid-columns" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="4">4 items</SelectItem>
<SelectItem value="5">5 items</SelectItem>
<SelectItem value="6">6 items</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
<div
className={
"flex items-center justify-between py-1 " +
(am ? "border border-orange-500 rounded px-2 mx-2" : "mx-4")
}
>
<Label htmlFor="padding" className="font-normal flex items-center">
{am && (
<Tooltip>
<TooltipTrigger>
<CircleAlert size={16} className="mr-2 text-orange-500" />
</TooltipTrigger>
<TooltipContent>
If you change this setting, it will take priority over your{" "}
<br />
account settings. These settings will not save over reloads.
</TooltipContent>
</Tooltip>
)}
Padding
</Label>
<Select
value={padding.toString()}
onValueChange={paddingChangerCallback}
>
<SelectTrigger className="w-[125px]">
<SelectValue placeholder="" id="padding" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="0">Default</SelectItem>
<SelectSeparator />
<SelectItem value="15">15px</SelectItem>
<SelectItem value="30">30px</SelectItem>
<SelectItem value="40">40px</SelectItem>
<SelectItem value="60">60px</SelectItem>
<SelectItem value="100">100px</SelectItem>
<SelectItem value="200">200px</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
<Separator />
<div className="space-y-4 pt-1 p-4">
<h3 className="text-xs uppercase text-gray-500">
Display Properties
</h3>
<div className="flex flex-wrap gap-2">
{["Author", "MOTD", "Tags", "Players Online", "Actions"].map(
(property) => (
<button
key={property}
onClick={() => toggleProperty(property)}
className={cn(
"px-3 py-1.5 text-sm rounded-md transition-colors",
selectedProperties.includes(property)
? "bg-secondary text-secondary-foreground border"
: "hover:bg-muted/80"
)}
>
{property}
</button>
)
)}
</div>
</div>
</TabsContent>
<TabsContent value="table" className="mt-0 px-4 my-4">
<Alert className="md:hidden">
<Phone className="h-4 w-4" />
<AlertTitle>Table mode isn't optimized for mobile</AlertTitle>
<AlertDescription>
At this time, we do not recommend using table mode on mobile.
</AlertDescription>
</Alert>
</TabsContent>
</Tabs>
</div>
);
}

@ -0,0 +1,134 @@
/*
* 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) 2024 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 {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Badge } from "../ui/badge";
import { allCategories, allTags } from "@/config/tags";
export function FilterMenu({
serverSizeChangerCallback,
serverSizeChangerValueCallback,
templateFilter,
tagChangerValueCallback,
tagChangerCallback,
categoryChangerCallback,
categoryChangerValueCallback,
children,
}: {
children: React.ReactNode;
serverSizeChangerCallback: any;
serverSizeChangerValueCallback: any;
templateFilter: any;
tagChangerValueCallback: any;
tagChangerCallback: any;
categoryChangerCallback: any;
categoryChangerValueCallback: any;
}) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
<DropdownMenuContent className="max-h-[400px] overflow-auto">
<DropdownMenuRadioGroup
onValueChange={serverSizeChangerCallback}
value={serverSizeChangerValueCallback()}
>
<DropdownMenuRadioItem value="smaller">
<div className="block">
Only allow smaller servers
<br />
<span className="text-sm text-muted-foreground">
Only allow servers that have the player range 7-15, and cannot{" "}
<br />
be Always Online.
</span>
</div>
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="bigger">
<div className="block">
Only allow bigger servers
<br />
<span className="text-sm text-muted-foreground">
Only allow servers with more than 15 players.
</span>
</div>
</DropdownMenuRadioItem>
<DropdownMenuRadioItem value="none">
No/custom requirements
</DropdownMenuRadioItem>
</DropdownMenuRadioGroup>
<DropdownMenuSeparator />
<DropdownMenuSub>
<span className="text-sm text-muted-foreground ml-2">Tags</span>
</DropdownMenuSub>
{allTags.map((tag) => (
<div key={tag.docsName}>
{tag.docsName && tag.__filter == undefined && (
<DropdownMenuCheckboxItem
disabled={templateFilter && tag.__disab != undefined}
id={tag.docsName}
checked={tagChangerValueCallback(tag)}
onCheckedChange={tagChangerCallback(tag)}
>
<Badge variant={tag.role} className="mr-1">
{tag.docsName}
</Badge>
</DropdownMenuCheckboxItem>
)}
</div>
))}
<DropdownMenuSeparator />
<DropdownMenuSub>
<span className="text-sm text-muted-foreground ml-2">Categories</span>
</DropdownMenuSub>
{allCategories.map((categorie) => (
<DropdownMenuCheckboxItem
id={categorie.name}
key={categorie.name}
onCheckedChange={categoryChangerCallback(categorie)}
checked={categoryChangerValueCallback(categorie)}
>
<Badge variant={categorie.role} className="mr-1">
{categorie.name}
</Badge>
</DropdownMenuCheckboxItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}

@ -0,0 +1,74 @@
/*
* 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) 2024 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 { Book } from "lucide-react";
import { BrandingColorfulIcon } from "../Icon";
import { Button } from "../ui/button";
import Github from "../ui/github";
import { Separator } from "../ui/separator";
import Link from "next/link";
import { useTheme } from "next-themes";
export default function Footer() {
const { resolvedTheme } = useTheme();
return (
<footer>
<Separator />
<p className="px-4 pt-8 pb-2">
<span className="text-xl font-bold text-muted-foreground pb-12 flex items-center">
<BrandingColorfulIcon className="w-12 h-12 mr-2" />
MHSF
</span>
<p>© {new Date().getFullYear()} dvelo</p>
<strong className="text-sm">
MHSF is built on open-source technologies and is not endorsed by or
affiliated with GamerSafer or its subsidiaries.{" "}
</strong>
<br />
<span className="flex items-center">
<Link href="https://github.com/DeveloLongScript/MHSF">
<Button variant="ghost" size="icon">
<Github fill={resolvedTheme === "dark" ? "white" : "black"} />
</Button>
</Link>
<Link href="/docs">
<Button variant="ghost" size="icon">
<Book size={14} />
</Button>
</Link>
</span>
</p>
</footer>
);
}

@ -29,44 +29,107 @@
*/
"use client";
import { useState } from "react";
import { TextCopyComp } from "./TextCopyComp";
import { ChevronDown, ChevronUp } from "lucide-react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
import { Badge } from "../ui/badge";
import Link from "next/link";
import { Alert, AlertDescription, AlertTitle } from "../ui/alert";
import { ServerOff } from "lucide-react";
export function ShowInfo() {
const [open, setOpen] = useState(false);
return (
<div>
{open == false && (
<div
className="text-blue-500 flex items-center cursor-pointer"
onClick={() => setOpen(true)}
<br />
Choose a method:
<Tabs defaultValue="preview" className="relative mr-auto w-full">
<div className="flex items-center justify-between pb-3">
<TabsList className="w-full justify-start rounded-none border-b bg-transparent p-0">
<TabsTrigger
value="coreboxx"
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
>
More info <ChevronDown size={16} className="ml-2" />
CoreBoxx <Badge className="ml-3">Recommended</Badge>
</TabsTrigger>
<TabsTrigger
value="mhsfpv"
className="relative h-9 rounded-none border-b-2 border-b-transparent bg-transparent px-4 pb-3 pt-2 font-semibold text-muted-foreground shadow-none transition-none data-[state=active]:border-b-primary data-[state=active]:text-foreground data-[state=active]:shadow-none"
>
MHSFPV
</TabsTrigger>
</TabsList>
</div>
)}
{open == true && (
<>
<TabsContent value="coreboxx">
<p>
By claiming your account, you can add Markdown descriptions and{" "}
custom color schemes to your server (and more), making it stand out.
To get started, join the server below on your Minecraft account.
Enter the code in chat in the website, and you will link your
account. You may need to go into the lobby and start the server.
<Link href="/server/CoreBoxx" className="underline">
CoreBoxx
</Link>{" "}
has partnered with us to have an integrated account linking feature,
which is also open all day.
</p>
<br />
<TextCopyComp />
<p className="py-1">
<code className="border rounded-full bg-muted h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center">
1
</code>
<span className="ml-[2.25rem] pt-0.5 grid grid-rows-2">
<span>Join CoreBoxx</span>
<code className="border rounded p-2">CoreBoxx.minehut.gg</code>
</span>
</p>
<p className="py-1">
<code className="border rounded-full bg-muted h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center">
2
</code>
<span className="ml-[2.25rem] pt-0.5 grid">
<span>
Link your account using <code>/mhsf</code>
</span>
</span>
</p>
<p className="py-1">
<code className="border rounded-full bg-muted h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center">
3
</code>
<span className="ml-[2.25rem] pt-0.5 grid">
<span>Input the code returned below</span>
</span>
</p>
</TabsContent>
<TabsContent value="mhsfpv">
<Alert>
<ServerOff className="h-4 w-4" />
<AlertTitle>Server isn't online all day</AlertTitle>
<AlertDescription>
While joining MHSFPV, you may need to go into the lobby to start
the server to then join.
</AlertDescription>
</Alert>
<br />
<p>
MHSFPV is a Minehut server dedicated to linking your account on
MHSF.
</p>
<br />
<div
className="text-blue-500 flex items-center cursor-pointer"
onClick={() => setOpen(false)}
>
Less info <ChevronUp size={16} className="ml-2" />
</div>
</>
)}
<p className="py-1">
<code className="border rounded-full bg-muted h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center">
1
</code>
<span className="ml-[2.25rem] pt-0.5 grid grid-rows-2">
<span>Join MHSFPV</span>
<code className="border rounded p-2">MHSFPV.minehut.gg</code>
</span>
</p>
<p className="py-1">
<code className="border rounded-full bg-muted h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center">
2
</code>
<span className="ml-[2.25rem] pt-0.5 grid">
<span>Input the code in chat below</span>
</span>
</p>
</TabsContent>
</Tabs>
</div>
);
}

@ -83,15 +83,20 @@ export default function CodeDialog({
});
async function onSubmit(data: z.infer<typeof FormSchema>) {
const { code } = data;
const playerName = await toast.promise(
new Promise(async (g, b) => {
const response = await linkMCAccount(code);
if (response == undefined) {
toast.promise(
new Promise<void>((g, b) => {
linkMCAccount(code).then((c) => {
console.log(c);
if (c === undefined) {
b();
return;
}
g(response);
g();
setName(c);
setDialog(true);
setLinked(true);
});
}),
{
loading: "Linking account..",
@ -99,9 +104,6 @@ export default function CodeDialog({
success: "Linked account!",
}
);
setName(playerName as string);
setDialog(true);
setLinked(true);
}
return (
@ -156,9 +158,9 @@ export default function CodeDialog({
<DialogTitle>You have linked your account!</DialogTitle>
<Confetti className="absolute left-0 top-0 z-0 size-full" />
<DialogDescription>
You've successfully linked your account, <strong>{name}</strong>!
Enjoy adding banners, custom accent colors, and other things to
your server page.
You've successfully linked your account, {name}! Enjoy adding
banners, custom accent colors, and other things to your server
page.
</DialogDescription>
</DialogHeader>
</DialogContent>

@ -31,7 +31,7 @@
import { DatabaseZap } from "lucide-react";
export default function NoItems() {
export default function NoItems({ title }: { title?: string }) {
return (
<>
<div className="flex flex-col items-center justify-center p-4 pt-10">
@ -40,7 +40,9 @@ export default function NoItems() {
size={32}
/>
<p className="text-xl text-gray-600 mt-2">
Huh, we tried to find something, but nothing was found.
{title
? title
: "Huh, we tried to find something, but nothing was found."}
</p>
</div>
</>

@ -0,0 +1,158 @@
/*
* 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) 2024 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 { Input } from "@/components/ui/input";
import {
ChevronDown,
Dices,
ListRestart,
MoreVertical,
Search,
SquareTerminal,
} from "lucide-react";
import { FilterMenu } from "./FilterMenu";
import { DisplaySettings } from "./DisplaySettings";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import events from "@/lib/commandEvent";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
export default function ServerListInterface({
linksProps,
viewProps,
refreshCallback,
pickRandomServerCallback,
}: {
linksProps: {
serverSizeChangerCallback: any;
serverSizeChangerValueCallback: any;
templateFilter: any;
tagChangerValueCallback: any;
tagChangerCallback: any;
categoryChangerCallback: any;
categoryChangerValueCallback: any;
};
viewProps: {
setPresentationMode: any;
presentationMode: any;
selectedProperties: any;
setSelectedProperties: any;
hero: any;
setHero: any;
iprChangerCallback: any;
ipr: any;
am: any;
padding: any;
paddingChangerCallback: any;
};
refreshCallback: any;
pickRandomServerCallback: any;
}) {
return (
<div className="w-full mt-6">
<div className="flex flex-col sm:flex-row items-stretch sm:items-center sm:justify-between gap-2 sm:gap-8">
<div className="grid grid-cols-2 sm:flex sm:flex-row items-stretch sm:items-center gap-2">
<FilterMenu {...linksProps}>
<Button variant="outline" className="w-full gap-2">
Filter
<ChevronDown className="h-4 w-4" />
</Button>
</FilterMenu>
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="w-full gap-2">
Display
<ChevronDown className="h-4 w-4" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-80 p-0">
<DisplaySettings {...viewProps} />
</PopoverContent>
</Popover>
</div>
<div className="flex items-center gap-2 w-full sm:w-auto">
<div className="relative flex-1">
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
type="search"
placeholder="Search..."
className="pl-8"
value=""
onClick={(c) => {
c.preventDefault();
events.emit("search-request-event");
}}
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="shrink-0">
<MoreVertical className="h-4 w-4" />
<span className="sr-only">More options</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem
className="dark:text-white py-2"
onSelect={refreshCallback}
>
<ListRestart />
Reload Servers
</DropdownMenuItem>
<DropdownMenuItem
className="dark:text-white py-2"
onSelect={pickRandomServerCallback}
>
<Dices />
Pick Random Server
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="dark:text-white py-2"
onSelect={() => events.emit("cmd-event")}
>
<SquareTerminal />
Show Command Bar
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</div>
);
}

@ -0,0 +1,59 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = "Alert"
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
))
AlertTitle.displayName = "AlertTitle"
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
))
AlertDescription.displayName = "AlertDescription"
export { Alert, AlertTitle, AlertDescription }

@ -1,10 +1,14 @@
import { useTheme } from "next-themes";
import type { SVGProps } from "react";
const Github = (props: SVGProps<SVGSVGElement>) => (
const Github = (props: SVGProps<SVGSVGElement>) => {
const { resolvedTheme } = useTheme();
return (
<svg
viewBox="0 0 256 250"
width="1em"
height="1em"
fill="#fff"
fill={resolvedTheme === "dark" ? "#fff" : "#24292f"}
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
{...props}
@ -12,4 +16,5 @@ const Github = (props: SVGProps<SVGSVGElement>) => (
<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>
);
};
export default Github;

@ -99,6 +99,15 @@ export const allTags: Array<{
role: "secondary",
__filter: true,
},
{
name: async () => "Partner",
condition: async (s) => s.name === "CoreBoxx",
tooltipDesc: "This server is a partner with MHSF.",
docsName: "Partner",
htmlDocs: "This tag represents that this server is a partner with MHSF.",
primary: true,
role: "purple",
},
// deprecated
/**{
name: async () => "Velocity",
@ -280,12 +289,11 @@ export const allCategories: Array<{
async function requestServer(s: OnlineServer): Promise<ServerResponse> {
if (serverCache[s.name] === undefined) {
const re = await fetch(
"https://api.minehut.com/server/" + s.name + "?byName=true",
"https://api.minehut.com/server/" + s.name + "?byName=true"
);
const json = await re.json();
serverCache[s.name] = json.server;
return json.server;
} else {
}
return serverCache[s.name];
}
}

@ -30,6 +30,9 @@
"use client";
import A from "@/components/misc/Link";
import { Button } from "@/components/ui/button";
import Github from "@/components/ui/github";
import Link from "next/link";
import type { ReactNode } from "react";
const User = ({ user }: { user: string }) => (
@ -41,13 +44,22 @@ const User = ({ user }: { user: string }) => (
const FeatureList = ({
features,
title,
github,
}: {
features: (string | ReactNode)[];
github?: string;
title: ReactNode;
}) => {
return (
<ul>
{title}
{github && (
<Link href={github}>
<Button variant="ghost" size="sm">
<Github className="mr-1" /> Release
</Button>
</Link>
)}
{features.map((feature, i) => (
<li key={i}> {feature}</li>
))}
@ -55,13 +67,42 @@ const FeatureList = ({
);
};
export const version = "1.6.50";
export const version = "1.7.0";
export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
{
id: "38ufajf8efajwj3njdaisef",
name: "v1.7",
changelog: (
<FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.7"
features={[
"Partnered with CoreBoxx!",
"You can now link your Minecraft account on CoreBoxx! (check out CB 3.0.15)",
"Revamped the server finding controls",
"Fixed various bugs",
"Made banners a different style",
"Made Discord embed not inside a card",
"Added incorrect server capitalization detection",
"Made the MOTD area slightly bigger",
"New footer",
"Added padding for settings page",
"Added new table mode",
"Added new button for GitHub release on changelog",
]}
title={
<strong className="flex items-center">
Version 1.7 (January 18th 2025)
</strong>
}
/>
),
},
{
id: "dut6hx3f2paswzjve4yg9r",
name: "v1.6.5",
changelog: (
<FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.6.5"
features={[
"New MOTD engine that is over 3,000% faster, runs client-side, and doesn't need any requests to run.",
"Fixed issue where GitHub link was broken if you were signed-out",
@ -80,6 +121,7 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
name: "v1.6.0",
changelog: (
<FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.6"
features={[
"Completely redid top of server view",
"Favorite counts are now prominent on the server view",
@ -102,6 +144,7 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
name: "v1.5.0",
changelog: (
<FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.5"
features={[
"New embeds",
"More mobile friendly elements",
@ -121,6 +164,7 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
name: "v1.4.5",
changelog: (
<FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.4.5"
features={["Add server icons"]}
title={
<strong className="flex items-center">
@ -135,6 +179,7 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
name: "v1.4.0",
changelog: (
<FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.4"
features={[
"Revamped documentation",
"Revamped changelog UI",

@ -290,7 +290,6 @@ export async function unlinkMCAccount(): Promise<boolean> {
const response = await fetch(
connector(`/account-linking/unlink-account`, { version: 0 }),
{
method: "POST",
headers: {
"Content-Type": "application/json",
},

@ -28,32 +28,22 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
"use client";
import { CommandEvents } from "@/lib/commandEvent";
import { useEffect, useState } from "react";
import Snowfall from "react-snowfall";
export const snowfallEvents = new CommandEvents();
export default function SnowfallController() {
const [visible, setVisible] = useState(true);
export default function useStatus() {
const [loading, setLoading] = useState(true);
const [incidents, setIncidents] = useState(null);
const [statusURL, setStatusURL] = useState(null);
useEffect(() => {
snowfallEvents.on("toggle", () => {
setVisible(!visible);
fetch("/api/v1/get-status")
.then((c) => c.json())
.then((d) => {
setLoading(false);
setIncidents(d.incidents);
setStatusURL(d.url);
});
}, [visible]);
}, []);
return (
<>
{visible && (
<Snowfall
style={{
position: "fixed",
width: "100vw",
height: "100vh",
}}
/>
)}
</>
);
return { loading, incidents, statusURL };
}

@ -61,7 +61,10 @@ export default async function handler(
const collection = db.collection("owned-servers");
if ((await collection.findOne({ server: server })) == undefined) {
const mh = await fetch("https://api.minehut.com/servers", {
const mh = await fetch(
process.env.MHSF_BACKEND_API_LOCATION ??
"https://api.minehut.com/servers",
{
headers: {
accept: "*/*",
"accept-language": Math.random().toString(),
@ -74,10 +77,12 @@ export default async function handler(
"sec-fetch-site": "cross-site",
Referer: "http://localhost:3000/",
"Referrer-Policy": "strict-origin-when-cross-origin",
Authentication: `MHSF-Backend-Server ${process.env.MHSF_BACKEND_API_LOCATION ? process.env.MHSF_BACKEND_SECRET : "Sorry Minehut Devs."}`,
},
body: null,
method: "GET",
});
}
);
const servers: Array<OnlineServer> = (await mh.json()).servers;
servers.forEach(async (c, i) => {

@ -46,13 +46,13 @@ export default async function handler(
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const users = db.collection("claimed-users");
const user = await (await clerkClient()).users.getUser(userId);
if (user.publicMetadata.player == undefined) {
if ((await users.find({ userId }).toArray()).length === 0) {
res.status(400).send({ result: "Hasn't linked yet!" });
return;
}
await users.findOneAndDelete({ player: user.publicMetadata.player });
await users.findOneAndDelete({ userId });
const user = await (await clerkClient()).users.getUser(userId);
await (
await clerkClient()
).users.updateUserMetadata(userId, {

@ -0,0 +1,61 @@
/*
* 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) 2024 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 { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const betterStackResult = await fetch(
`https://uptime.betterstack.com/api/v2/status-pages/${process.env.BS_STATUS_PAGE}/status-reports`,
{ headers: { Authorization: `Bearer ${process.env.BS_TOKEN}` } }
);
const betterStackURL = await fetch(
`https://uptime.betterstack.com/api/v2/status-pages/${process.env.BS_STATUS_PAGE}`,
{ headers: { Authorization: `Bearer ${process.env.BS_TOKEN}` } }
);
const result = await betterStackResult.json();
const url = await betterStackURL.json();
const filtered = result.data.filter(
(c: any) =>
c.attributes.ends_at === null &&
c.attributes.affected_resources.filter(
(v: any) =>
v.status_page_resource_id === process.env.BS_STATUS_MAIN_WEBSITE
).length > 0
);
res.status(200).send({
url: url.data.attributes.custom_domain,
incidents: filtered,
});
}

@ -373,6 +373,11 @@
ts-pattern "^4.3.0"
type-fest "^3.12.0"
"@corex/deepmerge@^4.0.43":
version "4.0.43"
resolved "https://registry.yarnpkg.com/@corex/deepmerge/-/deepmerge-4.0.43.tgz#9bd42559ebb41cc5a7fb7cfeea5f231c20977dca"
integrity sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==
"@discordjs/builders@^1.8.2":
version "1.8.2"
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.8.2.tgz#535d970331ee40f20dec9ef8079e43092f323ce9"
@ -841,6 +846,11 @@
resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.10.tgz#1d3178340028ced2d679f84140877db4f420333c"
integrity sha512-dZIu93Bf5LUtluBXIv4woQw2cZVZ2DJTjax5/5DOs3lzEOeKLy7GxRSr4caK9/SCPdaW6bCgpye6+n4Dh9oJPw==
"@next/env@^13.4.3":
version "13.5.8"
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.5.8.tgz#404d3b3e5881b6a0510500c6cc97e3589a2e6371"
integrity sha512-YmiG58BqyZ2FjrF2+5uZExL2BrLr8RTQzLXNDJ8pJr0O+rPlOeDPXp1p1/4OrR3avDidzZo3D8QO2cuDv1KCkw==
"@next/eslint-plugin-next@14.2.3":
version "14.2.3"
resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz"
@ -3224,9 +3234,9 @@ cross-fetch@^4.0.0:
node-fetch "^2.6.12"
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
@ -6326,7 +6336,7 @@ minimessage-js@^1.1.3:
resolved "https://registry.yarnpkg.com/minimessage-js/-/minimessage-js-1.1.3.tgz#ded53108d686e681cfb2071c2db60b6ae2da60db"
integrity sha512-dP7O/JVfRwMwFl/8KQALtKa+Vo4Za3ihgcSLVB6X3k/fjZWhA8SDQBJ1xUZu0NB6mslkzrIE1Xo+EHYcSXGtQw==
minimist@^1.2.0, minimist@^1.2.6:
minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8:
version "1.2.8"
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@ -6443,6 +6453,16 @@ next-css-obfuscator@^2.2.16:
recoverable-random "^1.0.5"
yargs "^17.7.2"
next-sitemap@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/next-sitemap/-/next-sitemap-4.2.3.tgz#5db3f650351a51e84b9fd6b58c5af2f9257b5058"
integrity sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==
dependencies:
"@corex/deepmerge" "^4.0.43"
"@next/env" "^13.4.3"
fast-glob "^3.2.12"
minimist "^1.2.8"
next-themes@^0.4.3:
version "0.4.3"
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.3.tgz#ea54552d5986936d177eed393ea50b658ae44800"