diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9a00da6..77322cf 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -42,6 +42,7 @@ We use [Atlas](https://www.mongodb.com/atlas) to host our MongoDB database, but
```dotenv
MONGO_DB="mongodb+srv://..."
```
+You can also set `CUSTOM_MONGO_DB` to a database name that will apply to all operations except statistical operations.
## Smaller things (for production-ready servers)
diff --git a/src/app/(main)/server/[server]/statistics/page.tsx b/src/app/(main)/server/[server]/statistics/page.tsx
index 6c80b7f..c1a7afa 100644
--- a/src/app/(main)/server/[server]/statistics/page.tsx
+++ b/src/app/(main)/server/[server]/statistics/page.tsx
@@ -36,6 +36,8 @@ import TabServer from "@/components/misc/TabServer";
import type { Metadata, ResolvingMetadata } from "next";
import StickyTopbar from "@/components/misc/StickyTopbar";
import { RelativeChart } from "@/components/charts/RelativeChart";
+import { MonthlyChart } from "@/components/charts/MonthlyChart";
+import { DailyChart } from "@/components/charts/DailyChart";
type Props = {
params: { server: string };
@@ -113,6 +115,11 @@ export default function ServerPage({ params }: { params: { server: string } }) {
+
+
+
+
+
diff --git a/src/components/ServerList.tsx b/src/components/ServerList.tsx
index d4835c5..91b230d 100644
--- a/src/components/ServerList.tsx
+++ b/src/components/ServerList.tsx
@@ -117,6 +117,7 @@ import { LoadingSpinner } from "./ui/loading-spinner";
import StickyTopbar from "./misc/StickyTopbar";
import { HoverCard } from "@radix-ui/react-hover-card";
import { HoverCardTrigger } from "./ui/hover-card";
+import { ExampleChart } from "./charts/ExampleChart";
// ag-grid
ModuleRegistry.registerModules([AllCommunityModule]);
@@ -451,13 +452,22 @@ export default function ServerList() {
and descriptions, making your server stand out with information
that can be shown to players.
-
+
{features.map((feature, idx) => (
))}
-
-
+
+
+
+
+ Monitor your success
+
+
+ Ever wondered how a server was doing? MHSF constantly monitors servers
+ and shows you statistics about how a server is doing at any point of time.
+
This means that the server can{"'"}t loading some resources, like
diff --git a/src/components/charts/DailyChart.tsx b/src/components/charts/DailyChart.tsx
new file mode 100644
index 0000000..cc189e2
--- /dev/null
+++ b/src/components/charts/DailyChart.tsx
@@ -0,0 +1,174 @@
+/*
+ * 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 { TrendingDown, TrendingUp } from "lucide-react";
+import {
+ Bar,
+ BarChart,
+ CartesianGrid,
+ LabelList,
+ Rectangle,
+ XAxis,
+ YAxis,
+} from "recharts";
+import { useEffect, useState } from "react";
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import { Skeleton } from "../ui/skeleton";
+import { getDailyData, getMonthlyData } from "@/lib/api";
+import { useTrend } from "@/lib/hooks/use-daily-trend";
+
+const chartConfig = {
+ result: {
+ label: "Players",
+ color: "hsl(var(--chart-3))",
+ },
+} satisfies ChartConfig;
+
+export function DailyChart({ server }: { server: string }) {
+ const [chartData, setChartData] = useState<{ day: string; result: number }[]>(
+ [],
+ );
+ const [loading, setLoading] = useState(true);
+ const { trend, percentage, success } = useTrend(chartData);
+
+ useEffect(() => {
+ getDailyData(server).then((c) => {
+ setChartData(c);
+ setLoading(false);
+ });
+ }, [server]);
+
+ if (loading) return ;
+
+ return (
+
+
+ Average daily players
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+ c.day ===
+ new Date().toLocaleDateString("en-US", { weekday: "long" }),
+ )}
+ activeBar={({ ...props }) => {
+ return (
+
+ );
+ }}
+ >
+
+
+
+
+
+
+ {success ? (
+
+ Trending up by 0% today{" "}
+ (Insufficient data)
+
+ )}
+
+ Showing an average of all data for {server}
+
+
+
+ );
+}
diff --git a/src/components/charts/ExampleChart.tsx b/src/components/charts/ExampleChart.tsx
new file mode 100644
index 0000000..e18c3d6
--- /dev/null
+++ b/src/components/charts/ExampleChart.tsx
@@ -0,0 +1,191 @@
+/*
+ * 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 * as React from "react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card"
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+const chartData = [
+ { date: "2024-04-01", player_count: 91 },
+ { date: "2024-04-02", player_count: 106 },
+ { date: "2024-04-03", player_count: 104 },
+ { date: "2024-04-04", player_count: 111 },
+ { date: "2024-04-05", player_count: 113 },
+ { date: "2024-04-06", player_count: 114 },
+ { date: "2024-04-07", player_count: 108 },
+ { date: "2024-04-08", player_count: 89 },
+ { date: "2024-04-09", player_count: 96 },
+ { date: "2024-04-10", player_count: 123 },
+ { date: "2024-04-11", player_count: 120 },
+ { date: "2024-04-12", player_count: 140 },
+ { date: "2024-04-13", player_count: 128 },
+ { date: "2024-04-14", player_count: 130 },
+ { date: "2024-04-15", player_count: 114 },
+ { date: "2024-04-16", player_count: 98 },
+ { date: "2024-04-17", player_count: 102 },
+ { date: "2024-04-18", player_count: 103 },
+ { date: "2024-04-19", player_count: 102 },
+ { date: "2024-04-20", player_count: 112},
+ { date: "2024-04-21", player_count: 117 },
+ { date: "2024-04-22", player_count: 119 },
+ { date: "2024-04-23", player_count: 129 },
+ { date: "2024-04-24", player_count: 121 },
+ { date: "2024-04-25", player_count: 126 },
+ { date: "2024-04-26", player_count: 98},
+ { date: "2024-04-27", player_count: 102 },
+ { date: "2024-04-28", player_count: 100 },
+ { date: "2024-04-29", player_count: 101 },
+ { date: "2024-04-30", player_count: 104 },
+ { date: "2024-05-01", player_count: 109 },
+ { date: "2024-05-02", player_count: 86 },
+ { date: "2024-05-03", player_count: 93 },
+ { date: "2024-05-04", player_count: 108 },
+ { date: "2024-05-05", player_count: 112 },
+ { date: "2024-05-06", player_count: 111 },
+ { date: "2024-05-07", player_count: 96 },
+ { date: "2024-05-08", player_count: 100 },
+ { date: "2024-05-09", player_count: 124 },
+ { date: "2024-05-10", player_count: 134 },
+ { date: "2024-05-11", player_count: 144 },
+ { date: "2024-05-12", player_count: 156 },
+ { date: "2024-05-13", player_count: 180 },
+ { date: "2024-05-14", player_count: 167 },
+ { date: "2024-05-15", player_count: 154 },
+ { date: "2024-05-16", player_count: 124 },
+ { date: "2024-05-17", player_count: 112 },
+ { date: "2024-05-18", player_count: 114 },
+ { date: "2024-05-19", player_count: 121 },
+ { date: "2024-05-20", player_count: 96 },
+ { date: "2024-05-21", player_count: 102 },
+ { date: "2024-05-22", player_count: 131 },
+]
+
+const chartConfig = {
+ player_count: {
+ label: "Players",
+ color: "hsl(var(--chart-1))",
+ }
+} satisfies ChartConfig
+
+export function ExampleChart() {
+
+ return (
+
+
+
+ Player count over 3 months
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ const date = new Date(value)
+ return date.toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }}
+ />
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }}
+ indicator="dot"
+ />
+ }
+ />
+
+ } />
+
+
+
+
+ )
+}
diff --git a/src/components/charts/MonthlyChart.tsx b/src/components/charts/MonthlyChart.tsx
new file mode 100644
index 0000000..6293763
--- /dev/null
+++ b/src/components/charts/MonthlyChart.tsx
@@ -0,0 +1,160 @@
+/*
+ * 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 { TrendingDown, TrendingUp } from "lucide-react";
+import { Bar, BarChart, CartesianGrid, LabelList, Rectangle, XAxis, YAxis } from "recharts";
+import { useEffect, useState } from "react";
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+import {
+ ChartConfig,
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+} from "@/components/ui/chart";
+import { Skeleton } from "../ui/skeleton";
+import { useTrend } from "@/lib/hooks/use-trend";
+import { getMonthlyData } from "@/lib/api";
+
+const chartConfig = {
+ result: {
+ label: "Players",
+ color: "hsl(var(--chart-3))",
+ },
+} satisfies ChartConfig;
+
+export function MonthlyChart({ server }: { server: string }) {
+ const [chartData, setChartData] = useState<
+ { month: string; result: number }[]
+ >([]);
+ const [loading, setLoading] = useState(true);
+ const { trend, percentage, success } = useTrend(chartData);
+
+ useEffect(() => {
+ getMonthlyData(server).then((c) => {
+ setChartData(c);
+ setLoading(false);
+ });
+ }, [server]);
+
+ if (loading) return ;
+
+ return (
+
+
+ Average monthly players
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+ c.month ===
+ new Date().toLocaleDateString("en-US", { month: "long" }),
+ )}
+ activeBar={({ ...props }) => {
+ return (
+
+ );
+ }}>
+
+
+
+
+
+
+ {success ? (
+
diff --git a/src/components/misc/MiniJoinsChart.tsx b/src/components/misc/MiniJoinsChart.tsx
index a521002..ed7bef1 100644
--- a/src/components/misc/MiniJoinsChart.tsx
+++ b/src/components/misc/MiniJoinsChart.tsx
@@ -52,7 +52,7 @@ export function MiniJoinsChart({ server }: { server: string }) {
useEffectOnce(() => {
getShortTermData(server, ["player_count", "date"]).then((result) => {
- setChartData(result.slice(-20));
+ setChartData(result.data.slice(-20));
setLoading(false);
});
});
diff --git a/src/lib/api.ts b/src/lib/api.ts
index 966cf20..f6e4616 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -89,6 +89,28 @@ export async function getRelativeServerData(
return json.data;
}
+export async function getMonthlyData(
+ server: string
+): Promise> {
+ const result = await fetch(
+ connector("/history/" + server + "/get-monthly-data", { version: 0 })
+ );
+
+ const json = await result.json();
+ return json.result;
+}
+
+export async function getDailyData(
+ server: string
+): Promise> {
+ const result = await fetch(
+ connector("/history/" + server + "/get-daily-data", { version: 0 })
+ );
+
+ const json = await result.json();
+ return json.result;
+}
+
export async function getCommunityServerFavorites(
server: string
): Promise {
diff --git a/src/lib/hooks/use-daily-trend.tsx b/src/lib/hooks/use-daily-trend.tsx
new file mode 100644
index 0000000..9ed6cea
--- /dev/null
+++ b/src/lib/hooks/use-daily-trend.tsx
@@ -0,0 +1,80 @@
+/*
+ * 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 { useEffect, useState } from "react";
+
+export function useTrend(data: { day: string; result: number }[]) {
+ const [success, setSuccess] = useState(true);
+ const [trend, setTrend] = useState<"up" | "down">("up");
+ const [percentage, setPercentage] = useState(0);
+
+ useEffect(() => {
+ const today = new Date();
+ const previousDay = new Date();
+ previousDay.setDate(previousDay.getDate() - 1);
+
+ const previousDayData = data.find(
+ (x) => x.day === previousDay.toLocaleString('en-us', { weekday: 'long' }),
+ );
+ const todayData = data.find(
+ (x) => x.day === today.toLocaleString('en-us', { weekday: 'long' }),
+ );
+
+ if (previousDayData === undefined || todayData === undefined) {
+ setSuccess(false);
+ return;
+ }
+
+ if (previousDayData.result === 0) {
+ setSuccess(false);
+ return;
+ }
+
+ setSuccess(true);
+ setTrend(previousDayData.result < todayData.result ? "up" : "down");
+ setPercentage(
+ Math.abs(
+ Number(
+ (
+ ((todayData.result - previousDayData.result) /
+ previousDayData.result) *
+ 100
+ ).toFixed(1),
+ ),
+ ),
+ );
+ }, [data]);
+
+ return {
+ success,
+ trend,
+ percentage,
+ };
+}
diff --git a/src/lib/hooks/use-trend.tsx b/src/lib/hooks/use-trend.tsx
new file mode 100644
index 0000000..ff8a645
--- /dev/null
+++ b/src/lib/hooks/use-trend.tsx
@@ -0,0 +1,95 @@
+/*
+ * MHSF, Minehut Server List
+ * All external content is rather licensed under the ECA Agreement
+ * located here: https://mhsf.app/docs/legal/external-content-agreement
+ *
+ * All code under MHSF is licensed under the MIT License
+ * by open source contributors
+ *
+ * Copyright (c) 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 { useEffect, useState } from "react";
+
+const months = [
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+];
+
+export function useTrend(data: { month: string; result: number }[]) {
+ const [success, setSuccess] = useState(true);
+ const [trend, setTrend] = useState<"up" | "down">("up");
+ const [percentage, setPercentage] = useState(0);
+
+ useEffect(() => {
+ const today = new Date();
+ const previousMonth = new Date();
+ previousMonth.setDate(0);
+
+ const previousMonthData = data.find(
+ (x) => x.month === months[previousMonth.getMonth()],
+ );
+ const todayMonthData = data.find(
+ (x) => x.month === months[today.getMonth()],
+ );
+
+ if (previousMonthData === undefined || todayMonthData === undefined) {
+ setSuccess(false);
+ return;
+ }
+
+ if (previousMonthData.result === 0) {
+ setSuccess(false);
+ return;
+ }
+
+ setSuccess(true);
+ setTrend(previousMonthData.result < todayMonthData.result ? "up" : "down");
+ setPercentage(
+ Math.abs(
+ Number(
+ (
+ ((todayMonthData.result - previousMonthData.result) /
+ previousMonthData.result) *
+ 100
+ ).toFixed(1),
+ ),
+ ),
+ );
+ }, [data]);
+
+ return {
+ success,
+ trend,
+ percentage,
+ };
+}
diff --git a/src/pages/api/v0/account-linking/claim-account-code.ts b/src/pages/api/v0/account-linking/claim-account-code.ts
index c8c422a..53e5b81 100644
--- a/src/pages/api/v0/account-linking/claim-account-code.ts
+++ b/src/pages/api/v0/account-linking/claim-account-code.ts
@@ -50,7 +50,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("auth_codes");
const entry = await collection.findOne({ code });
diff --git a/src/pages/api/v0/account-linking/is-owned.ts b/src/pages/api/v0/account-linking/is-owned.ts
index 18a89ba..e3627fe 100644
--- a/src/pages/api/v0/account-linking/is-owned.ts
+++ b/src/pages/api/v0/account-linking/is-owned.ts
@@ -45,7 +45,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("owned-servers");
res.send({ owned: (await collection.findOne({ server })) != undefined });
diff --git a/src/pages/api/v0/account-linking/own-server.ts b/src/pages/api/v0/account-linking/own-server.ts
index 32476ce..93138ab 100644
--- a/src/pages/api/v0/account-linking/own-server.ts
+++ b/src/pages/api/v0/account-linking/own-server.ts
@@ -57,7 +57,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("owned-servers");
if ((await collection.findOne({ server: server })) == undefined) {
diff --git a/src/pages/api/v0/account-linking/owned-user.ts b/src/pages/api/v0/account-linking/owned-user.ts
index 7e58570..23603f9 100644
--- a/src/pages/api/v0/account-linking/owned-user.ts
+++ b/src/pages/api/v0/account-linking/owned-user.ts
@@ -56,7 +56,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("owned-servers");
res.send({
diff --git a/src/pages/api/v0/account-linking/unlink-account.ts b/src/pages/api/v0/account-linking/unlink-account.ts
index e190b42..12384c4 100644
--- a/src/pages/api/v0/account-linking/unlink-account.ts
+++ b/src/pages/api/v0/account-linking/unlink-account.ts
@@ -44,7 +44,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const users = db.collection("claimed-users");
const user = await (await clerkClient()).users.getUser(userId);
diff --git a/src/pages/api/v0/account-linking/unown-server.ts b/src/pages/api/v0/account-linking/unown-server.ts
index 4b56d50..fb460b0 100644
--- a/src/pages/api/v0/account-linking/unown-server.ts
+++ b/src/pages/api/v0/account-linking/unown-server.ts
@@ -56,7 +56,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("owned-servers");
const customization = db.collection("customization");
diff --git a/src/pages/api/v0/achievements/[server].ts b/src/pages/api/v0/achievements/[server].ts
index f505f66..e423386 100644
--- a/src/pages/api/v0/achievements/[server].ts
+++ b/src/pages/api/v0/achievements/[server].ts
@@ -41,7 +41,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("achievements");
res.send({ result: await collection.find({ name: server }).toArray() });
diff --git a/src/pages/api/v0/customization/[server]/get.ts b/src/pages/api/v0/customization/[server]/get.ts
index 338d3aa..9ea526d 100644
--- a/src/pages/api/v0/customization/[server]/get.ts
+++ b/src/pages/api/v0/customization/[server]/get.ts
@@ -39,7 +39,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("customization");
res.send({ results: await collection.findOne({ server }) });
diff --git a/src/pages/api/v0/customization/[server]/set.ts b/src/pages/api/v0/customization/[server]/set.ts
index ed72e07..267da90 100644
--- a/src/pages/api/v0/customization/[server]/set.ts
+++ b/src/pages/api/v0/customization/[server]/set.ts
@@ -86,7 +86,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("owned-servers");
const customizationColl = db.collection("customization");
if (!((await collection.findOne({ server, author: userId })) == undefined)) {
diff --git a/src/pages/api/v0/customization/getList.ts b/src/pages/api/v0/customization/getList.ts
index 46c8c37..c212eab 100644
--- a/src/pages/api/v0/customization/getList.ts
+++ b/src/pages/api/v0/customization/getList.ts
@@ -44,7 +44,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("customization");
const results: { server: string; customization: any }[] = [];
diff --git a/src/pages/api/v0/favorites/[server]/community-favorites.ts b/src/pages/api/v0/favorites/[server]/community-favorites.ts
index 451690e..5553d02 100644
--- a/src/pages/api/v0/favorites/[server]/community-favorites.ts
+++ b/src/pages/api/v0/favorites/[server]/community-favorites.ts
@@ -40,7 +40,7 @@ export default async function handler(
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("meta");
const find = await collection.find({ server: server }).toArray();
diff --git a/src/pages/api/v0/favorites/[server]/favorite-server.ts b/src/pages/api/v0/favorites/[server]/favorite-server.ts
index 0c86129..50f1fe3 100644
--- a/src/pages/api/v0/favorites/[server]/favorite-server.ts
+++ b/src/pages/api/v0/favorites/[server]/favorite-server.ts
@@ -46,7 +46,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("favorites");
const find = await collection.find({ user: userId }).toArray();
diff --git a/src/pages/api/v0/favorites/[server]/favorited.ts b/src/pages/api/v0/favorites/[server]/favorited.ts
index 9c28700..6c9ff16 100644
--- a/src/pages/api/v0/favorites/[server]/favorited.ts
+++ b/src/pages/api/v0/favorites/[server]/favorited.ts
@@ -45,7 +45,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("favorites");
const find = await collection.find({ user: userId }).toArray();
if (find.length == 0) res.send({ result: false });
diff --git a/src/pages/api/v0/favorites/account-favorites.ts b/src/pages/api/v0/favorites/account-favorites.ts
index 8ac1147..d50cbaa 100644
--- a/src/pages/api/v0/favorites/account-favorites.ts
+++ b/src/pages/api/v0/favorites/account-favorites.ts
@@ -44,7 +44,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("favorites");
const find = await collection.find({ user: userId }).toArray();
diff --git a/src/pages/api/v0/history/[server]/get-daily-data.ts b/src/pages/api/v0/history/[server]/get-daily-data.ts
new file mode 100644
index 0000000..f5b95ad
--- /dev/null
+++ b/src/pages/api/v0/history/[server]/get-daily-data.ts
@@ -0,0 +1,62 @@
+/*
+ * 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 { MongoClient } from "mongodb";
+import { NextApiRequest, NextApiResponse } from "next";
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ const client = new MongoClient(process.env.MONGO_DB as string);
+ const db = client.db("mhsf").collection("history");
+ const server = req.query.server as string;
+
+ const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
+ const result = await Promise.all([1,2,3,4,5,6,7].map(async (c) => {
+ const results = await db.find({
+ $and: [
+ { server },
+ { $expr: { $eq: [{ $dayOfWeek: "$date" }, c] } }
+ ]
+ }).toArray()
+
+ if (results.length !== 0) {
+ const averageNums = (results as any as {player_count: number}[]).map((x: {player_count: number}) => x.player_count)
+ const average = averageNums.reduce((sum, val) => sum + val, 0) / averageNums.length;
+
+ return { day: daysOfWeek[c - 1], result: Math.floor(average) };
+ }
+ return undefined;
+ }));
+
+ client.close()
+ res.send({result: result.filter((c) => c !== undefined)});
+}
\ No newline at end of file
diff --git a/src/pages/api/v0/history/[server]/get-monthly-data.ts b/src/pages/api/v0/history/[server]/get-monthly-data.ts
new file mode 100644
index 0000000..b45ddd9
--- /dev/null
+++ b/src/pages/api/v0/history/[server]/get-monthly-data.ts
@@ -0,0 +1,62 @@
+/*
+ * 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 { MongoClient } from "mongodb";
+import { NextApiRequest, NextApiResponse } from "next";
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ const client = new MongoClient(process.env.MONGO_DB as string);
+ const db = client.db("mhsf").collection("history");
+ const server = req.query.server as string;
+
+ const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
+ const result = await Promise.all([1,2,3,4,5,6,7,8,9,10,11,12].map(async (c) => {
+ const results = await db.find({
+ $and: [
+ { server },
+ { date: { $gte: new Date(new Date().getFullYear(), c - 1, 1), $lt: new Date(new Date().getFullYear(), c, 1) } }
+ ]
+ }).toArray()
+
+ if (results.length !== 0) {
+ const averageNums = (results as any as {player_count: number}[]).map((x: {player_count: number}) => x.player_count)
+ const average = averageNums.reduce((sum, val) => sum + val, 0) / averageNums.length;
+
+ return { month: months[c - 1], result: Math.floor(average) };
+ }
+ return undefined;
+ }));
+
+ client.close()
+ res.send({result: result.filter((c) => c !== undefined)});
+}
\ No newline at end of file
diff --git a/src/pages/api/v0/sorting/favorites.ts b/src/pages/api/v0/sorting/favorites.ts
index 17a1cff..df461f3 100644
--- a/src/pages/api/v0/sorting/favorites.ts
+++ b/src/pages/api/v0/sorting/favorites.ts
@@ -38,7 +38,7 @@ export default async function handler(
const client = new MongoClient(process.env.MONGO_DB as string);
await client.connect();
- const db = client.db("mhsf");
+ const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
const collection = db.collection("meta");
const all = await collection.find().toArray();