Add performance optimizations and component refactors
- 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:
@@ -5,6 +5,7 @@
|
||||
import { count, eq } from "drizzle-orm";
|
||||
import { apiJson } from "@/lib/api";
|
||||
import { db, Guilds, GuildsMembers } from "@/lib/db";
|
||||
import { apiCacheKey, cacheSafe, redisCache } from "@/lib/redis-cache";
|
||||
|
||||
export async function GET(
|
||||
_req: Request,
|
||||
@@ -18,38 +19,50 @@ export async function GET(
|
||||
const guildId = Number(id);
|
||||
|
||||
try {
|
||||
const [guild] = await db
|
||||
.select({
|
||||
id: Guilds.id,
|
||||
name: Guilds.name,
|
||||
description: Guilds.description,
|
||||
userId: Guilds.userId,
|
||||
roomId: Guilds.roomId,
|
||||
badge: Guilds.badge,
|
||||
dateCreated: Guilds.dateCreated,
|
||||
})
|
||||
.from(Guilds)
|
||||
.where(eq(Guilds.id, guildId))
|
||||
.limit(1);
|
||||
const data = await redisCache(
|
||||
apiCacheKey(`guilds:${id}`),
|
||||
120,
|
||||
async () => {
|
||||
const [guild] = await db
|
||||
.select({
|
||||
id: Guilds.id,
|
||||
name: Guilds.name,
|
||||
description: Guilds.description,
|
||||
userId: Guilds.userId,
|
||||
roomId: Guilds.roomId,
|
||||
badge: Guilds.badge,
|
||||
dateCreated: Guilds.dateCreated,
|
||||
})
|
||||
.from(Guilds)
|
||||
.where(eq(Guilds.id, guildId))
|
||||
.limit(1);
|
||||
|
||||
if (!guild) {
|
||||
if (!guild) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Member count is a separate guarded query (no relation is modelled).
|
||||
let memberCount = 0;
|
||||
try {
|
||||
const [row] = await db
|
||||
.select({ total: count() })
|
||||
.from(GuildsMembers)
|
||||
.where(eq(GuildsMembers.guildId, guild.id));
|
||||
memberCount = row?.total ?? 0;
|
||||
} catch {
|
||||
memberCount = 0;
|
||||
}
|
||||
|
||||
const { userId, ...rest } = guild;
|
||||
return cacheSafe({ data: { ...rest, ownerId: userId, memberCount } });
|
||||
},
|
||||
);
|
||||
|
||||
if (data === null) {
|
||||
return apiJson({ error: "Guild not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
// Member count is a separate guarded query (no relation is modelled).
|
||||
let memberCount = 0;
|
||||
try {
|
||||
const [row] = await db
|
||||
.select({ total: count() })
|
||||
.from(GuildsMembers)
|
||||
.where(eq(GuildsMembers.guildId, guild.id));
|
||||
memberCount = row?.total ?? 0;
|
||||
} catch {
|
||||
memberCount = 0;
|
||||
}
|
||||
|
||||
const { userId, ...rest } = guild;
|
||||
return apiJson({ data: { ...rest, ownerId: userId, memberCount } });
|
||||
return apiJson(data);
|
||||
} catch {
|
||||
// DB unavailable — treat as not found rather than a 500.
|
||||
return apiJson({ error: "Guild not found" }, { status: 200 });
|
||||
|
||||
Reference in New Issue
Block a user