diff --git a/src/server.ts b/src/server.ts
index 8082e73..adf006c 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -54,6 +54,7 @@ import { GAME_SERVER_PORT } from "./game/udp";
import { sscRouter } from "./ssc/routes";
import { getCurrentCRC, LoadConfig, MATCHMAKING_CRC } from "./data/config";
import { PlayerTester, PlayerTesterModel } from "./database/PlayerTester";
+import { PlayerStatsModel } from "./database/PlayerStats";
import { Types } from "mongoose";
import { RegExpMatcher, TextCensor, englishDataset, englishRecommendedTransformers, asteriskCensorStrategy } from "obscenity";
import env from "./env/env";
@@ -239,6 +240,10 @@ const homeFilePath = path.join(__dirname, "static/home.html");
const homeSource = fs.readFileSync(homeFilePath, "utf8");
const homeTemplate = handlebars.compile(homeSource);
+const myStatsFilePath = path.join(__dirname, "static/my_stats.html");
+const myStatsSource = fs.readFileSync(myStatsFilePath, "utf8");
+const myStatsTemplate = handlebars.compile(myStatsSource);
+
const adminBannerFilePath = path.join(__dirname, "static/admin.html");
const adminBannerSource = fs.readFileSync(adminBannerFilePath, "utf8");
const adminBannerTemplate = handlebars.compile(adminBannerSource);
@@ -253,6 +258,47 @@ app.get("/home", async (req, res) => {
}
});
+// ── /stats — career-stats page for the caller's IP-resolved account ──
+// Mirrors the /namechange flow: caller's IP → resolvePlayerForWeb → PlayerStats.
+// If multiple accounts at the IP, the account picker is shown.
+// Public, no auth.
+app.get("/stats", async (req, res) => {
+ try {
+ const { player, pickerShown } = await resolvePlayerForWeb(req, res, "/stats");
+ if (pickerShown) return;
+
+ if (!player) {
+ const html = myStatsTemplate({ hasStats: false });
+ res.send(html);
+ return;
+ }
+
+ const accountId = String((player as any)._id);
+ const stats = await PlayerStatsModel.findOne({ account_id: accountId }).lean();
+
+ if (!stats || !stats.aggregate || Object.keys(stats.aggregate).length === 0) {
+ const html = myStatsTemplate({ hasStats: false, playerName: (player as any).name || "" });
+ res.send(html);
+ return;
+ }
+
+ // Stringify the aggregate as JSON for the inline
+
+ {{/if}}
+