- Fix DOMPurify SSR crash (use isomorphic-dompurify) - Fix SanitizedHtml to sanitize by default - Add auth guards to studio/catalog maintenance pages - Add update/edit to vouchers CRUD - Add update/edit to rare-values CRUD - Add approve workflow to applications page - Add edit form to guilds detail page - Add SEO metadata to all public pages (21 pages) - Fix mobile nav accessibility (focus trap, aria attributes) - Fix missing labels and table accessibility - Add dynamic imports for heavy client components (6 components) - Fix silent error swallowing (40+ locations) - Add content scheduling for articles (publishAt, status) - Wire up 12 missing webhook notification triggers - Add global search to admin panel - Add bulk actions to admin users table - Fix JSON formatting and a11y issues
67 lines
1.6 KiB
TypeScript
67 lines
1.6 KiB
TypeScript
import "server-only";
|
|
|
|
import { and, desc, eq, or, sql } from "drizzle-orm";
|
|
import { cached } from "@/lib/cache";
|
|
import { db, WebsiteArticles } from "@/lib/db";
|
|
|
|
export interface NewsListItem {
|
|
slug: string;
|
|
title: string;
|
|
shortStory: string;
|
|
image: string;
|
|
createdAt: Date | null;
|
|
}
|
|
|
|
interface NewsListRow {
|
|
slug: string;
|
|
title: string;
|
|
shortStory: string;
|
|
image: string;
|
|
createdAt: Date | null;
|
|
}
|
|
|
|
const TTL_MS = 300_000;
|
|
const CACHE_KEY = "news_list";
|
|
const FETCH_LIMIT = 30;
|
|
|
|
/**
|
|
* Shared, cached news list used by both the homepage and the news archive so
|
|
* a single "news_list" cache entry serves both routes. Returns at most
|
|
* `limit` rows with `createdAt` normalized to a Date (the cache round-trip
|
|
* serializes timestamps to ISO strings).
|
|
*/
|
|
export async function getNewsList(limit: number): Promise<NewsListItem[]> {
|
|
try {
|
|
const rows = await cached<NewsListRow[]>(CACHE_KEY, TTL_MS, () =>
|
|
db
|
|
.select({
|
|
slug: WebsiteArticles.slug,
|
|
title: WebsiteArticles.title,
|
|
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(FETCH_LIMIT),
|
|
);
|
|
return rows
|
|
.map((a) => ({
|
|
...a,
|
|
createdAt: a.createdAt ? new Date(a.createdAt) : null,
|
|
}))
|
|
.slice(0, limit);
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|