feat(ssr): add sitemap generator

This commit is contained in:
Lee 2023-11-07 23:05:11 +00:00
parent 4b966aaaae
commit 41d1d5f1d7
16 changed files with 129 additions and 16 deletions

3
.gitignore vendored

@ -42,3 +42,6 @@ next-env.d.ts
# Webpack bundle analyzer
analyze
# Sitemap
public/sitemap*

@ -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"

54
next-sitemap.config.js Normal file

@ -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;
},
};

43
package-lock.json generated

@ -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",

@ -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",

@ -1,2 +1,9 @@
# *
User-agent: *
Allow: /
# Host
Host: https://ssr.fascinated.cc
# Sitemaps
Sitemap: https://ssr.fascinated.cc/sitemap.xml

@ -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";

@ -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";

@ -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";

@ -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";

@ -125,7 +125,8 @@ export default function GlobalRanking({ page, country }: GlobalRankingProps) {
<p>
You are viewing{" "}
{country
? "scores from " + normalizedRegionName(country)
? "scores from " +
normalizedRegionName(country.toUpperCase())
: "Global scores"}
</p>
</div>

6
src/ssrSettings.json Normal file

@ -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"
}

@ -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",
};

@ -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";

@ -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";

@ -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";