Compare commits

..

1 Commits

42 changed files with 1904 additions and 3308 deletions

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

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

@ -1,17 +0,0 @@
# 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.

@ -1,16 +0,0 @@
{
"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"
}
}

@ -1,27 +0,0 @@
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");
});

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

@ -1,594 +0,0 @@
# 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==

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

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

@ -40,7 +40,7 @@ export default function RootLayout({
const pathname = usePathname(); const pathname = usePathname();
return ( return (
<span className="pt-[48px] "> <span className="pt-[48px]">
<Sidebar curPage={pathname as string}>{children}</Sidebar> <Sidebar curPage={pathname as string}>{children}</Sidebar>
</span> </span>
); );

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

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

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

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

@ -29,48 +29,28 @@
*/ */
"use client"; "use client";
import { getCustomization } from "@/lib/api"; import { getCustomization } from "@/lib/api";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import "@/themes.css"; import "@/themes.css";
import { toast } from "sonner";
import { useRouter } from "@/lib/useRouter";
import { useEffectOnce } from "@/lib/useEffectOnce";
export default function ColorProvider({ export default function ColorProvider({
server, server,
children, children,
fetchV, fetch,
}: { }: {
server: string; server: string;
children: any; children: any;
fetchV?: any; fetch?: any;
}) { }) {
const [color, setColor] = useState("zinc"); 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(() => { useEffect(() => {
if (!fetchV) if (!fetch)
getCustomization(server).then((v) => getCustomization(server).then((v) =>
setColor(v != null ? v.colorScheme : "zinc") setColor(v != null ? v.colorScheme : "zinc")
); );
else setColor(fetchV.colorScheme); else setColor(fetch.colorScheme);
}, []); }, []);
return <div className={`theme-${color}`}>{children}</div>; return <div className={`theme-${color}`}>{children}</div>;

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

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

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

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

@ -44,16 +44,34 @@ import {
AllCommunityModule, AllCommunityModule,
ModuleRegistry, ModuleRegistry,
colorSchemeDarkBlue, colorSchemeDarkBlue,
colorSchemeDarkWarm,
colorSchemeLightCold,
colorSchemeLightWarm, colorSchemeLightWarm,
themeQuartz, themeQuartz,
} from "ag-grid-community"; } 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 { Separator } from "@/components/ui/separator";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { allTags } from "@/config/tags"; import { allCategories, allTags } from "@/config/tags";
import events from "@/lib/commandEvent";
import ServersList from "@/lib/list"; import ServersList from "@/lib/list";
import { OnlineServer } from "@/lib/types/mh-server"; import { OnlineServer } from "@/lib/types/mh-server";
import useClipboard from "@/lib/useClipboard"; import useClipboard from "@/lib/useClipboard";
@ -63,7 +81,6 @@ import { cn } from "@/lib/utils";
import { SignedIn, SignedOut, useUser } from "@clerk/nextjs"; import { SignedIn, SignedOut, useUser } from "@clerk/nextjs";
import { ChatBubbleIcon, InputIcon } from "@radix-ui/react-icons"; import { ChatBubbleIcon, InputIcon } from "@radix-ui/react-icons";
import { import {
ArrowDown,
ArrowDownZA, ArrowDownZA,
Check, Check,
CircleUser, CircleUser,
@ -76,6 +93,7 @@ import {
Sun, Sun,
XIcon, XIcon,
} from "lucide-react"; } from "lucide-react";
import { CommandIcon } from "lucide-react";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import Link from "next/link"; import Link from "next/link";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
@ -94,13 +112,12 @@ import { pageFind } from "./misc/Link";
import { Badge } from "./ui/badge"; import { Badge } from "./ui/badge";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { Skeleton } from "./ui/skeleton"; import { Skeleton } from "./ui/skeleton";
import { affiliates } from "@/config/affiliates";
import { LoadingSpinner } from "./ui/loading-spinner"; import { LoadingSpinner } from "./ui/loading-spinner";
import StickyTopbar from "./misc/StickyTopbar"; import StickyTopbar from "./misc/StickyTopbar";
import { HoverCard } from "@radix-ui/react-hover-card"; import { HoverCard } from "@radix-ui/react-hover-card";
import { HoverCardTrigger } from "./ui/hover-card"; import { HoverCardTrigger } from "./ui/hover-card";
import { ExampleChart } from "./charts/ExampleChart"; import { ExampleChart } from "./charts/ExampleChart";
import ServerListInterface from "./misc/ServerListInterface";
import NoItems from "./misc/NoItems";
// ag-grid // ag-grid
ModuleRegistry.registerModules([AllCommunityModule]); ModuleRegistry.registerModules([AllCommunityModule]);
@ -167,23 +184,16 @@ export default function ServerList() {
const router = useRouter(); const router = useRouter();
const { user, isSignedIn } = useUser(); const { user, isSignedIn } = useUser();
const [pOS, setpOS] = useState(false); const [pOS, setpOS] = useState(false);
const [selectedProperties, setSelectedProperties] = useState<string[]>([
"Author",
"MOTD",
"Tags",
"Players Online",
"Actions",
]);
const [ipr, setIPR] = useState<string>("4"); const [ipr, setIPR] = useState<string>("4");
const [presentationMode, setPresentationMode] = useState<"table" | "grid">( const [presentationMode, setPresentationMode] = useState<"table" | "grid">(
"grid" "grid",
); );
const [am, setAM] = useState(false); const [am, setAM] = useState(false);
const [filters, setFilters] = useState< const [filters, setFilters] = useState<
Array<(server: OnlineServer) => Promise<boolean>> Array<(server: OnlineServer) => Promise<boolean>>
>([]); >([]);
const [randomData, setRandomData] = useState<OnlineServer | undefined>( const [randomData, setRandomData] = useState<OnlineServer | undefined>(
undefined undefined,
); );
const { resolvedTheme } = useTheme(); const { resolvedTheme } = useTheme();
const [color, setColor] = useState("#ffffff"); const [color, setColor] = useState("#ffffff");
@ -365,7 +375,7 @@ export default function ServerList() {
className={cn( className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " + "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] " + "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={() => onClick={() =>
router.push(pageFind(`Server:${server.name}`)) router.push(pageFind(`Server:${server.name}`))
@ -399,7 +409,7 @@ export default function ServerList() {
className={cn( className={cn(
"relative w-64 cursor-pointer overflow-hidden rounded-xl border no-underline " + "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] " + "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}`)} onClick={() => router.push(`/server/${server.name}`)}
> >
@ -454,15 +464,10 @@ export default function ServerList() {
Monitor your success Monitor your success
</h1> </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"> <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 Ever wondered how a server was doing? MHSF constantly monitors servers
servers and shows you statistics about how a server is doing at and shows you statistics about how a server is doing at any point of time.
any point of time.
</p> </p>
<ExampleChart /> <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> </div>
)} )}
<br /> <br />
@ -550,51 +555,30 @@ export default function ServerList() {
<Separator /> <Separator />
<ClientFadeIn delay={100}> <ClientFadeIn delay={100}>
<StickyTopbar scrollElevation={250} className="p-2"> <StickyTopbar scrollElevation={250} className="p-2">
<ServerListInterface <Menubar className="mt-3 border rounded shadow">
viewProps={{ <MenubarMenu>
setPresentationMode, <MenubarTrigger>Servers</MenubarTrigger>
presentationMode, <MenubarContent>
selectedProperties, <MenubarItem
setSelectedProperties, onSelect={() => events.emit("search-request-event")}
hero, >
setHero, Search Servers
padding, <MenubarShortcut className="flex items-center ml-3">
paddingChangerCallback: (v: any) => { <CommandIcon size={14} />
if (am) +Shift+K
toast.warning( </MenubarShortcut>
"These settings will not change over reloads because you have account specific options enabled", </MenubarItem>
{ <MenubarItem
action: { onSelect={() => {
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()); setRandomData(serverList.getRandomServer());
setRandom(true); setRandom(true);
}} }}
refreshCallback={() => { >
Pick Random Server
</MenubarItem>
<MenubarSeparator />
<MenubarItem
onSelect={() => {
toast.promise( toast.promise(
new Promise((s, e) => { new Promise((s, e) => {
setLoading(true); setLoading(true);
@ -618,9 +602,11 @@ export default function ServerList() {
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList; var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => { c.forEach(
(b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}); },
);
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
setLoading(false); setLoading(false);
@ -632,107 +618,22 @@ export default function ServerList() {
}); });
}), }),
{ {
success: "Succesfully reloaded servers", success: "Succesfully refreshed servers",
loading: "Reloading...", loading: "Refreshing...",
error: "Error while refreshing", error: "Error while refreshing",
} },
); );
}} }}
linksProps={{ >
templateFilter, Refresh
tagChangerValueCallback: (tag: any) => { </MenubarItem>
return nameFilters["t-" + tag.docsName]; </MenubarContent>
}, </MenubarMenu>
categoryChangerValueCallback: (categorie: any) => { <MenubarMenu>
return nameFilters["c-" + categorie.name]; <MenubarTrigger>Filter</MenubarTrigger>
}, <MenubarContent className="max-h-[400px] overflow-auto">
categoryChangerCallback: (categorie: any) => async (b: any) => { <MenubarRadioGroup
var filt = nameFilters; onValueChange={(v) => {
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( toast.promise(
new Promise((g, b) => { new Promise((g, b) => {
if (v == "smaller") { if (v == "smaller") {
@ -767,9 +668,11 @@ export default function ServerList() {
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList; var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => { c.forEach(
(b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}); },
);
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
g(undefined); g(undefined);
@ -807,9 +710,11 @@ export default function ServerList() {
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList; var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => { c.forEach(
(b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}); },
);
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
g(undefined); g(undefined);
@ -847,9 +752,11 @@ export default function ServerList() {
serverList.getMOTDs(stringList).then((c) => { serverList.getMOTDs(stringList).then((c) => {
var updatedSL = motdList; var updatedSL = motdList;
c.forEach((b: { server: string; motd: string }) => { c.forEach(
(b: { server: string; motd: string }) => {
updatedSL[b.server] = b.motd; updatedSL[b.server] = b.motd;
}); },
);
setMotdList(updatedSL); setMotdList(updatedSL);
setServers(serverList.currentServers); setServers(serverList.currentServers);
g(undefined); g(undefined);
@ -861,11 +768,283 @@ export default function ServerList() {
error: "Error while changing filters", error: "Error while changing filters",
loading: "Changing filters...", loading: "Changing filters...",
success: "Changed 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> </StickyTopbar>
</ClientFadeIn> </ClientFadeIn>
@ -928,7 +1107,7 @@ export default function ServerList() {
onClick={() => { onClick={() => {
setTextCopied(true); setTextCopied(true);
clipboard.writeText( clipboard.writeText(
randomData.name + ".mshf.minehut.gg" randomData.name + ".mshf.minehut.gg",
); );
toast.success("Copied!"); toast.success("Copied!");
setTimeout(() => setTextCopied(false), 1000); setTimeout(() => setTextCopied(false), 1000);
@ -971,9 +1150,13 @@ export default function ServerList() {
}} }}
loader={<LoadingSpinner className="flex items-center" />} loader={<LoadingSpinner className="flex items-center" />}
endMessage={ endMessage={
<p style={{ textAlign: "center" }}> <p
<NoItems title="You've scrolled to the very bottom!" /> style={{ textAlign: "center" }}
</p> dangerouslySetInnerHTML={{
__html:
randomText + "<br /> <strong>You've seen it all</strong>",
}}
/>
} }
style={{ style={{
overflow: "hidden !important", overflow: "hidden !important",
@ -1036,11 +1219,7 @@ export default function ServerList() {
))} ))}
</div> </div>
)} </> */} )} </> */}
<ServerCard <ServerCard b={b} motd={motdList[b.name]} />
b={b}
motd={motdList[b.name]}
selectedProperties={selectedProperties}
/>
</> </>
))} ))}
</div> </div>
@ -1140,7 +1319,7 @@ export default function ServerList() {
className="md:min-w-[128px] md:max-w-[328px] h-[32px] mt-1 ml-2" className="md:min-w-[128px] md:max-w-[328px] h-[32px] mt-1 ml-2"
onClick={() => { onClick={() => {
clipboard.writeText( clipboard.writeText(
c.data.name + ".mshf.minehut.gg" c.data.name + ".mshf.minehut.gg",
); );
toast.success("Copied IP to clipboard"); toast.success("Copied IP to clipboard");
}} }}

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

@ -13,7 +13,6 @@ import NextTopLoader from "@/lib/top-loader";
import Link from "next/link"; import Link from "next/link";
import BannerContainer from "@/components/feat/BannerContainer"; import BannerContainer from "@/components/feat/BannerContainer";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import useStatus from "@/lib/hooks/use-status";
const inter = Inter({ variable: "--font-inter", subsets: ["latin"] }); const inter = Inter({ variable: "--font-inter", subsets: ["latin"] });
export default function LayoutPart({ export default function LayoutPart({
@ -21,22 +20,15 @@ export default function LayoutPart({
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const { loading, incidents, statusURL } = useStatus();
return ( return (
<> <>
<BannerContainer <BannerContainer
className={ className={"w-screen h-[3rem] border-b fixed backdrop-blur flex z-10"}
"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) => ({ style={(size: number) => ({
marginTop: `${2 * size}rem`, marginTop: `${2 * size}rem`,
})} })}
> >
<div className="items-center justify-self-start me-auto mt-2 pl-7 max-sm:mt-3 flex-1"> <div className="items-center me-auto mt-2 pl-7 max-sm:mt-3">
<Breadcrumb> <Breadcrumb>
<BreadcrumbList> <BreadcrumbList>
<Link href="/"> <Link href="/">
@ -48,30 +40,7 @@ export default function LayoutPart({
</BreadcrumbList> </BreadcrumbList>
</Breadcrumb> </Breadcrumb>
</div> </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} /> <TopBar inter={inter.className} />
</div>
</BannerContainer> </BannerContainer>
<BannerContainer <BannerContainer
style={(size: number) => ({ style={(size: number) => ({

@ -1,29 +0,0 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://mhsf.app/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 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 { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { z } from "zod"; import { z } from "zod";
@ -43,7 +43,7 @@ export function BannerPopover({ server, get }: { server: string; get: any }) {
} }
return ( return (
<ColorProvider server={server} fetchV={get}> <ColorProvider server={server} fetch={get}>
<div> <div>
<span className="text-sm"> <span className="text-sm">
All images that are in a web supported format can be used as the 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]); }, [get]);
return ( return (
<ColorProvider server={server} fetchV={get}> <ColorProvider server={server} fetch={get}>
<div> <div>
<span className="text-sm"> <span className="text-sm">
To embed a Discord server into your server page, first enable the To embed a Discord server into your server page, first enable the

@ -1,200 +0,0 @@
"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>
);
}

@ -1,134 +0,0 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://mhsf.app/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 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>
);
}

@ -1,74 +0,0 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://mhsf.app/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 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,107 +29,44 @@
*/ */
"use client"; "use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
import { Badge } from "../ui/badge"; import { useState } from "react";
import Link from "next/link"; import { TextCopyComp } from "./TextCopyComp";
import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; import { ChevronDown, ChevronUp } from "lucide-react";
import { ServerOff } from "lucide-react";
export function ShowInfo() { export function ShowInfo() {
const [open, setOpen] = useState(false);
return ( return (
<div> <div>
<br /> {open == false && (
Choose a method: <div
<Tabs defaultValue="preview" className="relative mr-auto w-full"> className="text-blue-500 flex items-center cursor-pointer"
<div className="flex items-center justify-between pb-3"> onClick={() => setOpen(true)}
<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"
> >
CoreBoxx <Badge className="ml-3">Recommended</Badge> More info <ChevronDown size={16} className="ml-2" />
</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> </div>
<TabsContent value="coreboxx"> )}
{open == true && (
<>
<p> <p>
<Link href="/server/CoreBoxx" className="underline"> By claiming your account, you can add Markdown descriptions and{" "}
CoreBoxx custom color schemes to your server (and more), making it stand out.
</Link>{" "} To get started, join the server below on your Minecraft account.
has partnered with us to have an integrated account linking feature, Enter the code in chat in the website, and you will link your
which is also open all day. account. You may need to go into the lobby and start the server.
</p> </p>
<br /> <br />
<p className="py-1"> <TextCopyComp />
<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 /> <br />
<p>
MHSFPV is a Minehut server dedicated to linking your account on
MHSF.
</p>
<br /> <br />
<p className="py-1"> <div
<code className="border rounded-full bg-muted h-[1.75rem] w-[1.75rem] absolute inline-flex items-center justify-center"> className="text-blue-500 flex items-center cursor-pointer"
1 onClick={() => setOpen(false)}
</code> >
<span className="ml-[2.25rem] pt-0.5 grid grid-rows-2"> Less info <ChevronUp size={16} className="ml-2" />
<span>Join MHSFPV</span> </div>
</>
<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> </div>
); );
} }

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

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

@ -1,158 +0,0 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://mhsf.app/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 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>
);
}

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

@ -1,59 +0,0 @@
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,20 +1,15 @@
import { useTheme } from "next-themes";
import type { SVGProps } from "react"; import type { SVGProps } from "react";
const Github = (props: SVGProps<SVGSVGElement>) => { const Github = (props: SVGProps<SVGSVGElement>) => (
const { resolvedTheme } = useTheme();
return (
<svg <svg
viewBox="0 0 256 250" viewBox="0 0 256 250"
width="1em" width="1em"
height="1em" height="1em"
fill={resolvedTheme === "dark" ? "#fff" : "#24292f"} fill="#fff"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid" preserveAspectRatio="xMidYMid"
{...props} {...props}
> >
<path d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z" /> <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> </svg>
); );
};
export default Github; export default Github;

@ -99,15 +99,6 @@ export const allTags: Array<{
role: "secondary", role: "secondary",
__filter: true, __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 // deprecated
/**{ /**{
name: async () => "Velocity", name: async () => "Velocity",
@ -289,11 +280,12 @@ export const allCategories: Array<{
async function requestServer(s: OnlineServer): Promise<ServerResponse> { async function requestServer(s: OnlineServer): Promise<ServerResponse> {
if (serverCache[s.name] === undefined) { if (serverCache[s.name] === undefined) {
const re = await fetch( 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(); const json = await re.json();
serverCache[s.name] = json.server; serverCache[s.name] = json.server;
return json.server; return json.server;
} } else {
return serverCache[s.name]; return serverCache[s.name];
}
} }

@ -30,9 +30,6 @@
"use client"; "use client";
import A from "@/components/misc/Link"; 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"; import type { ReactNode } from "react";
const User = ({ user }: { user: string }) => ( const User = ({ user }: { user: string }) => (
@ -44,22 +41,13 @@ const User = ({ user }: { user: string }) => (
const FeatureList = ({ const FeatureList = ({
features, features,
title, title,
github,
}: { }: {
features: (string | ReactNode)[]; features: (string | ReactNode)[];
github?: string;
title: ReactNode; title: ReactNode;
}) => { }) => {
return ( return (
<ul> <ul>
{title} {title}
{github && (
<Link href={github}>
<Button variant="ghost" size="sm">
<Github className="mr-1" /> Release
</Button>
</Link>
)}
{features.map((feature, i) => ( {features.map((feature, i) => (
<li key={i}> {feature}</li> <li key={i}> {feature}</li>
))} ))}
@ -67,42 +55,13 @@ const FeatureList = ({
); );
}; };
export const version = "1.7.0"; export const version = "1.6.50";
export const changelog: { name: string; id: string; changelog: ReactNode }[] = [ 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", id: "dut6hx3f2paswzjve4yg9r",
name: "v1.6.5", name: "v1.6.5",
changelog: ( changelog: (
<FeatureList <FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.6.5"
features={[ features={[
"New MOTD engine that is over 3,000% faster, runs client-side, and doesn't need any requests to run.", "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", "Fixed issue where GitHub link was broken if you were signed-out",
@ -121,7 +80,6 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
name: "v1.6.0", name: "v1.6.0",
changelog: ( changelog: (
<FeatureList <FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.6"
features={[ features={[
"Completely redid top of server view", "Completely redid top of server view",
"Favorite counts are now prominent on the server view", "Favorite counts are now prominent on the server view",
@ -144,7 +102,6 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
name: "v1.5.0", name: "v1.5.0",
changelog: ( changelog: (
<FeatureList <FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.5"
features={[ features={[
"New embeds", "New embeds",
"More mobile friendly elements", "More mobile friendly elements",
@ -164,7 +121,6 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
name: "v1.4.5", name: "v1.4.5",
changelog: ( changelog: (
<FeatureList <FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.4.5"
features={["Add server icons"]} features={["Add server icons"]}
title={ title={
<strong className="flex items-center"> <strong className="flex items-center">
@ -179,7 +135,6 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
name: "v1.4.0", name: "v1.4.0",
changelog: ( changelog: (
<FeatureList <FeatureList
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.4"
features={[ features={[
"Revamped documentation", "Revamped documentation",
"Revamped changelog UI", "Revamped changelog UI",

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

@ -61,10 +61,7 @@ export default async function handler(
const collection = db.collection("owned-servers"); const collection = db.collection("owned-servers");
if ((await collection.findOne({ server: server })) == undefined) { if ((await collection.findOne({ server: server })) == undefined) {
const mh = await fetch( const mh = await fetch("https://api.minehut.com/servers", {
process.env.MHSF_BACKEND_API_LOCATION ??
"https://api.minehut.com/servers",
{
headers: { headers: {
accept: "*/*", accept: "*/*",
"accept-language": Math.random().toString(), "accept-language": Math.random().toString(),
@ -77,12 +74,10 @@ export default async function handler(
"sec-fetch-site": "cross-site", "sec-fetch-site": "cross-site",
Referer: "http://localhost:3000/", Referer: "http://localhost:3000/",
"Referrer-Policy": "strict-origin-when-cross-origin", "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, body: null,
method: "GET", method: "GET",
} });
);
const servers: Array<OnlineServer> = (await mh.json()).servers; const servers: Array<OnlineServer> = (await mh.json()).servers;
servers.forEach(async (c, i) => { 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 db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const users = db.collection("claimed-users"); const users = db.collection("claimed-users");
if ((await users.find({ userId }).toArray()).length === 0) { const user = await (await clerkClient()).users.getUser(userId);
if (user.publicMetadata.player == undefined) {
res.status(400).send({ result: "Hasn't linked yet!" }); res.status(400).send({ result: "Hasn't linked yet!" });
return; return;
} }
await users.findOneAndDelete({ userId }); await users.findOneAndDelete({ player: user.publicMetadata.player });
const user = await (await clerkClient()).users.getUser(userId);
await ( await (
await clerkClient() await clerkClient()
).users.updateUserMetadata(userId, { ).users.updateUserMetadata(userId, {

@ -1,61 +0,0 @@
/*
* MHSF, Minehut Server List
* All external content is rather licensed under the ECA Agreement
* located here: https://mhsf.app/docs/legal/external-content-agreement
*
* All code under MHSF is licensed under the MIT License
* by open source contributors
*
* Copyright (c) 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,11 +373,6 @@
ts-pattern "^4.3.0" ts-pattern "^4.3.0"
type-fest "^3.12.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": "@discordjs/builders@^1.8.2":
version "1.8.2" version "1.8.2"
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.8.2.tgz#535d970331ee40f20dec9ef8079e43092f323ce9" resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.8.2.tgz#535d970331ee40f20dec9ef8079e43092f323ce9"
@ -846,11 +841,6 @@
resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.10.tgz#1d3178340028ced2d679f84140877db4f420333c" resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.10.tgz#1d3178340028ced2d679f84140877db4f420333c"
integrity sha512-dZIu93Bf5LUtluBXIv4woQw2cZVZ2DJTjax5/5DOs3lzEOeKLy7GxRSr4caK9/SCPdaW6bCgpye6+n4Dh9oJPw== 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": "@next/eslint-plugin-next@14.2.3":
version "14.2.3" version "14.2.3"
resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz" resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz"
@ -6336,7 +6326,7 @@ minimessage-js@^1.1.3:
resolved "https://registry.yarnpkg.com/minimessage-js/-/minimessage-js-1.1.3.tgz#ded53108d686e681cfb2071c2db60b6ae2da60db" resolved "https://registry.yarnpkg.com/minimessage-js/-/minimessage-js-1.1.3.tgz#ded53108d686e681cfb2071c2db60b6ae2da60db"
integrity sha512-dP7O/JVfRwMwFl/8KQALtKa+Vo4Za3ihgcSLVB6X3k/fjZWhA8SDQBJ1xUZu0NB6mslkzrIE1Xo+EHYcSXGtQw== integrity sha512-dP7O/JVfRwMwFl/8KQALtKa+Vo4Za3ihgcSLVB6X3k/fjZWhA8SDQBJ1xUZu0NB6mslkzrIE1Xo+EHYcSXGtQw==
minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: minimist@^1.2.0, minimist@^1.2.6:
version "1.2.8" version "1.2.8"
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@ -6453,16 +6443,6 @@ next-css-obfuscator@^2.2.16:
recoverable-random "^1.0.5" recoverable-random "^1.0.5"
yargs "^17.7.2" 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: next-themes@^0.4.3:
version "0.4.3" version "0.4.3"
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.3.tgz#ea54552d5986936d177eed393ea50b658ae44800" resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.3.tgz#ea54552d5986936d177eed393ea50b658ae44800"