93 lines
2.6 KiB
TypeScript
93 lines
2.6 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import matter from "gray-matter";
|
|
|
|
export type BlogDetail = {
|
|
slug: string;
|
|
title: string;
|
|
category: string;
|
|
date: string;
|
|
readTime: string;
|
|
author: string;
|
|
excerpt: string;
|
|
coverImage: string;
|
|
markdown: string;
|
|
lang: "tr" | "en";
|
|
};
|
|
|
|
const BLOG_CONTENT_DIR = path.join(process.cwd(), "content", "blog");
|
|
|
|
function toSafeString(value: unknown, fallback: string) {
|
|
if (typeof value !== "string") return fallback;
|
|
const trimmed = value.trim();
|
|
return trimmed || fallback;
|
|
}
|
|
|
|
function normalizeSlug(fileName: string) {
|
|
return fileName.replace(/\.md$/i, "");
|
|
}
|
|
|
|
function mapMarkdownToBlogDetail(fileName: string, raw: string): BlogDetail {
|
|
const parsed = matter(raw);
|
|
const slug = normalizeSlug(fileName);
|
|
|
|
return {
|
|
slug,
|
|
title: toSafeString(parsed.data.title, slug),
|
|
category: toSafeString(parsed.data.category, "General"),
|
|
date: toSafeString(parsed.data.date, ""),
|
|
readTime: toSafeString(parsed.data.readTime, ""),
|
|
author: toSafeString(parsed.data.author, "Poyraz Avsever"),
|
|
excerpt: toSafeString(parsed.data.excerpt, ""),
|
|
coverImage: toSafeString(parsed.data.coverImage, "/news/design.svg"),
|
|
markdown: parsed.content.trim(),
|
|
lang: toSafeString(parsed.data.lang, "tr") as "tr" | "en",
|
|
};
|
|
}
|
|
|
|
export async function listBlogDetails(locale?: string): Promise<BlogDetail[]> {
|
|
let files: string[] = [];
|
|
try {
|
|
files = await fs.readdir(BLOG_CONTENT_DIR);
|
|
} catch {
|
|
return [];
|
|
}
|
|
|
|
const markdownFiles = files.filter((fileName) => fileName.endsWith(".md"));
|
|
const posts = await Promise.all(
|
|
markdownFiles.map(async (fileName) => {
|
|
const targetPath = path.join(BLOG_CONTENT_DIR, fileName);
|
|
const raw = await fs.readFile(targetPath, "utf8");
|
|
return mapMarkdownToBlogDetail(fileName, raw);
|
|
}),
|
|
);
|
|
|
|
return posts
|
|
.filter((post) => !locale || post.lang === locale)
|
|
.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
}
|
|
|
|
export async function getBlogTranslations(post: BlogDetail) {
|
|
const posts = await listBlogDetails();
|
|
|
|
return posts.filter(
|
|
(candidate) =>
|
|
candidate.coverImage === post.coverImage &&
|
|
candidate.category.toLocaleLowerCase() === post.category.toLocaleLowerCase(),
|
|
);
|
|
}
|
|
|
|
export async function getBlogDetailBySlug(slug: string): Promise<BlogDetail | null> {
|
|
const safeSlug = slug.trim().toLowerCase();
|
|
if (!safeSlug) return null;
|
|
|
|
const targetPath = path.join(BLOG_CONTENT_DIR, `${safeSlug}.md`);
|
|
|
|
try {
|
|
const raw = await fs.readFile(targetPath, "utf8");
|
|
return mapMarkdownToBlogDetail(`${safeSlug}.md`, raw);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|