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:
1 parent
54f2bc5e0c
commit
f285a7cd98
42 files changed
+1616
-1205
No files matched your search
@@ -4,6 +4,7 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { apiJson } from "@/lib/api";
|
||||
import { db, WebsiteRareValueCategories, WebsiteRareValues } from "@/lib/db";
|
||||
import { apiCacheKey, cacheSafe, redisCache } from "@/lib/redis-cache";
|
||||
|
||||
export async function GET(
|
||||
_req: Request,
|
||||
@@ -24,52 +25,64 @@ export async function GET(
|
||||
}
|
||||
|
||||
try {
|
||||
const [value] = await db
|
||||
.select({
|
||||
id: WebsiteRareValues.id,
|
||||
categoryId: WebsiteRareValues.categoryId,
|
||||
itemId: WebsiteRareValues.itemId,
|
||||
name: WebsiteRareValues.name,
|
||||
creditValue: WebsiteRareValues.creditValue,
|
||||
currencyValue: WebsiteRareValues.currencyValue,
|
||||
currencyType: WebsiteRareValues.currencyType,
|
||||
furnitureIcon: WebsiteRareValues.furnitureIcon,
|
||||
createdAt: WebsiteRareValues.createdAt,
|
||||
updatedAt: WebsiteRareValues.updatedAt,
|
||||
})
|
||||
.from(WebsiteRareValues)
|
||||
.where(eq(WebsiteRareValues.id, valueId))
|
||||
.limit(1);
|
||||
const data = await redisCache(
|
||||
apiCacheKey(`values:${id}`),
|
||||
300,
|
||||
async () => {
|
||||
const [value] = await db
|
||||
.select({
|
||||
id: WebsiteRareValues.id,
|
||||
categoryId: WebsiteRareValues.categoryId,
|
||||
itemId: WebsiteRareValues.itemId,
|
||||
name: WebsiteRareValues.name,
|
||||
creditValue: WebsiteRareValues.creditValue,
|
||||
currencyValue: WebsiteRareValues.currencyValue,
|
||||
currencyType: WebsiteRareValues.currencyType,
|
||||
furnitureIcon: WebsiteRareValues.furnitureIcon,
|
||||
createdAt: WebsiteRareValues.createdAt,
|
||||
updatedAt: WebsiteRareValues.updatedAt,
|
||||
})
|
||||
.from(WebsiteRareValues)
|
||||
.where(eq(WebsiteRareValues.id, valueId))
|
||||
.limit(1);
|
||||
|
||||
if (!value) {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// No relation is modelled between the value and its category, so resolve the
|
||||
// category in a second guarded query.
|
||||
let category: {
|
||||
id: bigint;
|
||||
name: string;
|
||||
badge: string;
|
||||
priority: number;
|
||||
} | null = null;
|
||||
try {
|
||||
const [row] = await db
|
||||
.select({
|
||||
id: WebsiteRareValueCategories.id,
|
||||
name: WebsiteRareValueCategories.name,
|
||||
badge: WebsiteRareValueCategories.badge,
|
||||
priority: WebsiteRareValueCategories.priority,
|
||||
})
|
||||
.from(WebsiteRareValueCategories)
|
||||
.where(eq(WebsiteRareValueCategories.id, value.categoryId))
|
||||
.limit(1);
|
||||
category = row ?? null;
|
||||
} catch {
|
||||
category = null;
|
||||
}
|
||||
|
||||
return cacheSafe({ data: { ...value, category } });
|
||||
},
|
||||
);
|
||||
|
||||
if (data === null) {
|
||||
return apiJson({ error: "Rare value not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
// No relation is modelled between the value and its category, so resolve the
|
||||
// category in a second guarded query.
|
||||
let category: {
|
||||
id: bigint;
|
||||
name: string;
|
||||
badge: string;
|
||||
priority: number;
|
||||
} | null = null;
|
||||
try {
|
||||
const [row] = await db
|
||||
.select({
|
||||
id: WebsiteRareValueCategories.id,
|
||||
name: WebsiteRareValueCategories.name,
|
||||
badge: WebsiteRareValueCategories.badge,
|
||||
priority: WebsiteRareValueCategories.priority,
|
||||
})
|
||||
.from(WebsiteRareValueCategories)
|
||||
.where(eq(WebsiteRareValueCategories.id, value.categoryId))
|
||||
.limit(1);
|
||||
category = row ?? null;
|
||||
} catch {
|
||||
category = null;
|
||||
}
|
||||
|
||||
return apiJson({ data: { ...value, category } });
|
||||
return apiJson(data);
|
||||
} catch {
|
||||
// DB unavailable — treat as not found rather than a 500.
|
||||
return apiJson({ error: "Rare value not found" }, { status: 200 });
|
||||
|
||||
Reference in new issue
Block a user