From 270f6efaa6c8d379324969d03a1358a24acede24 Mon Sep 17 00:00:00 2001 From: dvelo <52332868+DeveloLongScript@users.noreply.github.com> Date: Fri, 3 Jan 2025 13:40:13 -0600 Subject: [PATCH] fix: issue --- CONTRIBUTING.md | 1 + .../server/[server]/statistics/page.tsx | 7 + src/components/ServerList.tsx | 16 +- src/components/ServerView.tsx | 3 +- src/components/charts/DailyChart.tsx | 174 ++++++++++++++++ src/components/charts/ExampleChart.tsx | 191 ++++++++++++++++++ src/components/charts/MonthlyChart.tsx | 160 +++++++++++++++ src/components/charts/NewChart.tsx | 4 +- src/components/misc/MiniJoinsChart.tsx | 2 +- src/lib/api.ts | 22 ++ src/lib/hooks/use-daily-trend.tsx | 80 ++++++++ src/lib/hooks/use-trend.tsx | 95 +++++++++ .../v0/account-linking/claim-account-code.ts | 2 +- src/pages/api/v0/account-linking/is-owned.ts | 2 +- .../api/v0/account-linking/own-server.ts | 2 +- .../api/v0/account-linking/owned-user.ts | 2 +- .../api/v0/account-linking/unlink-account.ts | 2 +- .../api/v0/account-linking/unown-server.ts | 2 +- src/pages/api/v0/achievements/[server].ts | 2 +- .../api/v0/customization/[server]/get.ts | 2 +- .../api/v0/customization/[server]/set.ts | 2 +- src/pages/api/v0/customization/getList.ts | 2 +- .../favorites/[server]/community-favorites.ts | 2 +- .../v0/favorites/[server]/favorite-server.ts | 2 +- .../api/v0/favorites/[server]/favorited.ts | 2 +- .../api/v0/favorites/account-favorites.ts | 2 +- .../api/v0/history/[server]/get-daily-data.ts | 62 ++++++ .../v0/history/[server]/get-monthly-data.ts | 62 ++++++ src/pages/api/v0/sorting/favorites.ts | 2 +- 29 files changed, 887 insertions(+), 22 deletions(-) create mode 100644 src/components/charts/DailyChart.tsx create mode 100644 src/components/charts/ExampleChart.tsx create mode 100644 src/components/charts/MonthlyChart.tsx create mode 100644 src/lib/hooks/use-daily-trend.tsx create mode 100644 src/lib/hooks/use-trend.tsx create mode 100644 src/pages/api/v0/history/[server]/get-daily-data.ts create mode 100644 src/pages/api/v0/history/[server]/get-monthly-data.ts 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. +

+ )}
diff --git a/src/components/ServerView.tsx b/src/components/ServerView.tsx index f305283..f2e498b 100644 --- a/src/components/ServerView.tsx +++ b/src/components/ServerView.tsx @@ -121,12 +121,13 @@ export default function ServerView(props: { server: string }) { <> {single.grabOnline() == undefined && !single.grabOffline()?.online && (
+
- This server is offline + This server is offline

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 {trend} by {percentage}% today{" "} + {trend === "up" ? ( + + ) : ( + + )} +
+ ) : ( +
+ 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 ? ( +
+ Trending {trend} by {percentage}% this month{" "} + {trend === "up" ? ( + + ) : ( + + )} +
+ ) : ( +
+ Trending up by 0% this month{" "} + (Insufficient data) +
+ )} +
+ Showing an average of all data for {server} +
+
+
+ ); +} diff --git a/src/components/charts/NewChart.tsx b/src/components/charts/NewChart.tsx index 7e592bb..dd27f66 100644 --- a/src/components/charts/NewChart.tsx +++ b/src/components/charts/NewChart.tsx @@ -134,7 +134,7 @@ export function NewChart({ server }: { server: string }) { {chartConfig[activeChart].label} Chart for {server} - Showing {filter !== "all" && <>the last}{" "} + Showing {filter !== "all" && "the last"}{" "} {" "} - {filter === "all" && <>of the} entries. + {filter === "all" && "of the"} entries.
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();