Add performance optimizations and component refactors
CI / check (push) Successful in 34s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m23s

- Cache read-heavy public API routes via redisCache (leaderboard, values,
  shop, articles, photos, guilds, teams, staff, users, home, radio, badges)
- Add single-flight and bounded-memory cache layer with unit tests
- Parallelize independent DB queries on search, rares, shop, staff, polls
  and profile pages
- Push radio points leaderboard aggregation to SQL with a LIMIT
- Split studio-client and import-furni-client into focused modules
- Clean up next.config.ts
This commit is contained in:
openhands committed 2026-08-17 22:02:21 +02:00
1 parent 54f2bc5e0c
commit f285a7cd98
42 files changed
+1616 -1205

No files matched your search

+214 -150
View File
@@ -5,6 +5,7 @@ import { bearerUserId } from "@/lib/api-auth";
import { auth } from "@/lib/auth";
import { db, User, UsersSettings } from "@/lib/db";
import { logger } from "@/lib/logger";
import { apiCacheKey, cacheSafe, redisCache } from "@/lib/redis-cache";
type BadgeRarityKey =
| "common"
@@ -28,6 +29,12 @@ interface BadgeLeaderboardBoard {
viewerEntry?: Partial<BadgeLeaderboardEntry>;
}
/** The shared, viewer-independent part of a leaderboard (cacheable). */
type BoardCore = {
entries: BadgeLeaderboardEntry[];
totalPlayers: number;
};
interface BadgeLeaderboardStat {
badgeCode: string;
ownerCount: number;
@@ -65,9 +72,7 @@ async function rawRows<T>(query: ReturnType<typeof sql>): Promise<T[]> {
return rows;
}
async function loadTotalBadgesBoard(
userId: number | null,
): Promise<BadgeLeaderboardBoard> {
async function loadTotalBadgesBoard(): Promise<BoardCore> {
const rows = await rawRows<{
userId: bigint;
username: string;
@@ -103,48 +108,10 @@ async function loadTotalBadgesBoard(
)
: 0;
let viewerEntry: Partial<BadgeLeaderboardEntry> | undefined;
if (userId) {
const viewerRow = await rawRows<{ cnt: bigint }>(
sql`SELECT COUNT(*) AS cnt FROM users_badges WHERE user_id = ${userId}`,
);
const viewerScore = Number(viewerRow[0]?.cnt ?? 0);
if (viewerScore > 0) {
const viewerRank =
entries.length > 0
? Number(
(
await rawRows<{ cnt: bigint }>(sql`
SELECT COUNT(*) + 1 AS cnt
FROM (SELECT user_id, COUNT(*) AS total FROM users_badges GROUP BY user_id) t
WHERE t.total > ${viewerScore}
`)
)[0]?.cnt ?? entries.length + 1,
)
: 1;
const [viewerUser] = await db
.select({ username: User.username, look: User.look })
.from(User)
.where(eq(User.id, userId))
.limit(1);
if (viewerUser) {
viewerEntry = {
userId,
username: viewerUser.username,
figure: viewerUser.look,
score: viewerScore,
rank: viewerRank,
};
}
}
}
return { entries, totalPlayers, viewerEntry };
return { entries, totalPlayers };
}
async function loadAchievementBoard(
userId: number | null,
): Promise<BadgeLeaderboardBoard> {
async function loadAchievementBoard(): Promise<BoardCore> {
const rows = await rawRows<{
userId: bigint;
username: string;
@@ -179,46 +146,10 @@ async function loadAchievementBoard(
)
: 0;
let viewerEntry: Partial<BadgeLeaderboardEntry> | undefined;
if (userId) {
const [viewerUser] = await db
.select({ username: User.username, look: User.look })
.from(User)
.where(eq(User.id, userId))
.limit(1);
if (viewerUser) {
const [viewerSettings] = await db
.select({ achievementScore: UsersSettings.achievementScore })
.from(UsersSettings)
.where(eq(UsersSettings.userId, userId))
.limit(1);
if (viewerSettings && viewerSettings.achievementScore > 0) {
const viewerRank = Number(
(
await rawRows<{ cnt: bigint }>(sql`
SELECT COUNT(*) + 1 AS cnt FROM users_settings WHERE achievement_score > ${viewerSettings.achievementScore}
`)
)[0]?.cnt ?? entries.length + 1,
);
viewerEntry = {
userId,
username: viewerUser.username,
figure: viewerUser.look,
score: viewerSettings.achievementScore,
rank: viewerRank,
};
}
}
}
return { entries, totalPlayers, viewerEntry };
return { entries, totalPlayers };
}
async function loadRarityBoard(
_rarity: BadgeRarityKey,
badgeCodes: string[],
userId: number | null,
): Promise<BadgeLeaderboardBoard> {
async function loadRarityBoard(badgeCodes: string[]): Promise<BoardCore> {
if (badgeCodes.length === 0) return { entries: [], totalPlayers: 0 };
const codes = sql.join(
@@ -262,41 +193,111 @@ async function loadRarityBoard(
)
: 0;
let viewerEntry: Partial<BadgeLeaderboardEntry> | undefined;
if (userId) {
const viewerRow = await rawRows<{ cnt: bigint }>(sql`
SELECT COUNT(*) AS cnt FROM users_badges
WHERE user_id = ${userId} AND badge_code IN (${codes})
`);
const viewerScore = Number(viewerRow[0]?.cnt ?? 0);
if (viewerScore > 0) {
const [viewerUser] = await db
.select({ username: User.username, look: User.look })
.from(User)
.where(eq(User.id, userId))
.limit(1);
if (viewerUser) {
const viewerRank = Number(
(
await rawRows<{ cnt: bigint }>(sql`
SELECT COUNT(*) + 1 AS cnt
FROM (SELECT user_id, COUNT(*) AS total FROM users_badges WHERE badge_code IN (${codes}) GROUP BY user_id) t
WHERE t.total > ${viewerScore}
`)
)[0]?.cnt ?? entries.length + 1,
);
viewerEntry = {
userId,
username: viewerUser.username,
figure: viewerUser.look,
score: viewerScore,
rank: viewerRank,
};
}
}
}
return { entries, totalPlayers };
}
return { entries, totalPlayers, viewerEntry };
async function loadTotalBadgesViewer(
userId: number,
): Promise<Partial<BadgeLeaderboardEntry> | undefined> {
const viewerRow = await rawRows<{ cnt: bigint }>(
sql`SELECT COUNT(*) AS cnt FROM users_badges WHERE user_id = ${userId}`,
);
const viewerScore = Number(viewerRow[0]?.cnt ?? 0);
if (viewerScore <= 0) return undefined;
const rankRow = await rawRows<{ cnt: bigint }>(sql`
SELECT COUNT(*) + 1 AS cnt
FROM (SELECT user_id, COUNT(*) AS total FROM users_badges GROUP BY user_id) t
WHERE t.total > ${viewerScore}
`);
const rank = Number(rankRow[0]?.cnt ?? 1);
const [viewerUser] = await db
.select({ username: User.username, look: User.look })
.from(User)
.where(eq(User.id, userId))
.limit(1);
if (!viewerUser) return undefined;
return {
userId,
username: viewerUser.username,
figure: viewerUser.look,
score: viewerScore,
rank,
};
}
async function loadAchievementViewer(
userId: number,
): Promise<Partial<BadgeLeaderboardEntry> | undefined> {
const [viewerUser] = await db
.select({ username: User.username, look: User.look })
.from(User)
.where(eq(User.id, userId))
.limit(1);
if (!viewerUser) return undefined;
const [viewerSettings] = await db
.select({ achievementScore: UsersSettings.achievementScore })
.from(UsersSettings)
.where(eq(UsersSettings.userId, userId))
.limit(1);
if (!viewerSettings || viewerSettings.achievementScore <= 0) return undefined;
const rankRow = await rawRows<{ cnt: bigint }>(sql`
SELECT COUNT(*) + 1 AS cnt FROM users_settings WHERE achievement_score > ${viewerSettings.achievementScore}
`);
const rank = Number(rankRow[0]?.cnt ?? 1);
return {
userId,
username: viewerUser.username,
figure: viewerUser.look,
score: viewerSettings.achievementScore,
rank,
};
}
async function loadRarityViewer(
userId: number,
badgeCodes: string[],
): Promise<Partial<BadgeLeaderboardEntry> | undefined> {
if (badgeCodes.length === 0) return undefined;
const codes = sql.join(
badgeCodes.map((c) => sql`${c}`),
sql`, `,
);
const viewerRow = await rawRows<{ cnt: bigint }>(sql`
SELECT COUNT(*) AS cnt FROM users_badges
WHERE user_id = ${userId} AND badge_code IN (${codes})
`);
const viewerScore = Number(viewerRow[0]?.cnt ?? 0);
if (viewerScore <= 0) return undefined;
const [viewerUser] = await db
.select({ username: User.username, look: User.look })
.from(User)
.where(eq(User.id, userId))
.limit(1);
if (!viewerUser) return undefined;
const rankRow = await rawRows<{ cnt: bigint }>(sql`
SELECT COUNT(*) + 1 AS cnt
FROM (SELECT user_id, COUNT(*) AS total FROM users_badges WHERE badge_code IN (${codes}) GROUP BY user_id) t
WHERE t.total > ${viewerScore}
`);
const rank = Number(rankRow[0]?.cnt ?? 1);
return {
userId,
username: viewerUser.username,
figure: viewerUser.look,
score: viewerScore,
rank,
};
}
export async function GET(req: Request) {
@@ -308,28 +309,6 @@ export async function GET(req: Request) {
userId = session?.user?.id ? Number(session.user.id) : null;
}
const badgeStatsRaw = await rawRows<{
badgeCode: string;
ownerCount: bigint;
}>(sql`
SELECT badge_code AS badgeCode, COUNT(DISTINCT user_id) AS ownerCount
FROM users_badges
GROUP BY badge_code
`);
const badgeStats: BadgeLeaderboardStat[] = badgeStatsRaw.map((r) => ({
badgeCode: r.badgeCode,
ownerCount: Number(r.ownerCount),
rarity: assignRarity(Number(r.ownerCount)),
}));
const badgesByRarity = new Map<BadgeRarityKey, string[]>();
for (const stat of badgeStats) {
const list = badgesByRarity.get(stat.rarity) ?? [];
list.push(stat.badgeCode);
badgesByRarity.set(stat.rarity, list);
}
const rarityKeys: BadgeRarityKey[] = [
"common",
"rare",
@@ -339,24 +318,109 @@ export async function GET(req: Request) {
"unique",
];
const [totalBadges, achievementLevel, ...rarityBoards] = await Promise.all([
loadTotalBadgesBoard(userId),
loadAchievementBoard(userId),
...rarityKeys.map((rk) =>
loadRarityBoard(rk, badgesByRarity.get(rk) ?? [], userId),
),
]);
// The heavy global aggregation (badge rarity stats + the 8 leaderboards)
// is user-independent, so it is cached and recomputed once per TTL instead
// of on every request. The viewer's own rank is derived per request.
const global = await redisCache(
apiCacheKey("badges:leaderboard:global"),
120,
async () => {
const badgeStatsRaw = await rawRows<{
badgeCode: string;
ownerCount: bigint;
}>(sql`
SELECT badge_code AS badgeCode, COUNT(DISTINCT user_id) AS ownerCount
FROM users_badges
GROUP BY badge_code
`);
const rarity = Object.fromEntries(
// eslint-disable-next-line security/detect-object-injection -- rk from rarityKeys const, i is array index
rarityKeys.map((rk, i) => [rk, rarityBoards[i]]),
) as Record<BadgeRarityKey, BadgeLeaderboardBoard>;
const badgeStats: BadgeLeaderboardStat[] = badgeStatsRaw.map((r) => ({
badgeCode: r.badgeCode,
ownerCount: Number(r.ownerCount),
rarity: assignRarity(Number(r.ownerCount)),
}));
const badgesByRarity = new Map<BadgeRarityKey, string[]>();
for (const stat of badgeStats) {
const list = badgesByRarity.get(stat.rarity) ?? [];
list.push(stat.badgeCode);
badgesByRarity.set(stat.rarity, list);
}
const [totalBadges, achievementLevel, ...rarityBoards] =
await Promise.all([
loadTotalBadgesBoard(),
loadAchievementBoard(),
...rarityKeys.map((rk) =>
loadRarityBoard(badgesByRarity.get(rk) ?? []),
),
]);
const rarity = Object.fromEntries(
// eslint-disable-next-line security/detect-object-injection -- rk from rarityKeys const, i is array index
rarityKeys.map((rk, i) => [rk, rarityBoards[i]]),
) as Record<BadgeRarityKey, BoardCore>;
return cacheSafe({ badgeStats, totalBadges, achievementLevel, rarity });
},
);
// Per-viewer rank entries (only when a user is signed in).
let totalBadgesViewer: Partial<BadgeLeaderboardEntry> | undefined;
let achievementViewer: Partial<BadgeLeaderboardEntry> | undefined;
let rarityViewer: Record<
BadgeRarityKey,
Partial<BadgeLeaderboardEntry> | undefined
> = {
common: undefined,
rare: undefined,
epic: undefined,
legendary: undefined,
mythical: undefined,
unique: undefined,
};
if (userId) {
const badgesByRarity = new Map<BadgeRarityKey, string[]>();
for (const stat of global.badgeStats) {
const list = badgesByRarity.get(stat.rarity) ?? [];
list.push(stat.badgeCode);
badgesByRarity.set(stat.rarity, list);
}
const [tb, ach, ...rvs] = await Promise.all([
loadTotalBadgesViewer(userId),
loadAchievementViewer(userId),
...rarityKeys.map((rk) =>
loadRarityViewer(userId, badgesByRarity.get(rk) ?? []),
),
]);
totalBadgesViewer = tb;
achievementViewer = ach;
rarityViewer = Object.fromEntries(
// eslint-disable-next-line security/detect-object-injection -- rk from rarityKeys const, i is array index
rarityKeys.map((rk, i) => [rk, rvs[i]]),
) as Record<BadgeRarityKey, Partial<BadgeLeaderboardEntry> | undefined>;
}
return apiJson({
viewerUserId: userId ?? 0,
badgeStats,
badgeStats: global.badgeStats,
thresholds: THRESHOLDS,
leaderboards: { totalBadges, achievementLevel, rarity },
leaderboards: {
totalBadges: { ...global.totalBadges, viewerEntry: totalBadgesViewer },
achievementLevel: {
...global.achievementLevel,
viewerEntry: achievementViewer,
},
rarity: Object.fromEntries(
// eslint-disable-next-line security/detect-object-injection -- rk from rarityKeys const
rarityKeys.map((rk) => [
rk,
{ ...global.rarity[rk], viewerEntry: rarityViewer[rk] },
]),
) as Record<BadgeRarityKey, BadgeLeaderboardBoard>,
},
});
} catch (err) {
logger.error("Badge leaderboard error", {