import fs from 'node:fs/promises'; import { defineCachedFunction } from 'nitropack/runtime'; import SQL from 'sql-template-strings'; import type { Config } from '~/locale/config.ts'; import type { Database } from '~/server/db.ts'; import type { UserRow } from '~/server/express/user.ts'; import { rootDir } from '~/server/paths.ts'; import { extractMetadata } from '~/src/blog/metadata.ts'; import type { PostWithContent } from '~/src/blog/metadata.ts'; export const getPosts = defineCachedFunction(async (config: Config): Promise => { const dir = `${rootDir}/locale/${config.locale}/blog`; const posts: PostWithContent[] = []; for (const file of await fs.readdir(dir)) { if (!file.endsWith('.md')) { continue; } const content = await fs.readFile(`${dir}/${file}`, 'utf-8'); const slug = file.substring(0, file.length - 3); const metadata = extractMetadata(config, content); if (metadata !== undefined) { posts.push({ slug, content, ...metadata }); } } posts.sort((a, b) => { if (a.date < b.date) { return 1; } if (a.date > b.date) { return -1; } return 0; }); return posts; }, { name: 'blog', getKey: (config) => config.locale, maxAge: Infinity, }); export interface BlogReactions { total: Record; user?: string[]; } export const getPostReactions = async ( db: Database, locale: string, slug: string, userId: UserRow['id'] | undefined, ): Promise => { const totalReactions = await db.all<{ c: number; emoji: string }>(SQL`SELECT COUNT(*) as c, emoji FROM blog_reactions WHERE locale = ${locale} AND slug = ${slug} GROUP BY emoji`); const reactions: BlogReactions = { total: Object.fromEntries(totalReactions.map((row) => [row.emoji, row.c])), }; if (userId !== undefined) { const userReactions = await db.all<{ emoji: string }>(SQL`SELECT emoji FROM blog_reactions WHERE locale = ${locale} AND slug = ${slug} AND user_id = ${userId}`); reactions.user = userReactions.map((row) => row.emoji); } return reactions; };