- catalog: apply bulk offer edits in a single transaction with one RCON refresh instead of N round-trips, virtualize the items table past 150 rows and cap the shop preview at 200 tiles - cache: lift the public cache TTLs and the boot warm-up into env-tunable constants (src/lib/perf-ttls.ts; CMS_*_TTL_SEC, CMS_WARMUP_ENABLED) - auth: resolve the captcha provider, site key and secret from CAPTCHA_PROVIDER, TURNSTILE_SITE_KEY and TURNSTILE_SECRET_KEY when the admin settings are unset; an explicit DB value (including "none") still wins, so Turnstile can be enabled per-deploy without touching the DB.
53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
import "server-only";
|
|
|
|
import { and, count, desc, eq, or, sql } from "drizzle-orm";
|
|
import { db, User, WebsiteArticles } from "@/lib/db";
|
|
import { resolveHotelName } from "@/lib/hotel-name";
|
|
import { HOME_PAYLOAD_TTL_MS } from "@/lib/perf-ttls";
|
|
import { apiCacheKey, cacheSafe } from "@/lib/redis-cache";
|
|
import { cacheNews } from "@/lib/services/news-cache";
|
|
|
|
/**
|
|
* The `/api/home` landing payload: latest 4 published articles plus the current
|
|
* online player count. Shared as a loader so the cache warm-up can prime the
|
|
* exact same key the route reads — priming a placeholder instead made every
|
|
* instance serve a bogus `{ primed: true }` payload after each restart.
|
|
*/
|
|
export async function loadHomePayload() {
|
|
const [articles, onlineRows, hotelName] = await Promise.all([
|
|
db
|
|
.select({
|
|
id: WebsiteArticles.id,
|
|
title: WebsiteArticles.title,
|
|
slug: WebsiteArticles.slug,
|
|
shortStory: WebsiteArticles.shortStory,
|
|
image: WebsiteArticles.image,
|
|
createdAt: WebsiteArticles.createdAt,
|
|
})
|
|
.from(WebsiteArticles)
|
|
.where(
|
|
and(
|
|
eq(WebsiteArticles.status, "published"),
|
|
or(
|
|
sql`${WebsiteArticles.publishAt} IS NULL`,
|
|
sql`${WebsiteArticles.publishAt} <= NOW()`,
|
|
),
|
|
),
|
|
)
|
|
.orderBy(desc(WebsiteArticles.createdAt))
|
|
.limit(4),
|
|
db.select({ total: count() }).from(User).where(eq(User.online, "1")),
|
|
resolveHotelName(),
|
|
]);
|
|
return cacheSafe({
|
|
articles,
|
|
online: onlineRows[0]?.total ?? 0,
|
|
hotelName,
|
|
});
|
|
}
|
|
|
|
/** Cached read of {@link loadHomePayload} under the route's own key. */
|
|
export async function cachedHomePayload() {
|
|
return cacheNews(apiCacheKey("home"), HOME_PAYLOAD_TTL_MS, loadHomePayload);
|
|
}
|