mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 16:05:00 -05:00
feat(www): added statistical banner
This commit is contained in:
parent
45e0924808
commit
bbec84c7de
@ -1,16 +1,16 @@
|
||||
// For Edge runtime, we need to use fetch instead of fs
|
||||
export async function loadFonts() {
|
||||
const interRegularFontP = fetch(
|
||||
new URL("./Inter-Regular.ttf", import.meta.url),
|
||||
).then((res) => res.arrayBuffer());
|
||||
const interRegularFontP = fetch(
|
||||
new URL("./Inter-Regular.ttf", import.meta.url)
|
||||
).then((res) => res.arrayBuffer());
|
||||
|
||||
const interMediumFontP = fetch(
|
||||
new URL("./Inter-Medium.ttf", import.meta.url),
|
||||
).then((res) => res.arrayBuffer());
|
||||
const interMediumFontP = fetch(
|
||||
new URL("./Inter-Medium.ttf", import.meta.url)
|
||||
).then((res) => res.arrayBuffer());
|
||||
|
||||
const interBoldFontP = fetch(
|
||||
new URL("./Inter-Bold.ttf", import.meta.url),
|
||||
).then((res) => res.arrayBuffer());
|
||||
const interBoldFontP = fetch(
|
||||
new URL("./Inter-Bold.ttf", import.meta.url)
|
||||
).then((res) => res.arrayBuffer());
|
||||
|
||||
return Promise.all([interRegularFontP, interMediumFontP, interBoldFontP]);
|
||||
return Promise.all([interRegularFontP, interMediumFontP, interBoldFontP]);
|
||||
}
|
||||
|
||||
643
apps/www/src/app/api/og/server/[id]/players/route.tsx
Normal file
643
apps/www/src/app/api/og/server/[id]/players/route.tsx
Normal file
@ -0,0 +1,643 @@
|
||||
import { ImageResponse } from "@vercel/og";
|
||||
import { NextRequest } from "next/server";
|
||||
import { MongoClient } from "mongodb";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
// Helper function to format dates
|
||||
function formatDate(date: Date): string {
|
||||
return date.toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to format times
|
||||
function formatTime(date: Date): string {
|
||||
return date.toLocaleTimeString("en-US", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
// Load fonts directly from the filesystem
|
||||
async function loadLocalFonts() {
|
||||
const fontPath = path.join(process.cwd(), "src", "app", "api", "og", "fonts");
|
||||
|
||||
return [
|
||||
fs.readFileSync(path.join(fontPath, "Inter-Regular.ttf")),
|
||||
fs.readFileSync(path.join(fontPath, "Inter-Medium.ttf")),
|
||||
fs.readFileSync(path.join(fontPath, "Inter-Bold.ttf")),
|
||||
];
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
// Load banner image from filesystem
|
||||
const bannerPath = path.join(
|
||||
process.cwd(),
|
||||
"public",
|
||||
"branding",
|
||||
"bg-banner.png"
|
||||
);
|
||||
const bannerImageData = fs.readFileSync(bannerPath);
|
||||
|
||||
const id = (await params).id;
|
||||
|
||||
// Load fonts
|
||||
const [interRegular, interMedium, interBold] = await loadLocalFonts();
|
||||
|
||||
// Verify server exists
|
||||
const serverResponse = await fetch(`https://api.minehut.com/server/${id}`);
|
||||
const serverData = await serverResponse.json();
|
||||
|
||||
if (!serverData.server) {
|
||||
// Return a default banner for server not found
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
fontSize: 60,
|
||||
color: "white",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
padding: "50px 50px",
|
||||
textAlign: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundImage: `url(data:image/png;base64,${bannerImageData.toString("base64")})`,
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
fontFamily: "Inter",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<span style={{ display: "flex" }}>Server not found</span>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{
|
||||
name: "Inter",
|
||||
data: interRegular,
|
||||
style: "normal",
|
||||
weight: 400,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const serverName = serverData.server.name;
|
||||
|
||||
// Connect to MongoDB
|
||||
const mongo = new MongoClient(process.env.MONGO_DB as string);
|
||||
await mongo.connect();
|
||||
const db = mongo.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
|
||||
// Get player data (last 60 entries)
|
||||
const historyCollection = db.collection("history");
|
||||
const playerData = await historyCollection
|
||||
.find({ server: serverName })
|
||||
.sort({ date: -1 })
|
||||
.limit(80)
|
||||
.project({ player_count: 1, date: 1, _id: 0 })
|
||||
.toArray();
|
||||
|
||||
// Close MongoDB connection
|
||||
await mongo.close();
|
||||
|
||||
// Reverse the data to show oldest to newest
|
||||
const playerHistory = playerData.reverse();
|
||||
|
||||
// Calculate max player count for scaling
|
||||
const maxPlayerCount = Math.max(
|
||||
...playerHistory.map((item: any) => item.player_count || 0),
|
||||
1 // Ensure we have at least 1 as max to avoid division by zero
|
||||
);
|
||||
|
||||
// Calculate average player count
|
||||
const averagePlayerCount = Math.round(
|
||||
playerHistory.reduce(
|
||||
(sum: number, item: any) => sum + (item.player_count || 0),
|
||||
0
|
||||
) / Math.max(playerHistory.length, 1)
|
||||
);
|
||||
|
||||
// Get current player count
|
||||
const currentPlayerCount =
|
||||
playerHistory.length > 0
|
||||
? playerHistory[playerHistory.length - 1].player_count || 0
|
||||
: 0;
|
||||
|
||||
const bars = [];
|
||||
const numBars = Math.min(playerHistory.length, 30); // Limit to 30 bars for better visibility
|
||||
const step = Math.max(1, Math.floor(playerHistory.length / numBars));
|
||||
|
||||
// Create Y-axis markers (player count) - Modified to avoid drawing horizontal lines that intersect with Y-axis
|
||||
const yAxisMarkers = [];
|
||||
const numYMarkers = 4; // Reduced to 4 for better alignment
|
||||
|
||||
for (let i = 0; i <= numYMarkers; i++) {
|
||||
const value = Math.round((maxPlayerCount * i) / numYMarkers);
|
||||
const position = 200 - (i / numYMarkers) * 200;
|
||||
|
||||
yAxisMarkers.push(
|
||||
<div
|
||||
key={`y-marker-${i}`}
|
||||
style={{
|
||||
display: "flex",
|
||||
position: "absolute",
|
||||
left: "50px",
|
||||
top: `${position}px`,
|
||||
width: "1100px",
|
||||
borderTop: i > 0 ? "1px dashed rgba(255, 255, 255, 0.2)" : "none",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
position: "absolute",
|
||||
left: "-40px",
|
||||
top: "-10px",
|
||||
fontSize: "12px",
|
||||
color: "rgba(255, 255, 255, 0.7)",
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const chartWidth = 970; // Fixed width of the chart area
|
||||
// Create X-axis markers (times)
|
||||
const xAxisMarkers = [];
|
||||
const numXMarkers = Math.min(10, playerHistory.length); // Increased to 10 for more labels
|
||||
|
||||
if (playerHistory.length > 0) {
|
||||
for (let i = 0; i < numXMarkers; i++) {
|
||||
const index = Math.floor(
|
||||
(i / (numXMarkers - 1)) * (playerHistory.length - 1)
|
||||
);
|
||||
const item = playerHistory[index];
|
||||
const position = (index / (playerHistory.length - 1)) * chartWidth;
|
||||
|
||||
xAxisMarkers.push(
|
||||
<div
|
||||
key={`x-marker-${i}`}
|
||||
style={{
|
||||
display: "flex",
|
||||
position: "absolute",
|
||||
left: `${position + 50}px`, // Add 50px offset for Y-axis
|
||||
bottom: "-25px",
|
||||
transform: "translateX(-50%)",
|
||||
fontSize: "12px",
|
||||
color: "rgba(255, 255, 255, 0.7)",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
{formatTime(new Date(item.date))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < playerHistory.length; i += step) {
|
||||
const item = playerHistory[i];
|
||||
const height = Math.max(
|
||||
5,
|
||||
((item.player_count || 0) / maxPlayerCount) * 200
|
||||
);
|
||||
const position = (i / (playerHistory.length - 1)) * chartWidth;
|
||||
|
||||
bars.push(
|
||||
<div
|
||||
key={`bar-${i}`}
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "flex-end",
|
||||
width: "16px",
|
||||
height: "209px",
|
||||
position: "absolute",
|
||||
left: `${position}px`, // Add 50px offset for Y-axis
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
height: `${height}px`,
|
||||
backgroundColor: "#4ade80",
|
||||
borderRadius: "2px 2px 0 0",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
padding: "50px",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
fontFamily: "Inter",
|
||||
backgroundImage: `url(data:image/png;base64,${bannerImageData.toString("base64")})`,
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
}}
|
||||
>
|
||||
{/* Decorative elements */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "-100px",
|
||||
right: "-100px",
|
||||
width: "400px",
|
||||
height: "400px",
|
||||
borderRadius: "50%",
|
||||
background: "rgba(79, 70, 229, 0.1)",
|
||||
filter: "blur(40px)",
|
||||
display: "flex",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: "-100px",
|
||||
left: "-100px",
|
||||
width: "300px",
|
||||
height: "300px",
|
||||
borderRadius: "50%",
|
||||
background: "rgba(236, 72, 153, 0.1)",
|
||||
filter: "blur(40px)",
|
||||
display: "flex",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Header with server name */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<h1
|
||||
style={{
|
||||
fontSize: 40,
|
||||
fontWeight: "bold",
|
||||
margin: 0,
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<span style={{ display: "flex" }}>
|
||||
{serverName} - Player Statistics
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{/* Stats summary */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 20, opacity: 0.7, display: "flex" }}>
|
||||
<span style={{ display: "flex" }}>Max Players</span>
|
||||
</div>
|
||||
<div
|
||||
style={{ fontSize: 36, fontWeight: "bold", display: "flex" }}
|
||||
>
|
||||
<span style={{ display: "flex" }}>{maxPlayerCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 20, opacity: 0.7, display: "flex" }}>
|
||||
<span style={{ display: "flex" }}>Average Players</span>
|
||||
</div>
|
||||
<div
|
||||
style={{ fontSize: 36, fontWeight: "bold", display: "flex" }}
|
||||
>
|
||||
<span style={{ display: "flex" }}>{averagePlayerCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 20, opacity: 0.7, display: "flex" }}>
|
||||
<span style={{ display: "flex" }}>Current Players</span>
|
||||
</div>
|
||||
<div
|
||||
style={{ fontSize: 36, fontWeight: "bold", display: "flex" }}
|
||||
>
|
||||
<span style={{ display: "flex" }}>{currentPlayerCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bar chart container */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
marginTop: "10px",
|
||||
height: "270px",
|
||||
width: "100%",
|
||||
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
||||
borderRadius: "8px",
|
||||
padding: "20px 40px 20px 40px", // Added padding for Y-axis labels
|
||||
position: "relative", // For absolute positioning of markers
|
||||
backdropFilter: "blur(12px)",
|
||||
}}
|
||||
>
|
||||
{/* Chart content container */}
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "220px",
|
||||
paddingBottom: "10px",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
{/* Y-axis and X-axis are now drawn as a single element to ensure continuity */}
|
||||
<svg
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
pointerEvents: "none",
|
||||
zIndex: "20",
|
||||
}}
|
||||
>
|
||||
{/* Y-axis line */}
|
||||
<line
|
||||
x1="50" // Changed from 30 to match new spacing
|
||||
y1="0"
|
||||
x2="50" // Changed from 30 to match new spacing
|
||||
y2="200"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeOpacity="0.8"
|
||||
/>
|
||||
{/* X-axis line */}
|
||||
<line
|
||||
x1="50"
|
||||
y1="200"
|
||||
x2="1150"
|
||||
y2="200"
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeOpacity="0.8"
|
||||
/>
|
||||
{/* Y-axis tick marks */}
|
||||
{Array.from({ length: numYMarkers + 1 }).map((_, i) => {
|
||||
const yPosition = 200 - (i / numYMarkers) * 200;
|
||||
return (
|
||||
<line
|
||||
key={`y-tick-${i}`}
|
||||
x1="45" // Changed from 25 to match new spacing
|
||||
y1={yPosition}
|
||||
x2="50" // Changed from 30 to match new spacing
|
||||
y2={yPosition}
|
||||
stroke="white"
|
||||
strokeWidth="2"
|
||||
strokeOpacity="0.8"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</svg>
|
||||
|
||||
{/* Y-axis markers (labels and grid lines) */}
|
||||
<div style={{ display: "flex" }}>{yAxisMarkers}</div>
|
||||
|
||||
{/* X-axis markers */}
|
||||
<div style={{ display: "flex" }}>{xAxisMarkers}</div>
|
||||
|
||||
{/* Bars */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
width: "1100px",
|
||||
height: "220px",
|
||||
marginLeft: "50px",
|
||||
}}
|
||||
>
|
||||
{bars}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Chart legend */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginTop: "10px",
|
||||
marginBottom: "30px",
|
||||
fontSize: "14px",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<span style={{ display: "flex" }}>
|
||||
Oldest Entry:{" "}
|
||||
{playerHistory.length > 0
|
||||
? formatDate(new Date(playerHistory[0].date))
|
||||
: "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<span style={{ display: "flex" }}>
|
||||
Latest Entry:{" "}
|
||||
{playerHistory.length > 0
|
||||
? formatDate(
|
||||
new Date(playerHistory[playerHistory.length - 1].date)
|
||||
)
|
||||
: "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
fontSize: 18,
|
||||
marginTop: "30px",
|
||||
opacity: 0.7,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex" }}>
|
||||
<span style={{ display: "flex" }}>✨ Powered by mhsf.app</span>
|
||||
</div>
|
||||
<br />
|
||||
<small
|
||||
style={{
|
||||
fontSize: 12,
|
||||
textAlign: "center",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<span style={{ display: "flex" }}>
|
||||
Licensed under MIT license using open source technologies.
|
||||
</span>
|
||||
<span style={{ display: "flex", textAlign: "center" }}>
|
||||
Using dynamic statistical data version 1.
|
||||
</span>
|
||||
<span style={{ display: "flex", textAlign: "center" }}>
|
||||
Not officially affiliated with Minehut, GamerSafer or Super
|
||||
League Enterprise.
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{
|
||||
name: "Inter",
|
||||
data: interRegular,
|
||||
style: "normal",
|
||||
weight: 400,
|
||||
},
|
||||
{
|
||||
name: "Inter",
|
||||
data: interBold,
|
||||
style: "normal",
|
||||
weight: 700,
|
||||
},
|
||||
{
|
||||
name: "Inter",
|
||||
data: interMedium,
|
||||
style: "normal",
|
||||
weight: 500,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error generating player statistics image:", error);
|
||||
|
||||
// Load fonts for error page
|
||||
let interRegular: Buffer | null = null;
|
||||
try {
|
||||
interRegular = (await loadLocalFonts())[0];
|
||||
} catch (e) {
|
||||
// If we can't load fonts, we'll just use a system font
|
||||
console.error("Failed to load fonts for error page:", e);
|
||||
}
|
||||
|
||||
// Try to load the banner image
|
||||
let bannerImageData: Buffer | null = null;
|
||||
try {
|
||||
const bannerPath = path.join(
|
||||
process.cwd(),
|
||||
"public",
|
||||
"branding",
|
||||
"dark-banner.png"
|
||||
);
|
||||
bannerImageData = fs.readFileSync(bannerPath);
|
||||
} catch (e) {
|
||||
// If banner image fails to load, use a solid color background
|
||||
console.error("Failed to load banner image for error page:", e);
|
||||
}
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
fontSize: 60,
|
||||
color: "white",
|
||||
background: bannerImageData ? undefined : "#121212",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
padding: "50px 50px",
|
||||
textAlign: "center",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
fontFamily: "Inter",
|
||||
...(bannerImageData && {
|
||||
backgroundImage: `url(data:image/png;base64,${bannerImageData.toString("base64")})`,
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<span style={{ display: "flex" }}>
|
||||
Error generating player statistics image
|
||||
</span>
|
||||
<span style={{ fontSize: 24, display: "flex" }}>
|
||||
{String(error).substring(0, 100)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
fonts: interRegular
|
||||
? [
|
||||
{
|
||||
name: "Inter",
|
||||
data: interRegular,
|
||||
style: "normal",
|
||||
weight: 400,
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
32
apps/www/src/config/version.ts
Normal file
32
apps/www/src/config/version.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2025 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
export const version = "2.0";
|
||||
@ -1,527 +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) 2025 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Github from "@/components/ui/github";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const User = ({ user }: { user: string }) => (
|
||||
<span className="cursor-pointer bg-[rgba(255,165,0,0.25);] rounded p-[2.5px]">
|
||||
{user}
|
||||
</span>
|
||||
);
|
||||
|
||||
const FeatureList = ({
|
||||
features,
|
||||
title,
|
||||
github,
|
||||
}: {
|
||||
features: (string | ReactNode)[];
|
||||
github?: string;
|
||||
title: ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<ul>
|
||||
{title}
|
||||
{github && (
|
||||
<Link href={github}>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Github className="mr-1" /> Release
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
{features.map((feature, i) => (
|
||||
<li key={i}>• {feature}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export const version = "2.0";
|
||||
export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
|
||||
{
|
||||
id: "tnjageringae231nfnajrekgra",
|
||||
name: "v1.8.0",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.8.0"
|
||||
features={[
|
||||
"Major changes have been made with the technical project structure",
|
||||
"Please check the GitHub for information about the status of the project.",
|
||||
]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.8.0 (February 14th 2025, {"<3 happy valentines"})
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "tj4ijg09aern9eargjjuauerr",
|
||||
name: "v1.7.5",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.7.5"
|
||||
features={["Migrated accounts from development to production"]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.7.5 (January 29th 2025)
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "38ufajf8efajwj3njdaisef",
|
||||
name: "v1.7",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.7"
|
||||
features={[
|
||||
"Partnered with CoreBoxx!",
|
||||
"You can now link your Minecraft account on CoreBoxx! (check out CB 3.0.15)",
|
||||
"Revamped the server finding controls",
|
||||
"Fixed various bugs",
|
||||
"Made banners a different style",
|
||||
"Made Discord embed not inside a card",
|
||||
"Added incorrect server capitalization detection",
|
||||
"Made the MOTD area slightly bigger",
|
||||
"New footer",
|
||||
"Added padding for settings page",
|
||||
"Added new table mode",
|
||||
"Added new button for GitHub release on changelog",
|
||||
]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.7 (January 18th 2025)
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "dut6hx3f2paswzjve4yg9r",
|
||||
name: "v1.6.5",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.6.5"
|
||||
features={[
|
||||
"New MOTD engine that is over 3,000% faster, runs client-side, and doesn't need any requests to run.",
|
||||
"Fixed issue where GitHub link was broken if you were signed-out",
|
||||
"Adding snowfall finally (better late then ever)",
|
||||
]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.6.5 (December 20th 2024)
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "h9jr2cbxn7qwfvt5uypsdg",
|
||||
name: "v1.6.0",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.6"
|
||||
features={[
|
||||
"Completely redid top of server view",
|
||||
"Favorite counts are now prominent on the server view",
|
||||
"New theme transition (smooth)",
|
||||
"New favorite button",
|
||||
"Added more padding in the server view",
|
||||
"Separated the tabs on the side for sharing actions",
|
||||
"Added new QR code generator",
|
||||
]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.6.0 (November 17th 2024)
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "r9swempc7kaqd2j84nutv5",
|
||||
name: "v1.5.0",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.5"
|
||||
features={[
|
||||
"New embeds",
|
||||
"More mobile friendly elements",
|
||||
"Better tabs in the server",
|
||||
"Fixed issue where some servers due to their age were not loading",
|
||||
]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.5.0 (November 16th 2024)
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "ywvhtcs4k9rqjfp57x",
|
||||
name: "v1.4.5",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.4.5"
|
||||
features={["Add server icons"]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.4.5 (November 6th 2024)
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "amq4suhgcfwrb7y5j6",
|
||||
name: "v1.4.0",
|
||||
changelog: (
|
||||
<FeatureList
|
||||
github="https://github.com/DeveloLongScript/MHSF/releases/tag/1.4"
|
||||
features={[
|
||||
"Revamped documentation",
|
||||
"Revamped changelog UI",
|
||||
"New hover joins chart",
|
||||
]}
|
||||
title={
|
||||
<strong className="flex items-center">
|
||||
Version 1.4.0 (November 3rd 2024)
|
||||
</strong>
|
||||
}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "jeh48p7w9bx2k3ad6f",
|
||||
name: "v1.3.2",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version 1.3.2 (October 4th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Minor backend changes</li>
|
||||
<li>
|
||||
•{" "}
|
||||
<Link href="Special:GitHub/releases/tag/1.3.2">
|
||||
Please check on GitHub for statuses about this project.
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "wvg9x5dbpj76sn4yrz",
|
||||
name: "v1.3.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version 1.3.0 (September 9th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>
|
||||
• <Link href="Docs:Reading">New documentation linking</Link>
|
||||
</li>
|
||||
<li>
|
||||
• Achievements are here! See more at{" "}
|
||||
<Link href="Docs:Advanced/Achievements">here</Link>
|
||||
</li>
|
||||
<li>• Finally fixed Cron actions for the final time™</li>
|
||||
<li>• Overhauled account preferences</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "v1.2.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version 1.2.0 (September 3rd 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Added documentation</li>
|
||||
<li>• Brand new linking of padding and server options</li>
|
||||
<li>• New system to ensure automatic Cron actions!</li>
|
||||
<li>• and alot more!</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "e482y9k5hvjt73urfx",
|
||||
},
|
||||
{
|
||||
name: "v1.1.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version 1.1.0 (August 24rd 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Brand new hero page</li>
|
||||
<li>• New padding option on server list</li>
|
||||
<li>• New help guide</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "hfn9p243765x8bwurj",
|
||||
},
|
||||
{
|
||||
name: "v1.0.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version 1.0.0 (August 22nd 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• 1.0!</li>
|
||||
<li>• New hover card on server title hover</li>
|
||||
<li>• Moving to self-hosted cron jobs</li>
|
||||
<li>• Fixing some mobile issues</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "a8w4xvjbg3s7ynehu6",
|
||||
},
|
||||
{
|
||||
name: "v0.10.7",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.10.7 (August 18th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• New server information tab on server pages</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "asbt64h9fdyu8neqmp",
|
||||
},
|
||||
{
|
||||
name: "v0.10.2",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.10.2 (August 18th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Content fades-in on load</li>
|
||||
<li>• Instead of using spinners, now we are using Skeletons</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "kct29adbp6zug5r3q8",
|
||||
},
|
||||
{
|
||||
name: "v0.10.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.10.0 (August 17th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Revamped server list button list</li>
|
||||
<li>• Added welcome dialog when first launching</li>
|
||||
<li>
|
||||
• Fixed an issue where servers were still able to be favorited
|
||||
client-side when logged out
|
||||
</li>
|
||||
<li>• Improved MOTD engine</li>
|
||||
</ul>
|
||||
<br />
|
||||
<i>👀</i>
|
||||
{/** Ensure Tailwind pre-renders all grid column types */}
|
||||
<span className="grid-cols-6" />
|
||||
<span className="grid-cols-5" />
|
||||
<span className="grid-cols-4" />
|
||||
</div>
|
||||
),
|
||||
id: "ah6t7c8sfzyrkp3u52",
|
||||
},
|
||||
{
|
||||
name: "v0.9.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.9.0 (August 15th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Adding favorites sorting option</li>
|
||||
<li>• Fixed right-click context menu on the server list</li>
|
||||
<li>• Fixed metadata bugs</li>
|
||||
</ul>
|
||||
<br />
|
||||
<i>
|
||||
Hey! Update on statistics. Recently, we have figured out the Minehut
|
||||
API is blocked to Vercel servers (atleast the <code>/servers</code>{" "}
|
||||
endpoint). I'm actively trying to find a loop-hole so that statistics
|
||||
works correctly. Thank you {":)"}
|
||||
</i>
|
||||
<br />
|
||||
</div>
|
||||
),
|
||||
id: "kjxnrfazc7hp9q4e82",
|
||||
},
|
||||
{
|
||||
name: "v0.8.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.8.0 (August 11th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Fixing up command bar</li>
|
||||
<li>• Renaming "Short Term" to "Statistics"</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "f8rmhwzuxk3qyds542",
|
||||
},
|
||||
{
|
||||
name: "v0.7.2",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.7.2 (August 7th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Adding new spinners to pages that needed it</li>
|
||||
<li>• Fixed lots of bugs</li>
|
||||
<li>• Moved from Inngest to Vercel Cron</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "g2rhxfj6bu8wqk43n7",
|
||||
},
|
||||
{
|
||||
name: "v0.7.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.7.0 (August 7th 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Added customization to servers</li>
|
||||
<li>• New button focus effect</li>
|
||||
<li>• Lots of bugfixes</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "a5xb97jv3surwmqn62",
|
||||
},
|
||||
{
|
||||
name: "v0.6.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.6.0 (August 3rd 2024)
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Enhanced shortcuts</li>
|
||||
<li>• Added gradient beam to player count</li>
|
||||
<li>• Updated loading animations</li>
|
||||
<li>• Lots of bugfixes</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "u83r5mkea9x4p2fjnb",
|
||||
},
|
||||
{
|
||||
name: "v0.4.5",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.4.5 (July 26th 2024):
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Made charts better</li>
|
||||
<li>• Sorted API endpoints</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "vu3k2daqj4y68bnwsp",
|
||||
},
|
||||
{
|
||||
name: "v0.4.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.4 (July 25th 2024):
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Added Info button</li>
|
||||
<li>• Fixed Clerk in production</li>
|
||||
<li>• Added Turbo for faster builds</li>
|
||||
<li>
|
||||
• <strong>Added historical data</strong>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "psr9tx5jah74d32vq6",
|
||||
},
|
||||
{
|
||||
name: "v0.3.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.3 (July 23th 2024):
|
||||
</strong>
|
||||
<ul>
|
||||
<li>
|
||||
• Fixed minor bugs described by <User user="@Tarna" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "m2ngpd6fwtv7xh5zrk",
|
||||
},
|
||||
{
|
||||
name: "v0.2.0",
|
||||
changelog: (
|
||||
<div>
|
||||
<strong className="flex items-center">
|
||||
Version b-0.2 (July 23th 2024):
|
||||
</strong>
|
||||
<ul>
|
||||
<li>• Inital release!</li>
|
||||
</ul>
|
||||
</div>
|
||||
),
|
||||
id: "xsfw2rcnv7m3kuhpbq",
|
||||
},
|
||||
];
|
||||
Loading…
Reference in New Issue
Block a user