import fs from 'node:fs/promises'; import { defineCachedFunction } from 'nitropack/runtime'; import SQL from 'sql-template-strings'; import type { Database } from '~/server/db.ts'; import type { UserRow } from '~/server/express/user.ts'; import { rootDir } from '~/server/paths.ts'; import { extractMetadata, type Post } from '~/src/blog/metadata.ts'; export const getPosts = defineCachedFunction(async (): Promise => { const dir = `${rootDir}/data/blog`; const posts: Post[] = []; 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(global.config, content); if (metadata !== undefined) { posts.push({ slug, ...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: () => 'default', maxAge: Infinity, }); export interface BlogReactions { total: Record; user?: string[]; } export const getPostReactions = async ( db: Database, 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 = ${global.config.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 = ${global.config.locale} AND slug = ${slug} AND user_id = ${userId}`); reactions.user = userReactions.map((row) => row.emoji); } return reactions; };