nextblogkit 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +951 -0
  3. package/dist/admin/index.cjs +2465 -0
  4. package/dist/admin/index.cjs.map +1 -0
  5. package/dist/admin/index.d.cts +44 -0
  6. package/dist/admin/index.d.ts +44 -0
  7. package/dist/admin/index.js +2438 -0
  8. package/dist/admin/index.js.map +1 -0
  9. package/dist/api/categories.cjs +82 -0
  10. package/dist/api/categories.cjs.map +1 -0
  11. package/dist/api/categories.d.cts +27 -0
  12. package/dist/api/categories.d.ts +27 -0
  13. package/dist/api/categories.js +77 -0
  14. package/dist/api/categories.js.map +1 -0
  15. package/dist/api/media.cjs +113 -0
  16. package/dist/api/media.cjs.map +1 -0
  17. package/dist/api/media.d.cts +22 -0
  18. package/dist/api/media.d.ts +22 -0
  19. package/dist/api/media.js +109 -0
  20. package/dist/api/media.js.map +1 -0
  21. package/dist/api/posts.cjs +103 -0
  22. package/dist/api/posts.cjs.map +1 -0
  23. package/dist/api/posts.d.cts +27 -0
  24. package/dist/api/posts.d.ts +27 -0
  25. package/dist/api/posts.js +98 -0
  26. package/dist/api/posts.js.map +1 -0
  27. package/dist/api/rss.cjs +25 -0
  28. package/dist/api/rss.cjs.map +1 -0
  29. package/dist/api/rss.d.cts +5 -0
  30. package/dist/api/rss.d.ts +5 -0
  31. package/dist/api/rss.js +23 -0
  32. package/dist/api/rss.js.map +1 -0
  33. package/dist/api/settings.cjs +40 -0
  34. package/dist/api/settings.cjs.map +1 -0
  35. package/dist/api/settings.d.cts +17 -0
  36. package/dist/api/settings.d.ts +17 -0
  37. package/dist/api/settings.js +37 -0
  38. package/dist/api/settings.js.map +1 -0
  39. package/dist/api/sitemap.cjs +25 -0
  40. package/dist/api/sitemap.cjs.map +1 -0
  41. package/dist/api/sitemap.d.cts +5 -0
  42. package/dist/api/sitemap.d.ts +5 -0
  43. package/dist/api/sitemap.js +23 -0
  44. package/dist/api/sitemap.js.map +1 -0
  45. package/dist/chunk-4NKOJYWJ.js +68 -0
  46. package/dist/chunk-4NKOJYWJ.js.map +1 -0
  47. package/dist/chunk-4PY224XM.js +103 -0
  48. package/dist/chunk-4PY224XM.js.map +1 -0
  49. package/dist/chunk-64HUVJOZ.js +446 -0
  50. package/dist/chunk-64HUVJOZ.js.map +1 -0
  51. package/dist/chunk-6HKMZOI4.cjs +48 -0
  52. package/dist/chunk-6HKMZOI4.cjs.map +1 -0
  53. package/dist/chunk-A2S32RZN.js +138 -0
  54. package/dist/chunk-A2S32RZN.js.map +1 -0
  55. package/dist/chunk-E2QLTHKN.cjs +70 -0
  56. package/dist/chunk-E2QLTHKN.cjs.map +1 -0
  57. package/dist/chunk-JLPJKNRZ.js +37 -0
  58. package/dist/chunk-JLPJKNRZ.js.map +1 -0
  59. package/dist/chunk-JM7QRXXK.js +330 -0
  60. package/dist/chunk-JM7QRXXK.js.map +1 -0
  61. package/dist/chunk-KDZER3PU.cjs +43 -0
  62. package/dist/chunk-KDZER3PU.cjs.map +1 -0
  63. package/dist/chunk-N5MKAD7J.cjs +109 -0
  64. package/dist/chunk-N5MKAD7J.cjs.map +1 -0
  65. package/dist/chunk-QE4VLQYN.cjs +337 -0
  66. package/dist/chunk-QE4VLQYN.cjs.map +1 -0
  67. package/dist/chunk-R6MO3QIP.js +46 -0
  68. package/dist/chunk-R6MO3QIP.js.map +1 -0
  69. package/dist/chunk-U2ROR6AY.cjs +476 -0
  70. package/dist/chunk-U2ROR6AY.cjs.map +1 -0
  71. package/dist/chunk-ZP5XRVVH.cjs +141 -0
  72. package/dist/chunk-ZP5XRVVH.cjs.map +1 -0
  73. package/dist/cli/index.cjs +1308 -0
  74. package/dist/components/index.cjs +541 -0
  75. package/dist/components/index.cjs.map +1 -0
  76. package/dist/components/index.d.cts +165 -0
  77. package/dist/components/index.d.ts +165 -0
  78. package/dist/components/index.js +527 -0
  79. package/dist/components/index.js.map +1 -0
  80. package/dist/editor/index.cjs +1083 -0
  81. package/dist/editor/index.cjs.map +1 -0
  82. package/dist/editor/index.d.cts +133 -0
  83. package/dist/editor/index.d.ts +133 -0
  84. package/dist/editor/index.js +1051 -0
  85. package/dist/editor/index.js.map +1 -0
  86. package/dist/index-Cgzphklp.d.ts +266 -0
  87. package/dist/index-vjlZDWNr.d.cts +266 -0
  88. package/dist/index.cjs +368 -0
  89. package/dist/index.cjs.map +1 -0
  90. package/dist/index.d.cts +27 -0
  91. package/dist/index.d.ts +27 -0
  92. package/dist/index.js +208 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/lib/index.cjs +120 -0
  95. package/dist/lib/index.cjs.map +1 -0
  96. package/dist/lib/index.d.cts +4 -0
  97. package/dist/lib/index.d.ts +4 -0
  98. package/dist/lib/index.js +7 -0
  99. package/dist/lib/index.js.map +1 -0
  100. package/dist/styles/admin.css +657 -0
  101. package/dist/styles/blog.css +851 -0
  102. package/dist/styles/editor.css +452 -0
  103. package/dist/styles/globals.css +270 -0
  104. package/dist/styles/prose.css +299 -0
  105. package/dist/types-CBEEBR4A.d.cts +732 -0
  106. package/dist/types-CBEEBR4A.d.ts +732 -0
  107. package/package.json +134 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/search.ts","../src/lib/seo.ts","../src/lib/seo-scorer.ts"],"names":["getEnvConfig"],"mappings":"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","file":"chunk-QE4VLQYN.cjs","sourcesContent":["import type { Collection, Document } from 'mongodb';\n\nexport interface SearchResult {\n slug: string;\n title: string;\n excerpt: string;\n score: number;\n}\n\nexport async function searchPosts(\n collection: Collection,\n query: string,\n limit: number = 10\n): Promise<SearchResult[]> {\n if (!query.trim()) return [];\n\n const results = await collection\n .find(\n {\n $text: { $search: query },\n status: 'published',\n },\n {\n projection: {\n slug: 1,\n title: 1,\n excerpt: 1,\n score: { $meta: 'textScore' },\n },\n }\n )\n .sort({ score: { $meta: 'textScore' } })\n .limit(limit)\n .toArray();\n\n return results.map((doc: Document) => ({\n slug: doc.slug as string,\n title: doc.title as string,\n excerpt: doc.excerpt as string,\n score: doc.score as number,\n }));\n}\n","import type { BlogPost } from './types';\nimport { getEnvConfig } from './config';\n\nexport interface MetaTags {\n title: string;\n description: string;\n canonical: string;\n openGraph: {\n title: string;\n description: string;\n url: string;\n siteName: string;\n type: string;\n images: { url: string; width?: number; height?: number; alt?: string }[];\n article?: {\n publishedTime: string;\n modifiedTime: string;\n section?: string;\n tags?: string[];\n };\n };\n twitter: {\n card: string;\n title: string;\n description: string;\n images: string[];\n };\n robots?: string;\n}\n\nexport function generateMetaTags(post: BlogPost): MetaTags {\n const env = getEnvConfig();\n const postUrl = `${env.NEXTBLOGKIT_SITE_URL}/blog/${post.slug}`;\n const title = post.seo?.metaTitle || post.title;\n const description = post.seo?.metaDescription || post.excerpt;\n const canonical = post.seo?.canonicalUrl || postUrl;\n const ogImage = post.seo?.ogImage || post.coverImage?.url;\n\n return {\n title: `${title} | ${env.NEXTBLOGKIT_SITE_NAME}`,\n description,\n canonical,\n openGraph: {\n title,\n description,\n url: postUrl,\n siteName: env.NEXTBLOGKIT_SITE_NAME,\n type: post.seo?.ogType || 'article',\n images: ogImage\n ? [\n {\n url: ogImage,\n width: 1200,\n height: 630,\n alt: title,\n },\n ]\n : [],\n article: {\n publishedTime: post.publishedAt?.toISOString() || '',\n modifiedTime: post.updatedAt.toISOString(),\n section: post.categories[0],\n tags: post.tags,\n },\n },\n twitter: {\n card: 'summary_large_image',\n title,\n description,\n images: ogImage ? [ogImage] : [],\n },\n robots: post.seo?.noIndex ? 'noindex, nofollow' : undefined,\n };\n}\n\nexport interface ArticleStructuredData {\n '@context': string;\n '@type': string;\n headline: string;\n description: string;\n image?: string;\n datePublished?: string;\n dateModified: string;\n author: {\n '@type': string;\n name: string;\n url?: string;\n };\n publisher: {\n '@type': string;\n name: string;\n };\n mainEntityOfPage: {\n '@type': string;\n '@id': string;\n };\n wordCount: number;\n articleSection?: string;\n}\n\nexport function generateStructuredData(post: BlogPost): ArticleStructuredData {\n const env = getEnvConfig();\n const postUrl = `${env.NEXTBLOGKIT_SITE_URL}/blog/${post.slug}`;\n\n return {\n '@context': 'https://schema.org',\n '@type': 'BlogPosting',\n headline: post.title,\n description: post.excerpt,\n image: post.coverImage?.url || post.seo?.ogImage,\n datePublished: post.publishedAt?.toISOString(),\n dateModified: post.updatedAt.toISOString(),\n author: {\n '@type': 'Person',\n name: post.author.name,\n url: post.author.url,\n },\n publisher: {\n '@type': 'Organization',\n name: env.NEXTBLOGKIT_SITE_NAME,\n },\n mainEntityOfPage: {\n '@type': 'WebPage',\n '@id': postUrl,\n },\n wordCount: post.wordCount,\n articleSection: post.categories[0],\n };\n}\n\nexport interface FAQStructuredData {\n '@context': string;\n '@type': string;\n mainEntity: {\n '@type': string;\n name: string;\n acceptedAnswer: {\n '@type': string;\n text: string;\n };\n }[];\n}\n\nexport function generateFAQStructuredData(\n faqItems: { question: string; answer: string }[]\n): FAQStructuredData | null {\n if (!faqItems.length) return null;\n\n return {\n '@context': 'https://schema.org',\n '@type': 'FAQPage',\n mainEntity: faqItems.map((item) => ({\n '@type': 'Question',\n name: item.question,\n acceptedAnswer: {\n '@type': 'Answer',\n text: item.answer,\n },\n })),\n };\n}\n\nexport interface BreadcrumbStructuredData {\n '@context': string;\n '@type': string;\n itemListElement: {\n '@type': string;\n position: number;\n name: string;\n item?: string;\n }[];\n}\n\nexport function generateBreadcrumbs(\n post: BlogPost,\n categoryName?: string\n): BreadcrumbStructuredData {\n const env = getEnvConfig();\n const items: BreadcrumbStructuredData['itemListElement'] = [\n {\n '@type': 'ListItem',\n position: 1,\n name: 'Home',\n item: env.NEXTBLOGKIT_SITE_URL,\n },\n {\n '@type': 'ListItem',\n position: 2,\n name: 'Blog',\n item: `${env.NEXTBLOGKIT_SITE_URL}/blog`,\n },\n ];\n\n if (categoryName && post.categories[0]) {\n items.push({\n '@type': 'ListItem',\n position: 3,\n name: categoryName,\n item: `${env.NEXTBLOGKIT_SITE_URL}/blog/category/${post.categories[0]}`,\n });\n items.push({\n '@type': 'ListItem',\n position: 4,\n name: post.title,\n });\n } else {\n items.push({\n '@type': 'ListItem',\n position: 3,\n name: post.title,\n });\n }\n\n return {\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: items,\n };\n}\n","import type { BlogPost, SEOScore, SEOCheck, SEOCheckStatus } from './types';\n\nfunction check(id: string, status: SEOCheckStatus, message: string): SEOCheck {\n return { id, status, message };\n}\n\nexport function calculateSEOScore(post: BlogPost): SEOScore {\n const checks: SEOCheck[] = [];\n const keyword = post.seo?.focusKeyword?.toLowerCase() || '';\n const title = post.title.toLowerCase();\n const slug = post.slug.toLowerCase();\n const excerpt = (post.seo?.metaDescription || post.excerpt || '').toLowerCase();\n const contentText = post.contentText?.toLowerCase() || '';\n const contentHTML = post.contentHTML || '';\n\n // 1. Focus keyword in title\n if (!keyword) {\n checks.push(check('focus-keyword-in-title', 'warn', 'No focus keyword set'));\n } else if (title.includes(keyword)) {\n checks.push(check('focus-keyword-in-title', 'pass', 'Focus keyword appears in title'));\n } else {\n checks.push(check('focus-keyword-in-title', 'fail', 'Focus keyword not found in title'));\n }\n\n // 2. Focus keyword in slug\n if (!keyword) {\n checks.push(check('focus-keyword-in-slug', 'warn', 'No focus keyword set'));\n } else if (slug.includes(keyword.replace(/\\s+/g, '-'))) {\n checks.push(check('focus-keyword-in-slug', 'pass', 'Focus keyword appears in URL slug'));\n } else {\n checks.push(check('focus-keyword-in-slug', 'fail', 'Focus keyword not found in URL slug'));\n }\n\n // 3. Focus keyword in meta description\n if (!keyword) {\n checks.push(check('focus-keyword-in-excerpt', 'warn', 'No focus keyword set'));\n } else if (excerpt.includes(keyword)) {\n checks.push(check('focus-keyword-in-excerpt', 'pass', 'Focus keyword appears in meta description'));\n } else {\n checks.push(check('focus-keyword-in-excerpt', 'fail', 'Focus keyword not found in meta description'));\n }\n\n // 4. Focus keyword in H2\n if (keyword) {\n const h2Regex = /<h2[^>]*>(.*?)<\\/h2>/gi;\n const h2s = contentHTML.match(h2Regex) || [];\n const keywordInH2 = h2s.some((h2) => h2.toLowerCase().includes(keyword));\n if (keywordInH2) {\n checks.push(check('focus-keyword-in-h2', 'pass', 'Focus keyword found in a subheading'));\n } else {\n checks.push(check('focus-keyword-in-h2', 'warn', 'Focus keyword not found in any H2 subheading'));\n }\n }\n\n // 5. Focus keyword in first paragraph\n if (keyword && contentText) {\n const first150Words = contentText.split(/\\s+/).slice(0, 150).join(' ');\n if (first150Words.includes(keyword)) {\n checks.push(check('focus-keyword-in-first-paragraph', 'pass', 'Focus keyword appears early in content'));\n } else {\n checks.push(check('focus-keyword-in-first-paragraph', 'warn', 'Focus keyword not found in the first paragraph'));\n }\n }\n\n // 6. Keyword density\n if (keyword && contentText) {\n const words = contentText.split(/\\s+/).length;\n const keywordCount = (contentText.match(new RegExp(keyword, 'g')) || []).length;\n const density = (keywordCount / words) * 100;\n if (density >= 0.5 && density <= 2.5) {\n checks.push(check('focus-keyword-density', 'pass', `Keyword density is ${density.toFixed(1)}% (ideal)`));\n } else if (density < 0.5) {\n checks.push(check('focus-keyword-density', 'warn', `Keyword density is ${density.toFixed(1)}% (too low, aim for 0.5-2.5%)`));\n } else {\n checks.push(check('focus-keyword-density', 'warn', `Keyword density is ${density.toFixed(1)}% (too high, aim for 0.5-2.5%)`));\n }\n }\n\n // 7. Title length\n const metaTitle = post.seo?.metaTitle || post.title;\n if (metaTitle.length >= 50 && metaTitle.length <= 60) {\n checks.push(check('title-length', 'pass', `Title is ${metaTitle.length} characters (ideal)`));\n } else if (metaTitle.length < 30) {\n checks.push(check('title-length', 'fail', `Title is ${metaTitle.length} characters (too short, aim for 50-60)`));\n } else if (metaTitle.length > 70) {\n checks.push(check('title-length', 'warn', `Title is ${metaTitle.length} characters (too long, aim for 50-60)`));\n } else {\n checks.push(check('title-length', 'warn', `Title is ${metaTitle.length} characters (aim for 50-60)`));\n }\n\n // 8. Meta description length\n const metaDesc = post.seo?.metaDescription || post.excerpt || '';\n if (metaDesc.length >= 150 && metaDesc.length <= 160) {\n checks.push(check('meta-description-length', 'pass', `Meta description is ${metaDesc.length} characters (ideal)`));\n } else if (metaDesc.length < 120) {\n checks.push(check('meta-description-length', 'warn', `Meta description is ${metaDesc.length} characters (too short, aim for 150-160)`));\n } else if (metaDesc.length > 170) {\n checks.push(check('meta-description-length', 'warn', `Meta description is ${metaDesc.length} characters (too long, may be truncated)`));\n } else {\n checks.push(check('meta-description-length', 'pass', `Meta description is ${metaDesc.length} characters`));\n }\n\n // 9. Slug length\n if (post.slug.length <= 75) {\n checks.push(check('slug-length', 'pass', `URL slug is ${post.slug.length} characters`));\n } else {\n checks.push(check('slug-length', 'warn', `URL slug is ${post.slug.length} characters (should be under 75)`));\n }\n\n // 10. Content length\n if (post.wordCount >= 300) {\n checks.push(check('content-length', 'pass', `Content is ${post.wordCount} words`));\n } else {\n checks.push(check('content-length', 'fail', `Content is only ${post.wordCount} words (aim for 300+)`));\n }\n\n // 11. Heading hierarchy\n const headingRegex = /<(h[2-6])[^>]*>/gi;\n const headings = [...contentHTML.matchAll(headingRegex)].map((m) => parseInt(m[1][1]));\n let hierarchyOk = true;\n for (let i = 1; i < headings.length; i++) {\n if (headings[i] > headings[i - 1] + 1) {\n hierarchyOk = false;\n break;\n }\n }\n if (headings.length === 0) {\n checks.push(check('heading-hierarchy', 'warn', 'No subheadings found — add H2s to structure content'));\n } else if (hierarchyOk) {\n checks.push(check('heading-hierarchy', 'pass', 'Heading hierarchy is correct'));\n } else {\n checks.push(check('heading-hierarchy', 'warn', 'Heading levels are skipped (e.g., H2 → H4)'));\n }\n\n // 12. Image alt text\n const imgRegex = /<img[^>]*>/gi;\n const images = contentHTML.match(imgRegex) || [];\n const missingAlt = images.filter((img) => !img.includes('alt=') || img.includes('alt=\"\"'));\n if (images.length === 0) {\n checks.push(check('image-alt-text', 'warn', 'No images found in content'));\n } else if (missingAlt.length === 0) {\n checks.push(check('image-alt-text', 'pass', 'All images have alt text'));\n } else {\n checks.push(check('image-alt-text', 'fail', `${missingAlt.length} image(s) missing alt text`));\n }\n\n // 13. Internal links\n const internalLinkRegex = /href=[\"']\\/[^\"']*[\"']/gi;\n const internalLinks = contentHTML.match(internalLinkRegex) || [];\n if (internalLinks.length > 0) {\n checks.push(check('internal-links', 'pass', `${internalLinks.length} internal link(s) found`));\n } else {\n checks.push(check('internal-links', 'warn', 'No internal links found — add links to related content'));\n }\n\n // 14. External links\n const externalLinkRegex = /href=[\"']https?:\\/\\/[^\"']*[\"']/gi;\n const externalLinks = contentHTML.match(externalLinkRegex) || [];\n if (externalLinks.length > 0) {\n checks.push(check('external-links', 'pass', `${externalLinks.length} external link(s) found`));\n } else {\n checks.push(check('external-links', 'warn', 'No external links found'));\n }\n\n // 15. Paragraph length\n const paragraphs = contentHTML.split(/<\\/p>/i).filter((p) => p.trim());\n const longParagraphs = paragraphs.filter((p) => {\n const text = p.replace(/<[^>]+>/g, '');\n return text.split(/\\s+/).length > 300;\n });\n if (longParagraphs.length === 0) {\n checks.push(check('paragraph-length', 'pass', 'All paragraphs are a reasonable length'));\n } else {\n checks.push(check('paragraph-length', 'warn', `${longParagraphs.length} paragraph(s) exceed 300 words`));\n }\n\n // 16. Cover image\n if (post.coverImage?.url) {\n checks.push(check('cover-image', 'pass', 'Post has a cover image'));\n } else {\n checks.push(check('cover-image', 'warn', 'No cover image set — social shares may look plain'));\n }\n\n // 17. Readability (simplified Flesch-like check)\n if (contentText) {\n const sentences = contentText.split(/[.!?]+/).filter((s) => s.trim().length > 0);\n const words = contentText.split(/\\s+/).length;\n const avgWordsPerSentence = sentences.length > 0 ? words / sentences.length : 0;\n if (avgWordsPerSentence <= 20) {\n checks.push(check('readability-score', 'pass', `Average sentence length: ${avgWordsPerSentence.toFixed(0)} words`));\n } else if (avgWordsPerSentence <= 25) {\n checks.push(check('readability-score', 'warn', `Average sentence length: ${avgWordsPerSentence.toFixed(0)} words (try to keep under 20)`));\n } else {\n checks.push(check('readability-score', 'fail', `Average sentence length: ${avgWordsPerSentence.toFixed(0)} words (too long, aim for under 20)`));\n }\n }\n\n // Calculate overall score\n const fails = checks.filter((c) => c.status === 'fail').length;\n const warns = checks.filter((c) => c.status === 'warn').length;\n\n let overall: SEOScore['overall'];\n if (fails >= 3) {\n overall = 'poor';\n } else if (fails >= 1 || warns >= 5) {\n overall = 'ok';\n } else {\n overall = 'good';\n }\n\n return { overall, checks };\n}\n"]}
@@ -0,0 +1,46 @@
1
+ import { getEnvConfig, getCollection } from './chunk-64HUVJOZ.js';
2
+
3
+ // src/lib/rss.ts
4
+ async function generateRSSFeed(fullContent = false) {
5
+ const env = getEnvConfig();
6
+ const posts = await getCollection("nbk_posts");
7
+ const publishedPosts = await posts.find({ status: "published" }).sort({ publishedAt: -1 }).limit(50).toArray();
8
+ const items = publishedPosts.map((post) => {
9
+ const postUrl = `${env.NEXTBLOGKIT_SITE_URL}/blog/${post.slug}`;
10
+ const content = fullContent ? post.contentHTML : post.excerpt;
11
+ const pubDate = post.publishedAt ? new Date(post.publishedAt).toUTCString() : new Date(post.createdAt).toUTCString();
12
+ let enclosure = "";
13
+ if (post.coverImage?.url) {
14
+ enclosure = `
15
+ <enclosure url="${escapeXml(post.coverImage.url)}" type="image/jpeg" />`;
16
+ }
17
+ return ` <item>
18
+ <title>${escapeXml(post.title)}</title>
19
+ <link>${escapeXml(postUrl)}</link>
20
+ <guid isPermaLink="true">${escapeXml(postUrl)}</guid>
21
+ <description><![CDATA[${content || ""}]]></description>
22
+ <pubDate>${pubDate}</pubDate>${enclosure}${post.categories?.length ? post.categories.map((c) => `
23
+ <category>${escapeXml(c)}</category>`).join("") : ""}
24
+ </item>`;
25
+ }).join("\n");
26
+ const buildDate = (/* @__PURE__ */ new Date()).toUTCString();
27
+ return `<?xml version="1.0" encoding="UTF-8"?>
28
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
29
+ <channel>
30
+ <title>${escapeXml(env.NEXTBLOGKIT_SITE_NAME)} Blog</title>
31
+ <link>${escapeXml(env.NEXTBLOGKIT_SITE_URL)}/blog</link>
32
+ <description>Latest posts from ${escapeXml(env.NEXTBLOGKIT_SITE_NAME)}</description>
33
+ <language>en</language>
34
+ <lastBuildDate>${buildDate}</lastBuildDate>
35
+ <atom:link href="${escapeXml(env.NEXTBLOGKIT_SITE_URL)}/api/blog/rss.xml" rel="self" type="application/rss+xml" />
36
+ ${items}
37
+ </channel>
38
+ </rss>`;
39
+ }
40
+ function escapeXml(str) {
41
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
42
+ }
43
+
44
+ export { generateRSSFeed };
45
+ //# sourceMappingURL=chunk-R6MO3QIP.js.map
46
+ //# sourceMappingURL=chunk-R6MO3QIP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/rss.ts"],"names":[],"mappings":";;;AAGA,eAAsB,eAAA,CAAgB,cAAc,KAAA,EAAwB;AAC1E,EAAA,MAAM,MAAM,YAAA,EAAa;AACzB,EAAA,MAAM,KAAA,GAAQ,MAAM,aAAA,CAAc,WAAW,CAAA;AAE7C,EAAA,MAAM,iBAAiB,MAAM,KAAA,CAC1B,KAAK,EAAE,MAAA,EAAQ,aAAa,CAAA,CAC5B,IAAA,CAAK,EAAE,aAAa,EAAA,EAAI,EACxB,KAAA,CAAM,EAAE,EACR,OAAA,EAAQ;AAEX,EAAA,MAAM,KAAA,GAAQ,cAAA,CACX,GAAA,CAAI,CAAC,IAAA,KAAS;AACb,IAAA,MAAM,UAAU,CAAA,EAAG,GAAA,CAAI,oBAAoB,CAAA,MAAA,EAAS,KAAK,IAAI,CAAA,CAAA;AAC7D,IAAA,MAAM,OAAA,GAAU,WAAA,GAAc,IAAA,CAAK,WAAA,GAAc,IAAA,CAAK,OAAA;AACtD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,WAAA,GACjB,IAAI,KAAK,IAAA,CAAK,WAAW,CAAA,CAAE,WAAA,KAC3B,IAAI,IAAA,CAAK,IAAA,CAAK,SAAS,EAAE,WAAA,EAAY;AAEzC,IAAA,IAAI,SAAA,GAAY,EAAA;AAChB,IAAA,IAAI,IAAA,CAAK,YAAY,GAAA,EAAK;AACxB,MAAA,SAAA,GAAY;AAAA,sBAAA,EAA2B,SAAA,CAAU,IAAA,CAAK,UAAA,CAAW,GAAG,CAAC,CAAA,sBAAA,CAAA;AAAA,IACvE;AAEA,IAAA,OAAO,CAAA;AAAA,aAAA,EACE,SAAA,CAAU,IAAA,CAAK,KAAK,CAAC,CAAA;AAAA,YAAA,EACtB,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA,+BAAA,EACC,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA,4BAAA,EACrB,WAAW,EAAE,CAAA;AAAA,eAAA,EAC1B,OAAO,CAAA,UAAA,EAAa,SAAS,CAAA,EACtC,IAAA,CAAK,UAAA,EAAY,MAAA,GACb,IAAA,CAAK,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,KAAc;AAAA,gBAAA,EAAqB,SAAA,CAAU,CAAC,CAAC,CAAA,WAAA,CAAa,EAAE,IAAA,CAAK,EAAE,IAC1F,EACN;AAAA,WAAA,CAAA;AAAA,EAEF,CAAC,CAAA,CACA,IAAA,CAAK,IAAI,CAAA;AAEZ,EAAA,MAAM,SAAA,GAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAEzC,EAAA,OAAO,CAAA;AAAA;AAAA;AAAA,WAAA,EAGI,SAAA,CAAU,GAAA,CAAI,qBAAqB,CAAC,CAAA;AAAA,UAAA,EACrC,SAAA,CAAU,GAAA,CAAI,oBAAoB,CAAC,CAAA;AAAA,mCAAA,EACV,SAAA,CAAU,GAAA,CAAI,qBAAqB,CAAC,CAAA;AAAA;AAAA,mBAAA,EAEpD,SAAS,CAAA;AAAA,qBAAA,EACP,SAAA,CAAU,GAAA,CAAI,oBAAoB,CAAC,CAAA;AAAA,EACxD,KAAK;AAAA;AAAA,MAAA,CAAA;AAGP;AAEA,SAAS,UAAU,GAAA,EAAqB;AACtC,EAAA,OAAO,IACJ,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CACrB,OAAA,CAAQ,MAAM,MAAM,CAAA,CACpB,QAAQ,IAAA,EAAM,MAAM,EACpB,OAAA,CAAQ,IAAA,EAAM,QAAQ,CAAA,CACtB,OAAA,CAAQ,MAAM,QAAQ,CAAA;AAC3B","file":"chunk-R6MO3QIP.js","sourcesContent":["import { getCollection } from './db';\nimport { getEnvConfig } from './config';\n\nexport async function generateRSSFeed(fullContent = false): Promise<string> {\n const env = getEnvConfig();\n const posts = await getCollection('nbk_posts');\n\n const publishedPosts = await posts\n .find({ status: 'published' })\n .sort({ publishedAt: -1 })\n .limit(50)\n .toArray();\n\n const items = publishedPosts\n .map((post) => {\n const postUrl = `${env.NEXTBLOGKIT_SITE_URL}/blog/${post.slug}`;\n const content = fullContent ? post.contentHTML : post.excerpt;\n const pubDate = post.publishedAt\n ? new Date(post.publishedAt).toUTCString()\n : new Date(post.createdAt).toUTCString();\n\n let enclosure = '';\n if (post.coverImage?.url) {\n enclosure = `\\n <enclosure url=\"${escapeXml(post.coverImage.url)}\" type=\"image/jpeg\" />`;\n }\n\n return ` <item>\n <title>${escapeXml(post.title)}</title>\n <link>${escapeXml(postUrl)}</link>\n <guid isPermaLink=\"true\">${escapeXml(postUrl)}</guid>\n <description><![CDATA[${content || ''}]]></description>\n <pubDate>${pubDate}</pubDate>${enclosure}${\n post.categories?.length\n ? post.categories.map((c: string) => `\\n <category>${escapeXml(c)}</category>`).join('')\n : ''\n }\n </item>`;\n })\n .join('\\n');\n\n const buildDate = new Date().toUTCString();\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n <channel>\n <title>${escapeXml(env.NEXTBLOGKIT_SITE_NAME)} Blog</title>\n <link>${escapeXml(env.NEXTBLOGKIT_SITE_URL)}/blog</link>\n <description>Latest posts from ${escapeXml(env.NEXTBLOGKIT_SITE_NAME)}</description>\n <language>en</language>\n <lastBuildDate>${buildDate}</lastBuildDate>\n <atom:link href=\"${escapeXml(env.NEXTBLOGKIT_SITE_URL)}/api/blog/rss.xml\" rel=\"self\" type=\"application/rss+xml\" />\n${items}\n </channel>\n</rss>`;\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n"]}
@@ -0,0 +1,476 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var mongodb = require('mongodb');
5
+
6
+ // src/lib/config.ts
7
+ var envSchema = zod.z.object({
8
+ NEXTBLOGKIT_MONGODB_URI: zod.z.string().min(1, "MongoDB URI is required"),
9
+ NEXTBLOGKIT_R2_ACCOUNT_ID: zod.z.string().min(1, "R2 Account ID is required"),
10
+ NEXTBLOGKIT_R2_ACCESS_KEY: zod.z.string().min(1, "R2 Access Key is required"),
11
+ NEXTBLOGKIT_R2_SECRET_KEY: zod.z.string().min(1, "R2 Secret Key is required"),
12
+ NEXTBLOGKIT_R2_BUCKET: zod.z.string().min(1, "R2 Bucket name is required"),
13
+ NEXTBLOGKIT_R2_PUBLIC_URL: zod.z.string().url("R2 Public URL must be a valid URL"),
14
+ NEXTBLOGKIT_API_KEY: zod.z.string().min(32, "API key must be at least 32 characters"),
15
+ NEXTBLOGKIT_SITE_URL: zod.z.string().url("Site URL must be a valid URL"),
16
+ NEXTBLOGKIT_SITE_NAME: zod.z.string().min(1, "Site name is required")
17
+ });
18
+ var cachedEnv = null;
19
+ function getEnvConfig() {
20
+ if (cachedEnv) return cachedEnv;
21
+ const result = envSchema.safeParse(process.env);
22
+ if (!result.success) {
23
+ const missing = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`);
24
+ throw new Error(`NextBlogKit configuration error:
25
+ ${missing.join("\n")}`);
26
+ }
27
+ cachedEnv = result.data;
28
+ return cachedEnv;
29
+ }
30
+ var defaultConfig = {
31
+ basePath: "/blog",
32
+ adminPath: "/admin/blog",
33
+ apiPath: "/api/blog",
34
+ postsPerPage: 10,
35
+ excerptLength: 160,
36
+ codeHighlighter: "shiki",
37
+ editor: {
38
+ blocks: [
39
+ "paragraph",
40
+ "heading",
41
+ "image",
42
+ "codeBlock",
43
+ "blockquote",
44
+ "bulletList",
45
+ "orderedList",
46
+ "taskList",
47
+ "table",
48
+ "embed",
49
+ "horizontalRule",
50
+ "callout",
51
+ "tableOfContents",
52
+ "faq",
53
+ "html"
54
+ ],
55
+ maxImageSize: 10 * 1024 * 1024,
56
+ imageFormats: ["jpg", "jpeg", "png", "gif", "webp", "svg"],
57
+ autosaveInterval: 3e4
58
+ },
59
+ seo: {
60
+ titleTemplate: "%s | %siteName%",
61
+ generateRSS: true,
62
+ generateSitemap: true,
63
+ structuredData: true,
64
+ minContentLength: 300
65
+ },
66
+ auth: {
67
+ strategy: "api-key"
68
+ },
69
+ features: {
70
+ search: true,
71
+ relatedPosts: true,
72
+ readingProgress: true,
73
+ tableOfContents: true,
74
+ shareButtons: true,
75
+ darkMode: true,
76
+ scheduling: true,
77
+ revisionHistory: true,
78
+ imageOptimization: true
79
+ },
80
+ theme: {
81
+ darkMode: true,
82
+ variables: {
83
+ "--nbk-primary": "#2563eb",
84
+ "--nbk-primary-hover": "#1d4ed8",
85
+ "--nbk-text": "#1f2937",
86
+ "--nbk-text-muted": "#6b7280",
87
+ "--nbk-bg": "#ffffff",
88
+ "--nbk-bg-secondary": "#f9fafb",
89
+ "--nbk-card-bg": "#ffffff",
90
+ "--nbk-border": "#e5e7eb",
91
+ "--nbk-radius": "0.5rem",
92
+ "--nbk-font-heading": '"Inter", system-ui, sans-serif',
93
+ "--nbk-font-body": '"Inter", system-ui, sans-serif',
94
+ "--nbk-font-code": '"JetBrains Mono", "Fira Code", monospace'
95
+ }
96
+ },
97
+ hooks: {}
98
+ };
99
+ var cachedConfig = null;
100
+ function defineConfig(config) {
101
+ return config;
102
+ }
103
+ function getConfig(userConfig) {
104
+ if (cachedConfig && !userConfig) return cachedConfig;
105
+ const merged = {
106
+ ...defaultConfig,
107
+ ...userConfig,
108
+ editor: { ...defaultConfig.editor, ...userConfig?.editor },
109
+ seo: { ...defaultConfig.seo, ...userConfig?.seo },
110
+ auth: { ...defaultConfig.auth, ...userConfig?.auth },
111
+ features: { ...defaultConfig.features, ...userConfig?.features },
112
+ theme: {
113
+ ...defaultConfig.theme,
114
+ ...userConfig?.theme,
115
+ variables: { ...defaultConfig.theme.variables, ...userConfig?.theme?.variables }
116
+ },
117
+ hooks: { ...defaultConfig.hooks, ...userConfig?.hooks }
118
+ };
119
+ if (!userConfig) {
120
+ cachedConfig = merged;
121
+ }
122
+ return merged;
123
+ }
124
+ function getBlogConfig() {
125
+ const env = getEnvConfig();
126
+ const config = getConfig();
127
+ return {
128
+ ...config,
129
+ siteUrl: env.NEXTBLOGKIT_SITE_URL,
130
+ siteName: env.NEXTBLOGKIT_SITE_NAME,
131
+ metadata: {
132
+ title: `Blog | ${env.NEXTBLOGKIT_SITE_NAME}`,
133
+ description: `Latest posts from ${env.NEXTBLOGKIT_SITE_NAME}`,
134
+ openGraph: {
135
+ title: `Blog | ${env.NEXTBLOGKIT_SITE_NAME}`,
136
+ description: `Latest posts from ${env.NEXTBLOGKIT_SITE_NAME}`,
137
+ url: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}`,
138
+ siteName: env.NEXTBLOGKIT_SITE_NAME,
139
+ type: "website"
140
+ }
141
+ }
142
+ };
143
+ }
144
+
145
+ // src/lib/slug.ts
146
+ function generateSlug(text) {
147
+ return text.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
148
+ }
149
+ async function ensureUniqueSlug(slug, collection, excludeId) {
150
+ let candidate = slug;
151
+ let counter = 1;
152
+ while (true) {
153
+ const query = { slug: candidate };
154
+ if (excludeId) {
155
+ query._id = { $ne: excludeId };
156
+ }
157
+ const existing = await collection.findOne(query);
158
+ if (!existing) return candidate;
159
+ candidate = `${slug}-${counter}`;
160
+ counter++;
161
+ }
162
+ }
163
+
164
+ // src/lib/reading-time.ts
165
+ var WORDS_PER_MINUTE = 200;
166
+ function calculateReadingTime(text) {
167
+ const words = countWords(text);
168
+ return Math.max(1, Math.ceil(words / WORDS_PER_MINUTE));
169
+ }
170
+ function countWords(text) {
171
+ if (!text || !text.trim()) return 0;
172
+ return text.trim().split(/\s+/).length;
173
+ }
174
+ function extractTextFromHTML(html) {
175
+ return html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/\s+/g, " ").trim();
176
+ }
177
+ function extractTextFromBlocks(blocks) {
178
+ if (!Array.isArray(blocks)) return "";
179
+ const parts = [];
180
+ function walk(node) {
181
+ if (!node || typeof node !== "object") return;
182
+ const n = node;
183
+ if (typeof n.text === "string") {
184
+ parts.push(n.text);
185
+ }
186
+ if (Array.isArray(n.content)) {
187
+ for (const child of n.content) {
188
+ walk(child);
189
+ }
190
+ }
191
+ }
192
+ for (const block of blocks) {
193
+ walk(block);
194
+ }
195
+ return parts.join(" ");
196
+ }
197
+ var client = null;
198
+ var db = null;
199
+ async function getDb() {
200
+ if (db) return db;
201
+ const env = getEnvConfig();
202
+ client = new mongodb.MongoClient(env.NEXTBLOGKIT_MONGODB_URI);
203
+ await client.connect();
204
+ db = client.db();
205
+ return db;
206
+ }
207
+ async function getCollection(name) {
208
+ const database = await getDb();
209
+ return database.collection(name);
210
+ }
211
+ async function ensureIndexes() {
212
+ const database = await getDb();
213
+ const posts = database.collection("nbk_posts");
214
+ await posts.createIndex({ slug: 1 }, { unique: true });
215
+ await posts.createIndex({ status: 1, publishedAt: -1 });
216
+ await posts.createIndex({ categories: 1, status: 1 });
217
+ await posts.createIndex({ tags: 1, status: 1 });
218
+ await posts.createIndex({ "seo.focusKeyword": 1 });
219
+ await posts.createIndex({ contentText: "text", title: "text", excerpt: "text" });
220
+ const categories = database.collection("nbk_categories");
221
+ await categories.createIndex({ slug: 1 }, { unique: true });
222
+ await categories.createIndex({ order: 1 });
223
+ const media = database.collection("nbk_media");
224
+ await media.createIndex({ createdAt: -1 });
225
+ await media.createIndex({ r2Key: 1 }, { unique: true });
226
+ }
227
+ async function createPost(input, defaultAuthor) {
228
+ const col = await getCollection("nbk_posts");
229
+ const slug = await ensureUniqueSlug(
230
+ input.slug || generateSlug(input.title),
231
+ col
232
+ );
233
+ const contentText = input.contentText || extractTextFromBlocks(input.content || []);
234
+ const wordCount = countWords(contentText);
235
+ const readingTime = calculateReadingTime(contentText);
236
+ const excerpt = input.excerpt || contentText.slice(0, 160).replace(/\s+\S*$/, "") + "...";
237
+ const now = /* @__PURE__ */ new Date();
238
+ const doc = {
239
+ title: input.title,
240
+ slug,
241
+ excerpt,
242
+ content: input.content || [],
243
+ contentHTML: input.contentHTML || "",
244
+ contentText,
245
+ coverImage: input.coverImage,
246
+ categories: input.categories || [],
247
+ tags: input.tags || [],
248
+ author: input.author || defaultAuthor || { name: "Admin" },
249
+ seo: {
250
+ ogType: "article",
251
+ noIndex: false,
252
+ ...input.seo
253
+ },
254
+ status: input.status || "draft",
255
+ publishedAt: input.status === "published" ? now : input.publishedAt,
256
+ scheduledAt: input.scheduledAt,
257
+ readingTime,
258
+ wordCount,
259
+ version: 1,
260
+ revisions: [],
261
+ createdAt: now,
262
+ updatedAt: now
263
+ };
264
+ const result = await col.insertOne(doc);
265
+ return { _id: result.insertedId, ...doc };
266
+ }
267
+ async function updatePost(id, input) {
268
+ const col = await getCollection("nbk_posts");
269
+ const objectId = new mongodb.ObjectId(id);
270
+ const existing = await col.findOne({ _id: objectId });
271
+ if (!existing) return null;
272
+ const updates = { ...input, updatedAt: /* @__PURE__ */ new Date() };
273
+ if (input.slug && input.slug !== existing.slug) {
274
+ updates.slug = await ensureUniqueSlug(input.slug, col, id);
275
+ }
276
+ if (input.content) {
277
+ const contentText = input.contentText || extractTextFromBlocks(input.content);
278
+ updates.contentText = contentText;
279
+ updates.wordCount = countWords(contentText);
280
+ updates.readingTime = calculateReadingTime(contentText);
281
+ if (!input.excerpt) {
282
+ updates.excerpt = contentText.slice(0, 160).replace(/\s+\S*$/, "") + "...";
283
+ }
284
+ const revision = {
285
+ version: existing.version || 1,
286
+ title: existing.title,
287
+ content: existing.content,
288
+ contentHTML: existing.contentHTML,
289
+ savedAt: /* @__PURE__ */ new Date()
290
+ };
291
+ const revisions = [...existing.revisions || [], revision].slice(-10);
292
+ updates.revisions = revisions;
293
+ updates.version = (existing.version || 1) + 1;
294
+ }
295
+ if (input.status === "published" && existing.status !== "published") {
296
+ updates.publishedAt = /* @__PURE__ */ new Date();
297
+ }
298
+ await col.updateOne({ _id: objectId }, { $set: updates });
299
+ return await col.findOne({ _id: objectId });
300
+ }
301
+ async function deletePost(id) {
302
+ const col = await getCollection("nbk_posts");
303
+ const result = await col.updateOne(
304
+ { _id: new mongodb.ObjectId(id) },
305
+ { $set: { status: "archived", updatedAt: /* @__PURE__ */ new Date() } }
306
+ );
307
+ return result.modifiedCount > 0;
308
+ }
309
+ async function getPostBySlug(slug) {
310
+ const col = await getCollection("nbk_posts");
311
+ return await col.findOne({ slug });
312
+ }
313
+ async function getPostById(id) {
314
+ const col = await getCollection("nbk_posts");
315
+ return await col.findOne({ _id: new mongodb.ObjectId(id) });
316
+ }
317
+ async function listPosts(query = {}) {
318
+ const col = await getCollection("nbk_posts");
319
+ const {
320
+ page = 1,
321
+ limit = 10,
322
+ category,
323
+ tag,
324
+ status,
325
+ search,
326
+ sortBy = "publishedAt",
327
+ sortOrder = "desc"
328
+ } = query;
329
+ const filter = {};
330
+ if (status) {
331
+ filter.status = status;
332
+ } else {
333
+ filter.status = { $ne: "archived" };
334
+ }
335
+ if (category) filter.categories = category;
336
+ if (tag) filter.tags = tag;
337
+ if (search) {
338
+ filter.$text = { $search: search };
339
+ }
340
+ const sort = {
341
+ [sortBy]: sortOrder === "asc" ? 1 : -1
342
+ };
343
+ const skip = (page - 1) * limit;
344
+ const [posts, total] = await Promise.all([
345
+ col.find(filter).sort(sort).skip(skip).limit(limit).toArray(),
346
+ col.countDocuments(filter)
347
+ ]);
348
+ return {
349
+ posts,
350
+ total
351
+ };
352
+ }
353
+ async function createCategory(input) {
354
+ const col = await getCollection("nbk_categories");
355
+ const slug = await ensureUniqueSlug(
356
+ input.slug || generateSlug(input.name),
357
+ col
358
+ );
359
+ const doc = {
360
+ name: input.name,
361
+ slug,
362
+ description: input.description,
363
+ seo: input.seo,
364
+ order: input.order ?? 0,
365
+ parentId: input.parentId ? new mongodb.ObjectId(input.parentId) : void 0,
366
+ postCount: 0
367
+ };
368
+ const result = await col.insertOne(doc);
369
+ return { _id: result.insertedId, ...doc };
370
+ }
371
+ async function updateCategory(id, input) {
372
+ const col = await getCollection("nbk_categories");
373
+ const objectId = new mongodb.ObjectId(id);
374
+ const updates = { ...input };
375
+ if (input.slug) {
376
+ updates.slug = await ensureUniqueSlug(input.slug, col, id);
377
+ }
378
+ if (input.parentId) {
379
+ updates.parentId = new mongodb.ObjectId(input.parentId);
380
+ }
381
+ await col.updateOne({ _id: objectId }, { $set: updates });
382
+ return await col.findOne({ _id: objectId });
383
+ }
384
+ async function deleteCategory(id) {
385
+ const col = await getCollection("nbk_categories");
386
+ const result = await col.deleteOne({ _id: new mongodb.ObjectId(id) });
387
+ return result.deletedCount > 0;
388
+ }
389
+ async function listCategories() {
390
+ const col = await getCollection("nbk_categories");
391
+ return await col.find({}).sort({ order: 1 }).toArray();
392
+ }
393
+ async function getCategoryBySlug(slug) {
394
+ const col = await getCollection("nbk_categories");
395
+ return await col.findOne({ slug });
396
+ }
397
+ async function createMedia(data) {
398
+ const col = await getCollection("nbk_media");
399
+ const result = await col.insertOne(data);
400
+ return { _id: result.insertedId, ...data };
401
+ }
402
+ async function deleteMedia(id) {
403
+ const col = await getCollection("nbk_media");
404
+ const media = await col.findOne({ _id: new mongodb.ObjectId(id) });
405
+ if (!media) return null;
406
+ await col.deleteOne({ _id: new mongodb.ObjectId(id) });
407
+ return media;
408
+ }
409
+ async function listMedia(query = {}) {
410
+ const col = await getCollection("nbk_media");
411
+ const { page = 1, limit = 20, mimeType } = query;
412
+ const filter = {};
413
+ if (mimeType) filter.mimeType = { $regex: mimeType };
414
+ const skip = (page - 1) * limit;
415
+ const [media, total] = await Promise.all([
416
+ col.find(filter).sort({ createdAt: -1 }).skip(skip).limit(limit).toArray(),
417
+ col.countDocuments(filter)
418
+ ]);
419
+ return { media, total };
420
+ }
421
+ async function getSettings() {
422
+ const col = await getCollection("nbk_settings");
423
+ const settings = await col.findOne({ _id: "global" });
424
+ if (!settings) {
425
+ const defaults = {
426
+ _id: "global",
427
+ postsPerPage: 10,
428
+ commentSystem: "none"
429
+ };
430
+ await col.insertOne(defaults);
431
+ return defaults;
432
+ }
433
+ return settings;
434
+ }
435
+ async function updateSettings(data) {
436
+ const col = await getCollection("nbk_settings");
437
+ const { _id, ...updates } = data;
438
+ await col.updateOne(
439
+ { _id: "global" },
440
+ { $set: updates },
441
+ { upsert: true }
442
+ );
443
+ return getSettings();
444
+ }
445
+
446
+ exports.calculateReadingTime = calculateReadingTime;
447
+ exports.countWords = countWords;
448
+ exports.createCategory = createCategory;
449
+ exports.createMedia = createMedia;
450
+ exports.createPost = createPost;
451
+ exports.defineConfig = defineConfig;
452
+ exports.deleteCategory = deleteCategory;
453
+ exports.deleteMedia = deleteMedia;
454
+ exports.deletePost = deletePost;
455
+ exports.ensureIndexes = ensureIndexes;
456
+ exports.ensureUniqueSlug = ensureUniqueSlug;
457
+ exports.extractTextFromBlocks = extractTextFromBlocks;
458
+ exports.extractTextFromHTML = extractTextFromHTML;
459
+ exports.generateSlug = generateSlug;
460
+ exports.getBlogConfig = getBlogConfig;
461
+ exports.getCategoryBySlug = getCategoryBySlug;
462
+ exports.getCollection = getCollection;
463
+ exports.getConfig = getConfig;
464
+ exports.getDb = getDb;
465
+ exports.getEnvConfig = getEnvConfig;
466
+ exports.getPostById = getPostById;
467
+ exports.getPostBySlug = getPostBySlug;
468
+ exports.getSettings = getSettings;
469
+ exports.listCategories = listCategories;
470
+ exports.listMedia = listMedia;
471
+ exports.listPosts = listPosts;
472
+ exports.updateCategory = updateCategory;
473
+ exports.updatePost = updatePost;
474
+ exports.updateSettings = updateSettings;
475
+ //# sourceMappingURL=chunk-U2ROR6AY.cjs.map
476
+ //# sourceMappingURL=chunk-U2ROR6AY.cjs.map