import type { GameState, ProfileSettings, UpdateProfileRequest, } from "@elysium/types"; import { DEFAULT_PROFILE_SETTINGS } from "@elysium/types"; import { Hono } from "hono"; import { prisma } from "../db/client.js"; import { authMiddleware } from "../middleware/auth.js"; export const profileRouter = new Hono(); const VALID_NUMBER_FORMATS = new Set(["suffix", "scientific", "engineering"]); const parseProfileSettings = (raw: unknown): ProfileSettings => { if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) { const obj = raw as Record; const numberFormat = VALID_NUMBER_FORMATS.has(obj.numberFormat as string) ? (obj.numberFormat as ProfileSettings["numberFormat"]) : "suffix"; return { showTotalGold: obj.showTotalGold !== false, showTotalClicks: obj.showTotalClicks !== false, showPrestige: obj.showPrestige !== false, showGuildFounded: obj.showGuildFounded !== false, showBossesDefeated: obj.showBossesDefeated !== false, showQuestsCompleted: obj.showQuestsCompleted !== false, showAdventurersRecruited: obj.showAdventurersRecruited !== false, showAchievementsUnlocked: obj.showAchievementsUnlocked !== false, numberFormat, }; } return { ...DEFAULT_PROFILE_SETTINGS }; }; profileRouter.get("/:discordId", async (context) => { const { discordId } = context.req.param(); const [player, gameStateRecord] = await Promise.all([ prisma.player.findUnique({ where: { discordId } }), prisma.gameState.findUnique({ where: { discordId } }), ]); if (!player) { return context.json({ error: "Player not found" }, 404); } const state = gameStateRecord?.state as unknown as GameState | undefined; const prestigeCount = state?.prestige.count ?? 0; const profileSettings = parseProfileSettings(player.profileSettings); const bossesDefeated = state?.bosses.filter((b) => b.status === "defeated").length ?? 0; const questsCompleted = state?.quests.filter((q) => q.status === "completed").length ?? 0; const adventurersRecruited = state?.adventurers.reduce((sum, a) => sum + a.count, 0) ?? 0; const achievementsUnlocked = (state?.achievements ?? []).filter((a) => a.unlockedAt !== null).length; return context.json({ characterName: player.characterName, username: player.username, avatar: player.avatar ?? null, bio: player.bio ?? "", profileSettings, prestigeCount, totalGoldEarned: player.totalGoldEarned, totalClicks: player.totalClicks, bossesDefeated, questsCompleted, adventurersRecruited, achievementsUnlocked, createdAt: player.createdAt, }); }); profileRouter.put("/", authMiddleware, async (context) => { const discordId = context.get("discordId") as string; const body = await context.req.json(); const characterName = (body.characterName ?? "").trim().slice(0, 32); const bio = (body.bio ?? "").trim().slice(0, 200); const numberFormat = VALID_NUMBER_FORMATS.has(body.profileSettings?.numberFormat as string) ? (body.profileSettings?.numberFormat as ProfileSettings["numberFormat"]) : "suffix"; const profileSettings: ProfileSettings = { showTotalGold: body.profileSettings?.showTotalGold !== false, showTotalClicks: body.profileSettings?.showTotalClicks !== false, showPrestige: body.profileSettings?.showPrestige !== false, showGuildFounded: body.profileSettings?.showGuildFounded !== false, showBossesDefeated: body.profileSettings?.showBossesDefeated !== false, showQuestsCompleted: body.profileSettings?.showQuestsCompleted !== false, showAdventurersRecruited: body.profileSettings?.showAdventurersRecruited !== false, showAchievementsUnlocked: body.profileSettings?.showAchievementsUnlocked !== false, numberFormat, }; if (!characterName) { return context.json({ error: "Character name cannot be empty" }, 400); } const updated = await prisma.player.update({ where: { discordId }, data: { characterName, bio, profileSettings: profileSettings as object }, }); return context.json({ characterName: updated.characterName, bio: updated.bio, profileSettings, }); });