import fs from "node:fs"; import path from "node:path"; import type { WikiPage, WikiPageSummary } from "@/lib/types"; const WIKI_DIR = process.env.WIKI_DIR || path.join(process.cwd(), "wiki"); fs.mkdirSync(WIKI_DIR, { recursive: true }); function assertSafeFilename(filename: string) { if (filename.includes("..") || filename.includes("/") || filename.includes("\\")) { throw new Error("invalid_filename"); } } function extractMetadata(content: string) { const metadata = { title: "", created: "", modified: "", tags: [] as string[], }; const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); if (frontmatterMatch) { const frontmatter = frontmatterMatch[1]; const titleMatch = frontmatter.match(/title:\s*(.+)/i); const tagsMatch = frontmatter.match(/tags:\s*\[(.+)\]/i); if (titleMatch) { metadata.title = titleMatch[1].trim(); } if (tagsMatch) { metadata.tags = tagsMatch[1].split(",").map((tag) => tag.trim()).filter(Boolean); } } if (!metadata.title) { const headingMatch = content.match(/^#\s+(.+)$/m); if (headingMatch) { metadata.title = headingMatch[1].trim(); } } return metadata; } export function listWikiPages(): WikiPageSummary[] { if (!fs.existsSync(WIKI_DIR)) { return []; } return fs .readdirSync(WIKI_DIR) .filter((fileName) => fileName.endsWith(".md")) .map((filename) => { const filePath = path.join(WIKI_DIR, filename); const stats = fs.statSync(filePath); const content = fs.readFileSync(filePath, "utf8"); const metadata = extractMetadata(content); return { filename, title: metadata.title || filename.replace(".md", "").replace(/-/g, " "), created: stats.birthtime.toISOString(), modified: stats.mtime.toISOString(), tags: metadata.tags, }; }) .sort((left, right) => new Date(right.modified).getTime() - new Date(left.modified).getTime()); } export function readWikiPage(filename: string): WikiPage | null { assertSafeFilename(filename); const filePath = path.join(WIKI_DIR, filename); if (!fs.existsSync(filePath)) { return null; } const content = fs.readFileSync(filePath, "utf8"); const stats = fs.statSync(filePath); const metadata = extractMetadata(content); return { filename, content, metadata: { title: metadata.title || filename.replace(".md", ""), created: stats.birthtime.toISOString(), modified: stats.mtime.toISOString(), tags: metadata.tags, }, }; } export function createWikiPage(title: string) { const safeTitle = title .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 80); const timestamp = new Date().toISOString().slice(0, 10); let filename = `${timestamp}-${safeTitle}.md`; let counter = 1; while (fs.existsSync(path.join(WIKI_DIR, filename))) { filename = `${timestamp}-${safeTitle}-${counter}.md`; counter += 1; } const content = `# ${title} ## Summary Document the architecture, deployment notes, or runbook here. `; fs.writeFileSync(path.join(WIKI_DIR, filename), content, "utf8"); return filename; } export function updateWikiPage(filename: string, content: string) { assertSafeFilename(filename); fs.writeFileSync(path.join(WIKI_DIR, filename), content, "utf8"); } export function deleteWikiPage(filename: string) { assertSafeFilename(filename); fs.unlinkSync(path.join(WIKI_DIR, filename)); }