75 lines
2.2 KiB
TypeScript

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<PostWithContent[]> => {
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<string, number>;
user?: string[];
}
export const getPostReactions = async (
db: Database,
locale: string,
slug: string,
userId: UserRow['id'] | undefined,
): Promise<BlogReactions> => {
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;
};