Files
spsays-training/lib/api.ts
T
2026-04-14 19:12:11 +05:30

171 lines
4.4 KiB
TypeScript

/**
* Data access layer for server components.
*
* These functions query MongoDB via Prisma and are called directly by
* Next.js server components. The /api/* routes exist for external consumers
* and for client-side infinite-scroll fetching.
*/
import { prisma } from "@/lib/db";
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
export type BookMeta = {
book: string;
[key: string]: unknown;
};
export type VerseRecord = {
id: string;
book: string;
division: string | null;
chapter: number;
verse: number;
verseEnd: number | null;
reference: string;
bookTitle: string;
devanagari: string;
bengali: string | null;
transliteration: string;
synonyms: { word: string; meanings: string[] }[];
translation: string;
purport: string[];
};
export type VerseIndexEntry = {
verse: number;
verseEnd: number | null;
reference: string;
};
export type PaginatedVerses = {
verses: VerseRecord[];
total: number;
page: number;
limit: number;
totalPages: number;
hasMore: boolean;
};
export type GlossaryRecord = {
id: string;
term: string;
definition: string;
definitionHtml: string;
};
// ---------------------------------------------------------------------------
// Book meta
// ---------------------------------------------------------------------------
/**
* Get all book metas — each has { book, ...data } where data is the intro.json blob.
*/
export async function getAllBooks(): Promise<BookMeta[]> {
const metas = await prisma.bookMeta.findMany();
return metas.map((m) => ({
book: m.book,
...(m.data as Record<string, unknown>),
}));
}
/**
* Get a single book's meta by slug ("bg", "sb", "cc").
*/
export async function getBookMeta(book: string): Promise<BookMeta | null> {
const meta = await prisma.bookMeta.findUnique({ where: { book } });
if (!meta) return null;
return { book: meta.book, ...(meta.data as Record<string, unknown>) };
}
// ---------------------------------------------------------------------------
// Verse index (lightweight — for sidebar)
// ---------------------------------------------------------------------------
/**
* Get the verse index for a chapter — returns only verse number, verseEnd,
* and reference for every verse. This is lightweight enough to fetch all at
* once even for chapters with 400+ verses (each entry is ~50 bytes).
*
* - BG: division is null
* - SB: division is canto number as string ("1", "2", …)
* - CC: division is līlā key ("adi", "madhya", "antya")
*/
export async function getChapterVerseIndex(
book: string,
division: string | null,
chapter: number,
): Promise<VerseIndexEntry[]> {
const entries = await prisma.verse.findMany({
where: { book, division, chapter },
orderBy: { verse: "asc" },
select: {
verse: true,
verseEnd: true,
reference: true,
},
});
return entries;
}
// ---------------------------------------------------------------------------
// Verses (paginated)
// ---------------------------------------------------------------------------
const DEFAULT_PAGE_SIZE = 20;
/**
* Get a page of verses for a given book + division + chapter.
*
* Returns the verses for the requested page along with pagination metadata
* so the client knows whether to fetch more.
*/
export async function getChapterVerses(
book: string,
division: string | null,
chapter: number,
page: number = 1,
limit: number = DEFAULT_PAGE_SIZE,
): Promise<PaginatedVerses> {
const skip = (page - 1) * limit;
const where = { book, division, chapter };
const [verses, total] = await Promise.all([
prisma.verse.findMany({
where,
orderBy: { verse: "asc" },
skip,
take: limit,
}),
prisma.verse.count({ where }),
]);
return {
verses: verses as unknown as VerseRecord[],
total,
page,
limit,
totalPages: Math.ceil(total / limit),
hasMore: skip + verses.length < total,
};
}
// ---------------------------------------------------------------------------
// Glossary
// ---------------------------------------------------------------------------
/**
* Get all glossary entries, ordered alphabetically by term.
*/
export async function getAllGlossaryEntries(): Promise<GlossaryRecord[]> {
const entries = await prisma.glossaryEntry.findMany({
orderBy: { term: "asc" },
});
return entries as unknown as GlossaryRecord[];
}