diff --git a/.gitignore b/.gitignore index bbe04ac..5d9f6aa 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ next-env.d.ts # Webpack bundle analyzer analyze + +# Sitemap +public/sitemap* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index d020136..e58426f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,9 @@ COPY --from=builder --chown=nextjs:nodejs /app/public ./public COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/next.config.js ./next.config.js +# Generate sitemap +RUN npm run generate-sitemap + USER nextjs EXPOSE 80 ENV HOSTNAME "0.0.0.0" diff --git a/next-sitemap.config.js b/next-sitemap.config.js new file mode 100644 index 0000000..3ba8b55 --- /dev/null +++ b/next-sitemap.config.js @@ -0,0 +1,54 @@ +const ssrSettings = require("./src/ssrSettings"); +const { getCodeList } = require("country-list"); + +const SS_API_URL = ssrSettings.proxy + "/https://scoresaber.com/api"; +const SS_GET_PLAYERS_URL = SS_API_URL + "/players?page={}"; + +async function getTopPlayers() { + console.log("Fetching top players..."); + const players = []; + const pagesToFetch = 25; + for (let i = 0; i < pagesToFetch; i++) { + console.log(`Fetching page ${i + 1} of ${pagesToFetch}...`); + const response = await fetch(SS_GET_PLAYERS_URL.replace("{}", i)); + const data = await response.json(); + players.push(...data.players); + } + console.log("Done fetching top players."); + return players; +} + +/** @type {import('next-sitemap').IConfig} */ +module.exports = { + siteUrl: ssrSettings.siteUrl, + generateRobotsTxt: true, + additionalPaths: async (config) => { + const paths = []; + // Add the top 50 global ranking pages + for (let i = 0; i < 50; i++) { + paths.push({ + loc: `/ranking/global/${i + 1}`, + }); + } + + // Add the top 50 pages for all countries + const countries = Object.keys(getCodeList()); + for (const country of countries) { + for (let i = 0; i < 50; i++) { + paths.push({ + loc: `/ranking/country/${country}/${i + 1}`, + }); + } + } + + // Add top players + const players = await getTopPlayers(); + for (const player of players) { + paths.push({ + loc: `/player/${player.id}/top/1`, + }); + } + + return paths; + }, +}; diff --git a/package-lock.json b/package-lock.json index 6a583fb..e8f5710 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,12 +23,14 @@ "chart.js": "^4.4.0", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", + "country-list": "^2.3.0", "date-fns": "^2.30.0", "encoding": "^0.1.13", "idb-keyval": "^6.2.1", "lucide-react": "^0.292.0", "next": "14.0.1", "next-build-id": "^3.0.0", + "next-sitemap": "^4.2.3", "next-themes": "^0.2.1", "node-fetch-cache": "^3.1.3", "react": "^18", @@ -95,6 +97,11 @@ "resolved": "https://registry.npmjs.org/@boiseitguru/cookie-cutter/-/cookie-cutter-0.2.1.tgz", "integrity": "sha512-MLYTqtyECvjq+Wjq2k++bXReIlClTrtpSWSXeamp868MWKggn6SanFBZRVhI61isf3ETuvmDJObexW36Ck4SCQ==" }, + "node_modules/@corex/deepmerge": { + "version": "4.0.43", + "resolved": "https://registry.npmjs.org/@corex/deepmerge/-/deepmerge-4.0.43.tgz", + "integrity": "sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2464,6 +2471,11 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/country-list": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/country-list/-/country-list-2.3.0.tgz", + "integrity": "sha512-qZk66RlmQm7fQjMYWku1AyjlKPogjPEorAZJG88owPExoPV8EsyCcuFLvO2afTXHEhi9liVOoyd+5A6ZS5QwaA==" + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -4788,6 +4800,37 @@ "node": ">=8" } }, + "node_modules/next-sitemap": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/next-sitemap/-/next-sitemap-4.2.3.tgz", + "integrity": "sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==", + "funding": [ + { + "url": "https://github.com/iamvishnusankar/next-sitemap.git" + } + ], + "dependencies": { + "@corex/deepmerge": "^4.0.43", + "@next/env": "^13.4.3", + "fast-glob": "^3.2.12", + "minimist": "^1.2.8" + }, + "bin": { + "next-sitemap": "bin/next-sitemap.mjs", + "next-sitemap-cjs": "bin/next-sitemap.cjs" + }, + "engines": { + "node": ">=14.18" + }, + "peerDependencies": { + "next": "*" + } + }, + "node_modules/next-sitemap/node_modules/@next/env": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.6.tgz", + "integrity": "sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==" + }, "node_modules/next-themes": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", diff --git a/package.json b/package.json index 790e40c..53e1328 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "scripts": { "dev": "next dev", "build": "next build", + "generate-sitemap": "next-sitemap", "start": "next start", "lint": "next lint" }, @@ -24,12 +25,14 @@ "chart.js": "^4.4.0", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", + "country-list": "^2.3.0", "date-fns": "^2.30.0", "encoding": "^0.1.13", "idb-keyval": "^6.2.1", "lucide-react": "^0.292.0", "next": "14.0.1", "next-build-id": "^3.0.0", + "next-sitemap": "^4.2.3", "next-themes": "^0.2.1", "node-fetch-cache": "^3.1.3", "react": "^18", diff --git a/public/robots.txt b/public/robots.txt index 14267e9..a87031d 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,9 @@ +# * User-agent: * -Allow: / \ No newline at end of file +Allow: / + +# Host +Host: https://ssr.fascinated.cc + +# Sitemaps +Sitemap: https://ssr.fascinated.cc/sitemap.xml diff --git a/src/app/analytics/page.tsx b/src/app/analytics/page.tsx index 6574399..14b2e8a 100644 --- a/src/app/analytics/page.tsx +++ b/src/app/analytics/page.tsx @@ -2,7 +2,7 @@ import AnalyticsChart from "@/components/AnalyticsChart"; import Card from "@/components/Card"; import Container from "@/components/Container"; import { ScoresaberMetricsHistory } from "@/schemas/fascinated/scoresaberMetricsHistory"; -import { ssrSettings } from "@/ssrSettings"; +import ssrSettings from "@/ssrSettings.json"; import { formatNumber } from "@/utils/numberUtils"; import { isProduction } from "@/utils/utils"; import { Metadata } from "next"; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7ecbbb8..458b469 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,5 @@ import AppProvider from "@/components/AppProvider"; -import { ssrSettings } from "@/ssrSettings"; +import ssrSettings from "@/ssrSettings.json"; import clsx from "clsx"; import { Metadata } from "next"; import { Inter } from "next/font/google"; diff --git a/src/app/player/[id]/[sort]/[page]/page.tsx b/src/app/player/[id]/[sort]/[page]/page.tsx index f1af0c0..ffc9702 100644 --- a/src/app/player/[id]/[sort]/[page]/page.tsx +++ b/src/app/player/[id]/[sort]/[page]/page.tsx @@ -1,5 +1,5 @@ import PlayerPage from "@/components/player/PlayerPage"; -import { ssrSettings } from "@/ssrSettings"; +import ssrSettings from "@/ssrSettings.json"; import { formatNumber } from "@/utils/numberUtils"; import { ScoreSaberAPI } from "@/utils/scoresaber/api"; import { normalizedRegionName } from "@/utils/utils"; diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index bee132c..c3955f5 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,4 +1,4 @@ -import { ssrSettings } from "@/ssrSettings"; +import ssrSettings from "@/ssrSettings.json"; import { isProduction } from "@/utils/utils"; import Link from "next/link"; import Card from "./Card"; diff --git a/src/components/GlobalRanking.tsx b/src/components/GlobalRanking.tsx index a79478e..b09d2a3 100644 --- a/src/components/GlobalRanking.tsx +++ b/src/components/GlobalRanking.tsx @@ -125,7 +125,8 @@ export default function GlobalRanking({ page, country }: GlobalRankingProps) {

You are viewing{" "} {country - ? "scores from " + normalizedRegionName(country) + ? "scores from " + + normalizedRegionName(country.toUpperCase()) : "Global scores"}

diff --git a/src/ssrSettings.json b/src/ssrSettings.json new file mode 100644 index 0000000..bda4cdd --- /dev/null +++ b/src/ssrSettings.json @@ -0,0 +1,6 @@ +{ + "siteName": "ScoreSaber Reloaded", + "description": "Scoresaber Reloaded is a new way to view your scores and get more stats about your and your plays", + "siteUrl": "https://ssr.fascinated.cc", + "proxy": "https://proxy.fascinated.cc" +} diff --git a/src/ssrSettings.ts b/src/ssrSettings.ts deleted file mode 100644 index 4e06cdd..0000000 --- a/src/ssrSettings.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const ssrSettings = { - siteName: "ScoreSaber Reloaded", - description: - "Scoresaber Reloaded is a new way to view your scores and get more stats about your and your plays", - siteUrl: "https://ssr.fascinated.cc", - proxy: "https://proxy.fascinated.cc", -}; diff --git a/src/utils/beatleader/api.ts b/src/utils/beatleader/api.ts index 3d99efc..e0dc96f 100644 --- a/src/utils/beatleader/api.ts +++ b/src/utils/beatleader/api.ts @@ -1,6 +1,6 @@ import { BeatLeaderPlayer } from "@/schemas/beatleader/player"; import { BeatleaderScore } from "@/schemas/beatleader/score"; -import { ssrSettings } from "@/ssrSettings"; +import ssrSettings from "@/ssrSettings.json"; import { FetchQueue } from "../fetchWithQueue"; import { formatString } from "../string"; diff --git a/src/utils/beatsaver/api.ts b/src/utils/beatsaver/api.ts index 147fa86..2addb1e 100644 --- a/src/utils/beatsaver/api.ts +++ b/src/utils/beatsaver/api.ts @@ -1,5 +1,5 @@ import { BeatsaverMap } from "@/schemas/beatsaver/BeatsaverMap"; -import { ssrSettings } from "@/ssrSettings"; +import ssrSettings from "@/ssrSettings.json"; import { FetchQueue } from "../fetchWithQueue"; import { formatString } from "../string"; diff --git a/src/utils/scoresaber/api.ts b/src/utils/scoresaber/api.ts index 796b54e..3daba81 100644 --- a/src/utils/scoresaber/api.ts +++ b/src/utils/scoresaber/api.ts @@ -2,7 +2,7 @@ import { ScoresaberLeaderboardInfo } from "@/schemas/scoresaber/leaderboard"; import { ScoresaberPlayer } from "@/schemas/scoresaber/player"; import { ScoresaberPlayerScore } from "@/schemas/scoresaber/playerScore"; import { ScoresaberScore } from "@/schemas/scoresaber/score"; -import { ssrSettings } from "@/ssrSettings"; +import ssrSettings from "@/ssrSettings.json"; import { FetchQueue } from "../fetchWithQueue"; import { formatString } from "../string";