nextblogkit 0.6.2 → 0.7.2
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.
- package/README.md +84 -21
- package/dist/admin/index.cjs +656 -115
- package/dist/admin/index.cjs.map +1 -1
- package/dist/admin/index.d.cts +7 -3
- package/dist/admin/index.d.ts +7 -3
- package/dist/admin/index.js +575 -37
- package/dist/admin/index.js.map +1 -1
- package/dist/api/categories.cjs +32 -32
- package/dist/api/categories.cjs.map +1 -1
- package/dist/api/categories.d.cts +1 -1
- package/dist/api/categories.d.ts +1 -1
- package/dist/api/categories.js +6 -6
- package/dist/api/categories.js.map +1 -1
- package/dist/api/media.cjs +37 -30
- package/dist/api/media.cjs.map +1 -1
- package/dist/api/media.d.cts +1 -1
- package/dist/api/media.d.ts +1 -1
- package/dist/api/media.js +13 -6
- package/dist/api/media.js.map +1 -1
- package/dist/api/posts.cjs +39 -39
- package/dist/api/posts.cjs.map +1 -1
- package/dist/api/posts.d.cts +1 -1
- package/dist/api/posts.d.ts +1 -1
- package/dist/api/posts.js +6 -6
- package/dist/api/posts.js.map +1 -1
- package/dist/api/rss.cjs +3 -3
- package/dist/api/rss.js +2 -2
- package/dist/api/settings.cjs +13 -13
- package/dist/api/settings.cjs.map +1 -1
- package/dist/api/settings.d.cts +1 -1
- package/dist/api/settings.d.ts +1 -1
- package/dist/api/settings.js +5 -5
- package/dist/api/settings.js.map +1 -1
- package/dist/api/sitemap.cjs +3 -3
- package/dist/api/sitemap.js +2 -2
- package/dist/api/tokens.cjs +56 -0
- package/dist/api/tokens.cjs.map +1 -0
- package/dist/api/tokens.d.cts +22 -0
- package/dist/api/tokens.d.ts +22 -0
- package/dist/api/tokens.js +52 -0
- package/dist/api/tokens.js.map +1 -0
- package/dist/{chunk-6HKMZOI4.cjs → chunk-3BKPNOES.cjs} +8 -7
- package/dist/chunk-3BKPNOES.cjs.map +1 -0
- package/dist/{chunk-N5MKAD7J.cjs → chunk-DR7QNI32.cjs} +6 -2
- package/dist/chunk-DR7QNI32.cjs.map +1 -0
- package/dist/{chunk-QE4VLQYN.cjs → chunk-F47RPOTU.cjs} +13 -10
- package/dist/chunk-F47RPOTU.cjs.map +1 -0
- package/dist/{chunk-64HUVJOZ.js → chunk-JI2RK6KX.js} +80 -13
- package/dist/chunk-JI2RK6KX.js.map +1 -0
- package/dist/{chunk-R6MO3QIP.js → chunk-NSR7NYSB.js} +6 -5
- package/dist/chunk-NSR7NYSB.js.map +1 -0
- package/dist/{chunk-4PY224XM.js → chunk-O3XES5O2.js} +6 -3
- package/dist/chunk-O3XES5O2.js.map +1 -0
- package/dist/{chunk-4NKOJYWJ.js → chunk-OOUJYUGP.js} +8 -7
- package/dist/chunk-OOUJYUGP.js.map +1 -0
- package/dist/{chunk-A2S32RZN.js → chunk-OWWWTTUT.js} +8 -3
- package/dist/chunk-OWWWTTUT.js.map +1 -0
- package/dist/{chunk-E2QLTHKN.cjs → chunk-QBZLGBHQ.cjs} +11 -10
- package/dist/chunk-QBZLGBHQ.cjs.map +1 -0
- package/dist/{chunk-ZP5XRVVH.cjs → chunk-SUJT6LWH.cjs} +12 -7
- package/dist/chunk-SUJT6LWH.cjs.map +1 -0
- package/dist/{chunk-JM7QRXXK.js → chunk-TVHY4BR2.js} +10 -7
- package/dist/chunk-TVHY4BR2.js.map +1 -0
- package/dist/{chunk-JLPJKNRZ.js → chunk-UMIBGO4S.js} +18 -5
- package/dist/chunk-UMIBGO4S.js.map +1 -0
- package/dist/{chunk-U2ROR6AY.cjs → chunk-VWKVU3SE.cjs} +86 -12
- package/dist/chunk-VWKVU3SE.cjs.map +1 -0
- package/dist/{chunk-KDZER3PU.cjs → chunk-YTJQ426D.cjs} +19 -5
- package/dist/chunk-YTJQ426D.cjs.map +1 -0
- package/dist/cli/index.cjs +90 -19
- package/dist/components/index.cjs +12 -6
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +2 -1
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.js +12 -6
- package/dist/components/index.js.map +1 -1
- package/dist/db-OUSQPM53.js +3 -0
- package/dist/db-OUSQPM53.js.map +1 -0
- package/dist/db-RFY6O5UE.cjs +108 -0
- package/dist/db-RFY6O5UE.cjs.map +1 -0
- package/dist/editor/index.cjs +49 -12
- package/dist/editor/index.cjs.map +1 -1
- package/dist/editor/index.d.cts +5 -1
- package/dist/editor/index.d.ts +5 -1
- package/dist/editor/index.js +37 -1
- package/dist/editor/index.js.map +1 -1
- package/dist/{index-vjlZDWNr.d.cts → index-Bk8gOqBq.d.cts} +25 -21
- package/dist/{index-Cgzphklp.d.ts → index-DsnG2kdW.d.ts} +25 -21
- package/dist/index.cjs +47 -47
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/lib/index.cjs +39 -35
- package/dist/lib/index.d.cts +2 -2
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +5 -5
- package/dist/{types-CBEEBR4A.d.ts → types-Cu515Egx.d.cts} +16 -1
- package/dist/{types-CBEEBR4A.d.cts → types-Cu515Egx.d.ts} +16 -1
- package/package.json +1 -1
- package/dist/chunk-4NKOJYWJ.js.map +0 -1
- package/dist/chunk-4PY224XM.js.map +0 -1
- package/dist/chunk-64HUVJOZ.js.map +0 -1
- package/dist/chunk-6HKMZOI4.cjs.map +0 -1
- package/dist/chunk-A2S32RZN.js.map +0 -1
- package/dist/chunk-E2QLTHKN.cjs.map +0 -1
- package/dist/chunk-JLPJKNRZ.js.map +0 -1
- package/dist/chunk-JM7QRXXK.js.map +0 -1
- package/dist/chunk-KDZER3PU.cjs.map +0 -1
- package/dist/chunk-N5MKAD7J.cjs.map +0 -1
- package/dist/chunk-QE4VLQYN.cjs.map +0 -1
- package/dist/chunk-R6MO3QIP.js.map +0 -1
- package/dist/chunk-U2ROR6AY.cjs.map +0 -1
- package/dist/chunk-ZP5XRVVH.cjs.map +0 -1
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { getEnvConfig, getCollection } from './chunk-
|
|
1
|
+
import { getEnvConfig, getConfig, getCollection } from './chunk-JI2RK6KX.js';
|
|
2
2
|
|
|
3
3
|
// src/lib/sitemap.ts
|
|
4
4
|
async function generateSitemap() {
|
|
5
5
|
const env = getEnvConfig();
|
|
6
|
+
const config = getConfig();
|
|
6
7
|
const posts = await getCollection("nbk_posts");
|
|
7
8
|
const categories = await getCollection("nbk_categories");
|
|
8
9
|
const entries = [];
|
|
9
10
|
entries.push({
|
|
10
|
-
loc: `${env.NEXTBLOGKIT_SITE_URL}
|
|
11
|
+
loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}`,
|
|
11
12
|
changefreq: "daily",
|
|
12
13
|
priority: "0.9"
|
|
13
14
|
});
|
|
@@ -19,7 +20,7 @@ async function generateSitemap() {
|
|
|
19
20
|
if (daysSincePublish < 7) changefreq = "daily";
|
|
20
21
|
else if (daysSincePublish < 30) changefreq = "weekly";
|
|
21
22
|
entries.push({
|
|
22
|
-
loc: `${env.NEXTBLOGKIT_SITE_URL}
|
|
23
|
+
loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/${post.slug}`,
|
|
23
24
|
lastmod: lastmod ? new Date(lastmod).toISOString().split("T")[0] : void 0,
|
|
24
25
|
changefreq,
|
|
25
26
|
priority: "0.8"
|
|
@@ -28,7 +29,7 @@ async function generateSitemap() {
|
|
|
28
29
|
const allCategories = await categories.find({}).sort({ order: 1 }).project({ slug: 1 }).toArray();
|
|
29
30
|
for (const cat of allCategories) {
|
|
30
31
|
entries.push({
|
|
31
|
-
loc: `${env.NEXTBLOGKIT_SITE_URL}/
|
|
32
|
+
loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/category/${cat.slug}`,
|
|
32
33
|
changefreq: "weekly",
|
|
33
34
|
priority: "0.6"
|
|
34
35
|
});
|
|
@@ -38,7 +39,7 @@ async function generateSitemap() {
|
|
|
38
39
|
const totalPages = Math.ceil(totalPosts / postsPerPage);
|
|
39
40
|
for (let page = 2; page <= totalPages; page++) {
|
|
40
41
|
entries.push({
|
|
41
|
-
loc: `${env.NEXTBLOGKIT_SITE_URL}
|
|
42
|
+
loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}?page=${page}`,
|
|
42
43
|
changefreq: "weekly",
|
|
43
44
|
priority: "0.5"
|
|
44
45
|
});
|
|
@@ -64,5 +65,5 @@ function escapeXml(str) {
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
export { generateSitemap };
|
|
67
|
-
//# sourceMappingURL=chunk-
|
|
68
|
-
//# sourceMappingURL=chunk-
|
|
68
|
+
//# sourceMappingURL=chunk-OOUJYUGP.js.map
|
|
69
|
+
//# sourceMappingURL=chunk-OOUJYUGP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/sitemap.ts"],"names":[],"mappings":"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","file":"chunk-OOUJYUGP.js","sourcesContent":["import { getCollection } from './db';\nimport { getEnvConfig, getConfig } from './config';\n\ninterface SitemapEntry {\n loc: string;\n lastmod?: string;\n changefreq?: string;\n priority?: string;\n}\n\nexport async function generateSitemap(): Promise<string> {\n const env = getEnvConfig();\n const config = getConfig();\n const posts = await getCollection('nbk_posts');\n const categories = await getCollection('nbk_categories');\n\n const entries: SitemapEntry[] = [];\n\n // Blog listing page\n entries.push({\n loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}`,\n changefreq: 'daily',\n priority: '0.9',\n });\n\n // Published posts\n const publishedPosts = await posts\n .find({ status: 'published' })\n .sort({ publishedAt: -1 })\n .project({ slug: 1, updatedAt: 1, publishedAt: 1 })\n .toArray();\n\n for (const post of publishedPosts) {\n const lastmod = post.updatedAt || post.publishedAt;\n const daysSincePublish = lastmod\n ? Math.floor((Date.now() - new Date(lastmod).getTime()) / (1000 * 60 * 60 * 24))\n : 0;\n\n let changefreq = 'monthly';\n if (daysSincePublish < 7) changefreq = 'daily';\n else if (daysSincePublish < 30) changefreq = 'weekly';\n\n entries.push({\n loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/${post.slug}`,\n lastmod: lastmod ? new Date(lastmod).toISOString().split('T')[0] : undefined,\n changefreq,\n priority: '0.8',\n });\n }\n\n // Category pages\n const allCategories = await categories\n .find({})\n .sort({ order: 1 })\n .project({ slug: 1 })\n .toArray();\n\n for (const cat of allCategories) {\n entries.push({\n loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/category/${cat.slug}`,\n changefreq: 'weekly',\n priority: '0.6',\n });\n }\n\n // Paginated listing pages\n const totalPosts = publishedPosts.length;\n const postsPerPage = 10;\n const totalPages = Math.ceil(totalPosts / postsPerPage);\n for (let page = 2; page <= totalPages; page++) {\n entries.push({\n loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}?page=${page}`,\n changefreq: 'weekly',\n priority: '0.5',\n });\n }\n\n return buildXML(entries);\n}\n\nfunction buildXML(entries: SitemapEntry[]): string {\n const urls = entries\n .map(\n (entry) => ` <url>\n <loc>${escapeXml(entry.loc)}</loc>${entry.lastmod ? `\\n <lastmod>${entry.lastmod}</lastmod>` : ''}${entry.changefreq ? `\\n <changefreq>${entry.changefreq}</changefreq>` : ''}${entry.priority ? `\\n <priority>${entry.priority}</priority>` : ''}\n </url>`\n )\n .join('\\n');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urls}\n</urlset>`;\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n"]}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
import { getEnvConfig } from './chunk-
|
|
1
|
+
import { getEnvConfig, isR2Configured } from './chunk-JI2RK6KX.js';
|
|
2
2
|
import { PutObjectCommand, DeleteObjectCommand, ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3';
|
|
3
3
|
import { randomUUID } from 'crypto';
|
|
4
4
|
|
|
5
5
|
var clientInstance = null;
|
|
6
6
|
function getS3Client() {
|
|
7
7
|
if (clientInstance) return clientInstance;
|
|
8
|
+
if (!isR2Configured()) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"Cloudflare R2 is not configured. Set NEXTBLOGKIT_R2_ACCOUNT_ID, NEXTBLOGKIT_R2_ACCESS_KEY, NEXTBLOGKIT_R2_SECRET_KEY, NEXTBLOGKIT_R2_BUCKET, and NEXTBLOGKIT_R2_PUBLIC_URL environment variables to enable image uploads."
|
|
11
|
+
);
|
|
12
|
+
}
|
|
8
13
|
const env = getEnvConfig();
|
|
9
14
|
clientInstance = new S3Client({
|
|
10
15
|
region: "auto",
|
|
@@ -134,5 +139,5 @@ function getExtension(filename) {
|
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
export { R2StorageProvider, processImage };
|
|
137
|
-
//# sourceMappingURL=chunk-
|
|
138
|
-
//# sourceMappingURL=chunk-
|
|
142
|
+
//# sourceMappingURL=chunk-OWWWTTUT.js.map
|
|
143
|
+
//# sourceMappingURL=chunk-OWWWTTUT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/storage.ts","../src/lib/image.ts"],"names":[],"mappings":"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","file":"chunk-OWWWTTUT.js","sourcesContent":["import {\n S3Client,\n PutObjectCommand,\n DeleteObjectCommand,\n ListObjectsV2Command,\n} from '@aws-sdk/client-s3';\nimport { getEnvConfig, isR2Configured } from './config';\nimport { randomUUID } from 'crypto';\n\nexport interface MediaUploadResult {\n key: string;\n url: string;\n size: number;\n contentType: string;\n}\n\nexport interface StorageObject {\n key: string;\n size: number;\n lastModified: Date;\n}\n\nexport interface StorageProvider {\n upload(file: Buffer, filename: string, contentType: string): Promise<MediaUploadResult>;\n delete(key: string): Promise<void>;\n list(prefix?: string): Promise<StorageObject[]>;\n}\n\nlet clientInstance: S3Client | null = null;\n\nfunction getS3Client(): S3Client {\n if (clientInstance) return clientInstance;\n\n if (!isR2Configured()) {\n throw new Error(\n 'Cloudflare R2 is not configured. Set NEXTBLOGKIT_R2_ACCOUNT_ID, NEXTBLOGKIT_R2_ACCESS_KEY, NEXTBLOGKIT_R2_SECRET_KEY, NEXTBLOGKIT_R2_BUCKET, and NEXTBLOGKIT_R2_PUBLIC_URL environment variables to enable image uploads.'\n );\n }\n\n const env = getEnvConfig();\n clientInstance = new S3Client({\n region: 'auto',\n endpoint: `https://${env.NEXTBLOGKIT_R2_ACCOUNT_ID!}.r2.cloudflarestorage.com`,\n credentials: {\n accessKeyId: env.NEXTBLOGKIT_R2_ACCESS_KEY!,\n secretAccessKey: env.NEXTBLOGKIT_R2_SECRET_KEY!,\n },\n });\n\n return clientInstance;\n}\n\nfunction generateKey(filename: string): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const uuid = randomUUID().split('-')[0];\n const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, '-').toLowerCase();\n return `blog/${year}/${month}/${uuid}-${safeName}`;\n}\n\nexport class R2StorageProvider implements StorageProvider {\n async upload(\n file: Buffer,\n filename: string,\n contentType: string\n ): Promise<MediaUploadResult> {\n const env = getEnvConfig();\n const client = getS3Client();\n const key = generateKey(filename);\n\n await client.send(\n new PutObjectCommand({\n Bucket: env.NEXTBLOGKIT_R2_BUCKET,\n Key: key,\n Body: file,\n ContentType: contentType,\n })\n );\n\n return {\n key,\n url: `${env.NEXTBLOGKIT_R2_PUBLIC_URL}/${key}`,\n size: file.length,\n contentType,\n };\n }\n\n async delete(key: string): Promise<void> {\n const env = getEnvConfig();\n const client = getS3Client();\n\n await client.send(\n new DeleteObjectCommand({\n Bucket: env.NEXTBLOGKIT_R2_BUCKET,\n Key: key,\n })\n );\n }\n\n async list(prefix?: string): Promise<StorageObject[]> {\n const env = getEnvConfig();\n const client = getS3Client();\n\n const response = await client.send(\n new ListObjectsV2Command({\n Bucket: env.NEXTBLOGKIT_R2_BUCKET,\n Prefix: prefix || 'blog/',\n })\n );\n\n return (response.Contents || []).map((obj) => ({\n key: obj.Key || '',\n size: obj.Size || 0,\n lastModified: obj.LastModified || new Date(),\n }));\n }\n}\n","import type { R2StorageProvider, MediaUploadResult } from './storage';\n\nexport interface ProcessedImage {\n original: MediaUploadResult;\n width: number;\n height: number;\n format: string;\n}\n\nconst RESPONSIVE_SIZES = [640, 768, 1024, 1280, 1920];\n\nexport async function processImage(\n file: Buffer,\n filename: string,\n storage: R2StorageProvider\n): Promise<ProcessedImage> {\n let sharp: typeof import('sharp');\n try {\n sharp = (await import('sharp')).default;\n } catch {\n // sharp not available — upload raw file\n const result = await storage.upload(file, filename, getMimeType(filename));\n return {\n original: result,\n width: 0,\n height: 0,\n format: getExtension(filename),\n };\n }\n\n const image = sharp(file);\n const metadata = await image.metadata();\n\n // Convert to WebP\n const webpFilename = filename.replace(/\\.[^.]+$/, '.webp');\n const webpBuffer = await image.webp({ quality: 85 }).toBuffer();\n\n const original = await storage.upload(webpBuffer, webpFilename, 'image/webp');\n\n // Generate responsive sizes in background (non-blocking for the main upload)\n generateResponsiveSizes(file, webpFilename, storage, metadata.width || 0).catch(\n () => {\n // Silently fail responsive generation — originals are sufficient\n }\n );\n\n return {\n original,\n width: metadata.width || 0,\n height: metadata.height || 0,\n format: 'webp',\n };\n}\n\nasync function generateResponsiveSizes(\n file: Buffer,\n filename: string,\n storage: R2StorageProvider,\n originalWidth: number\n): Promise<void> {\n const sharp = (await import('sharp')).default;\n\n const sizes = RESPONSIVE_SIZES.filter((s) => s < originalWidth);\n\n await Promise.all(\n sizes.map(async (width) => {\n const resized = await sharp(file)\n .resize(width)\n .webp({ quality: 80 })\n .toBuffer();\n\n const sizedFilename = filename.replace('.webp', `-${width}w.webp`);\n await storage.upload(resized, sizedFilename, 'image/webp');\n })\n );\n\n // Generate thumbnail\n const thumb = await sharp(file)\n .resize(200, 200, { fit: 'cover' })\n .webp({ quality: 70 })\n .toBuffer();\n\n const thumbFilename = filename.replace('.webp', '-thumb.webp');\n await storage.upload(thumb, thumbFilename, 'image/webp');\n}\n\nfunction getMimeType(filename: string): string {\n const ext = getExtension(filename);\n const mimeTypes: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n avif: 'image/avif',\n };\n return mimeTypes[ext] || 'application/octet-stream';\n}\n\nfunction getExtension(filename: string): string {\n return filename.split('.').pop()?.toLowerCase() || '';\n}\n"]}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkVWKVU3SE_cjs = require('./chunk-VWKVU3SE.cjs');
|
|
4
4
|
|
|
5
5
|
// src/lib/sitemap.ts
|
|
6
6
|
async function generateSitemap() {
|
|
7
|
-
const env =
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
const env = chunkVWKVU3SE_cjs.getEnvConfig();
|
|
8
|
+
const config = chunkVWKVU3SE_cjs.getConfig();
|
|
9
|
+
const posts = await chunkVWKVU3SE_cjs.getCollection("nbk_posts");
|
|
10
|
+
const categories = await chunkVWKVU3SE_cjs.getCollection("nbk_categories");
|
|
10
11
|
const entries = [];
|
|
11
12
|
entries.push({
|
|
12
|
-
loc: `${env.NEXTBLOGKIT_SITE_URL}
|
|
13
|
+
loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}`,
|
|
13
14
|
changefreq: "daily",
|
|
14
15
|
priority: "0.9"
|
|
15
16
|
});
|
|
@@ -21,7 +22,7 @@ async function generateSitemap() {
|
|
|
21
22
|
if (daysSincePublish < 7) changefreq = "daily";
|
|
22
23
|
else if (daysSincePublish < 30) changefreq = "weekly";
|
|
23
24
|
entries.push({
|
|
24
|
-
loc: `${env.NEXTBLOGKIT_SITE_URL}
|
|
25
|
+
loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/${post.slug}`,
|
|
25
26
|
lastmod: lastmod ? new Date(lastmod).toISOString().split("T")[0] : void 0,
|
|
26
27
|
changefreq,
|
|
27
28
|
priority: "0.8"
|
|
@@ -30,7 +31,7 @@ async function generateSitemap() {
|
|
|
30
31
|
const allCategories = await categories.find({}).sort({ order: 1 }).project({ slug: 1 }).toArray();
|
|
31
32
|
for (const cat of allCategories) {
|
|
32
33
|
entries.push({
|
|
33
|
-
loc: `${env.NEXTBLOGKIT_SITE_URL}/
|
|
34
|
+
loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/category/${cat.slug}`,
|
|
34
35
|
changefreq: "weekly",
|
|
35
36
|
priority: "0.6"
|
|
36
37
|
});
|
|
@@ -40,7 +41,7 @@ async function generateSitemap() {
|
|
|
40
41
|
const totalPages = Math.ceil(totalPosts / postsPerPage);
|
|
41
42
|
for (let page = 2; page <= totalPages; page++) {
|
|
42
43
|
entries.push({
|
|
43
|
-
loc: `${env.NEXTBLOGKIT_SITE_URL}
|
|
44
|
+
loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}?page=${page}`,
|
|
44
45
|
changefreq: "weekly",
|
|
45
46
|
priority: "0.5"
|
|
46
47
|
});
|
|
@@ -66,5 +67,5 @@ function escapeXml(str) {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
exports.generateSitemap = generateSitemap;
|
|
69
|
-
//# sourceMappingURL=chunk-
|
|
70
|
-
//# sourceMappingURL=chunk-
|
|
70
|
+
//# sourceMappingURL=chunk-QBZLGBHQ.cjs.map
|
|
71
|
+
//# sourceMappingURL=chunk-QBZLGBHQ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/sitemap.ts"],"names":["getEnvConfig","getConfig","getCollection"],"mappings":"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","file":"chunk-QBZLGBHQ.cjs","sourcesContent":["import { getCollection } from './db';\nimport { getEnvConfig, getConfig } from './config';\n\ninterface SitemapEntry {\n loc: string;\n lastmod?: string;\n changefreq?: string;\n priority?: string;\n}\n\nexport async function generateSitemap(): Promise<string> {\n const env = getEnvConfig();\n const config = getConfig();\n const posts = await getCollection('nbk_posts');\n const categories = await getCollection('nbk_categories');\n\n const entries: SitemapEntry[] = [];\n\n // Blog listing page\n entries.push({\n loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}`,\n changefreq: 'daily',\n priority: '0.9',\n });\n\n // Published posts\n const publishedPosts = await posts\n .find({ status: 'published' })\n .sort({ publishedAt: -1 })\n .project({ slug: 1, updatedAt: 1, publishedAt: 1 })\n .toArray();\n\n for (const post of publishedPosts) {\n const lastmod = post.updatedAt || post.publishedAt;\n const daysSincePublish = lastmod\n ? Math.floor((Date.now() - new Date(lastmod).getTime()) / (1000 * 60 * 60 * 24))\n : 0;\n\n let changefreq = 'monthly';\n if (daysSincePublish < 7) changefreq = 'daily';\n else if (daysSincePublish < 30) changefreq = 'weekly';\n\n entries.push({\n loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/${post.slug}`,\n lastmod: lastmod ? new Date(lastmod).toISOString().split('T')[0] : undefined,\n changefreq,\n priority: '0.8',\n });\n }\n\n // Category pages\n const allCategories = await categories\n .find({})\n .sort({ order: 1 })\n .project({ slug: 1 })\n .toArray();\n\n for (const cat of allCategories) {\n entries.push({\n loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/category/${cat.slug}`,\n changefreq: 'weekly',\n priority: '0.6',\n });\n }\n\n // Paginated listing pages\n const totalPosts = publishedPosts.length;\n const postsPerPage = 10;\n const totalPages = Math.ceil(totalPosts / postsPerPage);\n for (let page = 2; page <= totalPages; page++) {\n entries.push({\n loc: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}?page=${page}`,\n changefreq: 'weekly',\n priority: '0.5',\n });\n }\n\n return buildXML(entries);\n}\n\nfunction buildXML(entries: SitemapEntry[]): string {\n const urls = entries\n .map(\n (entry) => ` <url>\n <loc>${escapeXml(entry.loc)}</loc>${entry.lastmod ? `\\n <lastmod>${entry.lastmod}</lastmod>` : ''}${entry.changefreq ? `\\n <changefreq>${entry.changefreq}</changefreq>` : ''}${entry.priority ? `\\n <priority>${entry.priority}</priority>` : ''}\n </url>`\n )\n .join('\\n');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urls}\n</urlset>`;\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n"]}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkVWKVU3SE_cjs = require('./chunk-VWKVU3SE.cjs');
|
|
4
4
|
var clientS3 = require('@aws-sdk/client-s3');
|
|
5
5
|
var crypto = require('crypto');
|
|
6
6
|
|
|
7
7
|
var clientInstance = null;
|
|
8
8
|
function getS3Client() {
|
|
9
9
|
if (clientInstance) return clientInstance;
|
|
10
|
-
|
|
10
|
+
if (!chunkVWKVU3SE_cjs.isR2Configured()) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
"Cloudflare R2 is not configured. Set NEXTBLOGKIT_R2_ACCOUNT_ID, NEXTBLOGKIT_R2_ACCESS_KEY, NEXTBLOGKIT_R2_SECRET_KEY, NEXTBLOGKIT_R2_BUCKET, and NEXTBLOGKIT_R2_PUBLIC_URL environment variables to enable image uploads."
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
const env = chunkVWKVU3SE_cjs.getEnvConfig();
|
|
11
16
|
clientInstance = new clientS3.S3Client({
|
|
12
17
|
region: "auto",
|
|
13
18
|
endpoint: `https://${env.NEXTBLOGKIT_R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
|
@@ -28,7 +33,7 @@ function generateKey(filename) {
|
|
|
28
33
|
}
|
|
29
34
|
var R2StorageProvider = class {
|
|
30
35
|
async upload(file, filename, contentType) {
|
|
31
|
-
const env =
|
|
36
|
+
const env = chunkVWKVU3SE_cjs.getEnvConfig();
|
|
32
37
|
const client = getS3Client();
|
|
33
38
|
const key = generateKey(filename);
|
|
34
39
|
await client.send(
|
|
@@ -47,7 +52,7 @@ var R2StorageProvider = class {
|
|
|
47
52
|
};
|
|
48
53
|
}
|
|
49
54
|
async delete(key) {
|
|
50
|
-
const env =
|
|
55
|
+
const env = chunkVWKVU3SE_cjs.getEnvConfig();
|
|
51
56
|
const client = getS3Client();
|
|
52
57
|
await client.send(
|
|
53
58
|
new clientS3.DeleteObjectCommand({
|
|
@@ -57,7 +62,7 @@ var R2StorageProvider = class {
|
|
|
57
62
|
);
|
|
58
63
|
}
|
|
59
64
|
async list(prefix) {
|
|
60
|
-
const env =
|
|
65
|
+
const env = chunkVWKVU3SE_cjs.getEnvConfig();
|
|
61
66
|
const client = getS3Client();
|
|
62
67
|
const response = await client.send(
|
|
63
68
|
new clientS3.ListObjectsV2Command({
|
|
@@ -137,5 +142,5 @@ function getExtension(filename) {
|
|
|
137
142
|
|
|
138
143
|
exports.R2StorageProvider = R2StorageProvider;
|
|
139
144
|
exports.processImage = processImage;
|
|
140
|
-
//# sourceMappingURL=chunk-
|
|
141
|
-
//# sourceMappingURL=chunk-
|
|
145
|
+
//# sourceMappingURL=chunk-SUJT6LWH.cjs.map
|
|
146
|
+
//# sourceMappingURL=chunk-SUJT6LWH.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/storage.ts","../src/lib/image.ts"],"names":["isR2Configured","getEnvConfig","S3Client","randomUUID","PutObjectCommand","DeleteObjectCommand","ListObjectsV2Command"],"mappings":"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","file":"chunk-SUJT6LWH.cjs","sourcesContent":["import {\n S3Client,\n PutObjectCommand,\n DeleteObjectCommand,\n ListObjectsV2Command,\n} from '@aws-sdk/client-s3';\nimport { getEnvConfig, isR2Configured } from './config';\nimport { randomUUID } from 'crypto';\n\nexport interface MediaUploadResult {\n key: string;\n url: string;\n size: number;\n contentType: string;\n}\n\nexport interface StorageObject {\n key: string;\n size: number;\n lastModified: Date;\n}\n\nexport interface StorageProvider {\n upload(file: Buffer, filename: string, contentType: string): Promise<MediaUploadResult>;\n delete(key: string): Promise<void>;\n list(prefix?: string): Promise<StorageObject[]>;\n}\n\nlet clientInstance: S3Client | null = null;\n\nfunction getS3Client(): S3Client {\n if (clientInstance) return clientInstance;\n\n if (!isR2Configured()) {\n throw new Error(\n 'Cloudflare R2 is not configured. Set NEXTBLOGKIT_R2_ACCOUNT_ID, NEXTBLOGKIT_R2_ACCESS_KEY, NEXTBLOGKIT_R2_SECRET_KEY, NEXTBLOGKIT_R2_BUCKET, and NEXTBLOGKIT_R2_PUBLIC_URL environment variables to enable image uploads.'\n );\n }\n\n const env = getEnvConfig();\n clientInstance = new S3Client({\n region: 'auto',\n endpoint: `https://${env.NEXTBLOGKIT_R2_ACCOUNT_ID!}.r2.cloudflarestorage.com`,\n credentials: {\n accessKeyId: env.NEXTBLOGKIT_R2_ACCESS_KEY!,\n secretAccessKey: env.NEXTBLOGKIT_R2_SECRET_KEY!,\n },\n });\n\n return clientInstance;\n}\n\nfunction generateKey(filename: string): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const uuid = randomUUID().split('-')[0];\n const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, '-').toLowerCase();\n return `blog/${year}/${month}/${uuid}-${safeName}`;\n}\n\nexport class R2StorageProvider implements StorageProvider {\n async upload(\n file: Buffer,\n filename: string,\n contentType: string\n ): Promise<MediaUploadResult> {\n const env = getEnvConfig();\n const client = getS3Client();\n const key = generateKey(filename);\n\n await client.send(\n new PutObjectCommand({\n Bucket: env.NEXTBLOGKIT_R2_BUCKET,\n Key: key,\n Body: file,\n ContentType: contentType,\n })\n );\n\n return {\n key,\n url: `${env.NEXTBLOGKIT_R2_PUBLIC_URL}/${key}`,\n size: file.length,\n contentType,\n };\n }\n\n async delete(key: string): Promise<void> {\n const env = getEnvConfig();\n const client = getS3Client();\n\n await client.send(\n new DeleteObjectCommand({\n Bucket: env.NEXTBLOGKIT_R2_BUCKET,\n Key: key,\n })\n );\n }\n\n async list(prefix?: string): Promise<StorageObject[]> {\n const env = getEnvConfig();\n const client = getS3Client();\n\n const response = await client.send(\n new ListObjectsV2Command({\n Bucket: env.NEXTBLOGKIT_R2_BUCKET,\n Prefix: prefix || 'blog/',\n })\n );\n\n return (response.Contents || []).map((obj) => ({\n key: obj.Key || '',\n size: obj.Size || 0,\n lastModified: obj.LastModified || new Date(),\n }));\n }\n}\n","import type { R2StorageProvider, MediaUploadResult } from './storage';\n\nexport interface ProcessedImage {\n original: MediaUploadResult;\n width: number;\n height: number;\n format: string;\n}\n\nconst RESPONSIVE_SIZES = [640, 768, 1024, 1280, 1920];\n\nexport async function processImage(\n file: Buffer,\n filename: string,\n storage: R2StorageProvider\n): Promise<ProcessedImage> {\n let sharp: typeof import('sharp');\n try {\n sharp = (await import('sharp')).default;\n } catch {\n // sharp not available — upload raw file\n const result = await storage.upload(file, filename, getMimeType(filename));\n return {\n original: result,\n width: 0,\n height: 0,\n format: getExtension(filename),\n };\n }\n\n const image = sharp(file);\n const metadata = await image.metadata();\n\n // Convert to WebP\n const webpFilename = filename.replace(/\\.[^.]+$/, '.webp');\n const webpBuffer = await image.webp({ quality: 85 }).toBuffer();\n\n const original = await storage.upload(webpBuffer, webpFilename, 'image/webp');\n\n // Generate responsive sizes in background (non-blocking for the main upload)\n generateResponsiveSizes(file, webpFilename, storage, metadata.width || 0).catch(\n () => {\n // Silently fail responsive generation — originals are sufficient\n }\n );\n\n return {\n original,\n width: metadata.width || 0,\n height: metadata.height || 0,\n format: 'webp',\n };\n}\n\nasync function generateResponsiveSizes(\n file: Buffer,\n filename: string,\n storage: R2StorageProvider,\n originalWidth: number\n): Promise<void> {\n const sharp = (await import('sharp')).default;\n\n const sizes = RESPONSIVE_SIZES.filter((s) => s < originalWidth);\n\n await Promise.all(\n sizes.map(async (width) => {\n const resized = await sharp(file)\n .resize(width)\n .webp({ quality: 80 })\n .toBuffer();\n\n const sizedFilename = filename.replace('.webp', `-${width}w.webp`);\n await storage.upload(resized, sizedFilename, 'image/webp');\n })\n );\n\n // Generate thumbnail\n const thumb = await sharp(file)\n .resize(200, 200, { fit: 'cover' })\n .webp({ quality: 70 })\n .toBuffer();\n\n const thumbFilename = filename.replace('.webp', '-thumb.webp');\n await storage.upload(thumb, thumbFilename, 'image/webp');\n}\n\nfunction getMimeType(filename: string): string {\n const ext = getExtension(filename);\n const mimeTypes: Record<string, string> = {\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n svg: 'image/svg+xml',\n avif: 'image/avif',\n };\n return mimeTypes[ext] || 'application/octet-stream';\n}\n\nfunction getExtension(filename: string): string {\n return filename.split('.').pop()?.toLowerCase() || '';\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getEnvConfig } from './chunk-
|
|
1
|
+
import { getEnvConfig, getConfig } from './chunk-JI2RK6KX.js';
|
|
2
2
|
|
|
3
3
|
// src/lib/search.ts
|
|
4
4
|
async function searchPosts(collection, query, limit = 10) {
|
|
@@ -28,7 +28,8 @@ async function searchPosts(collection, query, limit = 10) {
|
|
|
28
28
|
// src/lib/seo.ts
|
|
29
29
|
function generateMetaTags(post) {
|
|
30
30
|
const env = getEnvConfig();
|
|
31
|
-
const
|
|
31
|
+
const config = getConfig();
|
|
32
|
+
const postUrl = `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/${post.slug}`;
|
|
32
33
|
const title = post.seo?.metaTitle || post.title;
|
|
33
34
|
const description = post.seo?.metaDescription || post.excerpt;
|
|
34
35
|
const canonical = post.seo?.canonicalUrl || postUrl;
|
|
@@ -69,7 +70,8 @@ function generateMetaTags(post) {
|
|
|
69
70
|
}
|
|
70
71
|
function generateStructuredData(post) {
|
|
71
72
|
const env = getEnvConfig();
|
|
72
|
-
const
|
|
73
|
+
const config = getConfig();
|
|
74
|
+
const postUrl = `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/${post.slug}`;
|
|
73
75
|
return {
|
|
74
76
|
"@context": "https://schema.org",
|
|
75
77
|
"@type": "BlogPosting",
|
|
@@ -112,6 +114,7 @@ function generateFAQStructuredData(faqItems) {
|
|
|
112
114
|
}
|
|
113
115
|
function generateBreadcrumbs(post, categoryName) {
|
|
114
116
|
const env = getEnvConfig();
|
|
117
|
+
const config = getConfig();
|
|
115
118
|
const items = [
|
|
116
119
|
{
|
|
117
120
|
"@type": "ListItem",
|
|
@@ -123,7 +126,7 @@ function generateBreadcrumbs(post, categoryName) {
|
|
|
123
126
|
"@type": "ListItem",
|
|
124
127
|
position: 2,
|
|
125
128
|
name: "Blog",
|
|
126
|
-
item: `${env.NEXTBLOGKIT_SITE_URL}
|
|
129
|
+
item: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}`
|
|
127
130
|
}
|
|
128
131
|
];
|
|
129
132
|
if (categoryName && post.categories[0]) {
|
|
@@ -131,7 +134,7 @@ function generateBreadcrumbs(post, categoryName) {
|
|
|
131
134
|
"@type": "ListItem",
|
|
132
135
|
position: 3,
|
|
133
136
|
name: categoryName,
|
|
134
|
-
item: `${env.NEXTBLOGKIT_SITE_URL}/
|
|
137
|
+
item: `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/category/${post.categories[0]}`
|
|
135
138
|
});
|
|
136
139
|
items.push({
|
|
137
140
|
"@type": "ListItem",
|
|
@@ -326,5 +329,5 @@ function calculateSEOScore(post) {
|
|
|
326
329
|
}
|
|
327
330
|
|
|
328
331
|
export { calculateSEOScore, generateBreadcrumbs, generateFAQStructuredData, generateMetaTags, generateStructuredData, searchPosts };
|
|
329
|
-
//# sourceMappingURL=chunk-
|
|
330
|
-
//# sourceMappingURL=chunk-
|
|
332
|
+
//# sourceMappingURL=chunk-TVHY4BR2.js.map
|
|
333
|
+
//# sourceMappingURL=chunk-TVHY4BR2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/search.ts","../src/lib/seo.ts","../src/lib/seo-scorer.ts"],"names":[],"mappings":"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","file":"chunk-TVHY4BR2.js","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, getConfig } 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 config = getConfig();\n const postUrl = `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/${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 config = getConfig();\n const postUrl = `${env.NEXTBLOGKIT_SITE_URL}${config.basePath}/${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 config = getConfig();\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}${config.basePath}`,\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}${config.basePath}/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"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getEnvConfig } from './chunk-
|
|
1
|
+
import { getEnvConfig } from './chunk-JI2RK6KX.js';
|
|
2
2
|
import { NextResponse } from 'next/server';
|
|
3
3
|
|
|
4
4
|
function jsonSuccess(data, meta, status = 200) {
|
|
@@ -10,7 +10,20 @@ function jsonError(code, message, status = 400) {
|
|
|
10
10
|
{ status }
|
|
11
11
|
);
|
|
12
12
|
}
|
|
13
|
-
function requireAuth(request) {
|
|
13
|
+
async function requireAuth(request) {
|
|
14
|
+
const env = getEnvConfig();
|
|
15
|
+
const authHeader = request.headers.get("authorization");
|
|
16
|
+
if (!authHeader) {
|
|
17
|
+
return jsonError("UNAUTHORIZED", "Authorization header is required", 401);
|
|
18
|
+
}
|
|
19
|
+
const token = authHeader.replace(/^Bearer\s+/i, "");
|
|
20
|
+
if (token === env.NEXTBLOGKIT_API_KEY) return null;
|
|
21
|
+
const { verifyApiToken } = await import('./db-OUSQPM53.js');
|
|
22
|
+
const dbToken = await verifyApiToken(token);
|
|
23
|
+
if (dbToken) return null;
|
|
24
|
+
return jsonError("FORBIDDEN", "Invalid API key", 403);
|
|
25
|
+
}
|
|
26
|
+
function requireMasterAuth(request) {
|
|
14
27
|
const env = getEnvConfig();
|
|
15
28
|
const authHeader = request.headers.get("authorization");
|
|
16
29
|
if (!authHeader) {
|
|
@@ -32,6 +45,6 @@ function parseIntParam(value, defaultValue) {
|
|
|
32
45
|
return isNaN(parsed) ? defaultValue : parsed;
|
|
33
46
|
}
|
|
34
47
|
|
|
35
|
-
export { getSearchParams, jsonError, jsonSuccess, parseIntParam, requireAuth };
|
|
36
|
-
//# sourceMappingURL=chunk-
|
|
37
|
-
//# sourceMappingURL=chunk-
|
|
48
|
+
export { getSearchParams, jsonError, jsonSuccess, parseIntParam, requireAuth, requireMasterAuth };
|
|
49
|
+
//# sourceMappingURL=chunk-UMIBGO4S.js.map
|
|
50
|
+
//# sourceMappingURL=chunk-UMIBGO4S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/middleware.ts"],"names":[],"mappings":";;;AAIO,SAAS,WAAA,CAAY,IAAA,EAAe,IAAA,EAAgC,MAAA,GAAS,GAAA,EAAK;AACvF,EAAA,OAAO,YAAA,CAAa,IAAA,CAAK,EAAE,OAAA,EAAS,IAAA,EAAM,MAAM,IAAA,EAAK,EAAG,EAAE,MAAA,EAAQ,CAAA;AACpE;AAEO,SAAS,SAAA,CAAU,IAAA,EAAc,OAAA,EAAiB,MAAA,GAAS,GAAA,EAAqC;AACrG,EAAA,OAAO,YAAA,CAAa,IAAA;AAAA,IAClB,EAAE,OAAA,EAAS,KAAA,EAAO,OAAO,EAAE,IAAA,EAAM,SAAQ,EAAE;AAAA,IAC3C,EAAE,MAAA;AAAO,GACX;AACF;AAEA,eAAsB,YAAY,OAAA,EAAkE;AAClG,EAAA,MAAM,MAAM,YAAA,EAAa;AACzB,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA;AAEtD,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,OAAO,SAAA,CAAU,cAAA,EAAgB,kCAAA,EAAoC,GAAG,CAAA;AAAA,EAC1E;AAEA,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,OAAA,CAAQ,aAAA,EAAe,EAAE,CAAA;AAGlD,EAAA,IAAI,KAAA,KAAU,GAAA,CAAI,mBAAA,EAAqB,OAAO,IAAA;AAG9C,EAAA,MAAM,EAAE,cAAA,EAAe,GAAI,MAAM,OAAO,kBAAW,CAAA;AACnD,EAAA,MAAM,OAAA,GAAU,MAAM,cAAA,CAAe,KAAK,CAAA;AAC1C,EAAA,IAAI,SAAS,OAAO,IAAA;AAEpB,EAAA,OAAO,SAAA,CAAU,WAAA,EAAa,iBAAA,EAAmB,GAAG,CAAA;AACtD;AAEO,SAAS,kBAAkB,OAAA,EAAyD;AACzF,EAAA,MAAM,MAAM,YAAA,EAAa;AACzB,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,eAAe,CAAA;AAEtD,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,OAAO,SAAA,CAAU,cAAA,EAAgB,kCAAA,EAAoC,GAAG,CAAA;AAAA,EAC1E;AAEA,EAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,OAAA,CAAQ,aAAA,EAAe,EAAE,CAAA;AAClD,EAAA,IAAI,KAAA,KAAU,IAAI,mBAAA,EAAqB;AACrC,IAAA,OAAO,SAAA,CAAU,WAAA,EAAa,iBAAA,EAAmB,GAAG,CAAA;AAAA,EACtD;AAEA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,gBAAgB,OAAA,EAAkB;AAChD,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,EAAA,OAAO,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,YAAA,CAAa,SAAS,CAAA;AACtD;AAEO,SAAS,aAAA,CAAc,OAA2B,YAAA,EAA8B;AACrF,EAAA,IAAI,CAAC,OAAO,OAAO,YAAA;AACnB,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,KAAA,EAAO,EAAE,CAAA;AACjC,EAAA,OAAO,KAAA,CAAM,MAAM,CAAA,GAAI,YAAA,GAAe,MAAA;AACxC","file":"chunk-UMIBGO4S.js","sourcesContent":["import { NextResponse } from 'next/server';\nimport { getEnvConfig } from '../lib/config';\nimport type { ApiErrorResponse } from '../lib/types';\n\nexport function jsonSuccess(data: unknown, meta?: Record<string, unknown>, status = 200) {\n return NextResponse.json({ success: true, data, meta }, { status });\n}\n\nexport function jsonError(code: string, message: string, status = 400): NextResponse<ApiErrorResponse> {\n return NextResponse.json(\n { success: false, error: { code, message } },\n { status }\n ) as NextResponse<ApiErrorResponse>;\n}\n\nexport async function requireAuth(request: Request): Promise<NextResponse<ApiErrorResponse> | null> {\n const env = getEnvConfig();\n const authHeader = request.headers.get('authorization');\n\n if (!authHeader) {\n return jsonError('UNAUTHORIZED', 'Authorization header is required', 401);\n }\n\n const token = authHeader.replace(/^Bearer\\s+/i, '');\n\n // Fast path: master key\n if (token === env.NEXTBLOGKIT_API_KEY) return null;\n\n // Check DB tokens\n const { verifyApiToken } = await import('../lib/db');\n const dbToken = await verifyApiToken(token);\n if (dbToken) return null;\n\n return jsonError('FORBIDDEN', 'Invalid API key', 403);\n}\n\nexport function requireMasterAuth(request: Request): NextResponse<ApiErrorResponse> | null {\n const env = getEnvConfig();\n const authHeader = request.headers.get('authorization');\n\n if (!authHeader) {\n return jsonError('UNAUTHORIZED', 'Authorization header is required', 401);\n }\n\n const token = authHeader.replace(/^Bearer\\s+/i, '');\n if (token !== env.NEXTBLOGKIT_API_KEY) {\n return jsonError('FORBIDDEN', 'Invalid API key', 403);\n }\n\n return null;\n}\n\nexport function getSearchParams(request: Request) {\n const url = new URL(request.url);\n return Object.fromEntries(url.searchParams.entries());\n}\n\nexport function parseIntParam(value: string | undefined, defaultValue: number): number {\n if (!value) return defaultValue;\n const parsed = parseInt(value, 10);\n return isNaN(parsed) ? defaultValue : parsed;\n}\n"]}
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var zod = require('zod');
|
|
4
3
|
var mongodb = require('mongodb');
|
|
4
|
+
var crypto = require('crypto');
|
|
5
|
+
var zod = require('zod');
|
|
5
6
|
|
|
6
|
-
// src/lib/
|
|
7
|
+
// src/lib/db.ts
|
|
7
8
|
var envSchema = zod.z.object({
|
|
9
|
+
// Required
|
|
8
10
|
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
11
|
NEXTBLOGKIT_API_KEY: zod.z.string().min(32, "API key must be at least 32 characters"),
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
// Optional — Database name (defaults to the database in your connection URI)
|
|
13
|
+
NEXTBLOGKIT_MONGODB_DB: zod.z.string().optional(),
|
|
14
|
+
// Optional — Cloudflare R2 (image storage)
|
|
15
|
+
NEXTBLOGKIT_R2_ACCOUNT_ID: zod.z.string().optional(),
|
|
16
|
+
NEXTBLOGKIT_R2_ACCESS_KEY: zod.z.string().optional(),
|
|
17
|
+
NEXTBLOGKIT_R2_SECRET_KEY: zod.z.string().optional(),
|
|
18
|
+
NEXTBLOGKIT_R2_BUCKET: zod.z.string().optional(),
|
|
19
|
+
NEXTBLOGKIT_R2_PUBLIC_URL: zod.z.string().optional(),
|
|
20
|
+
// Optional — Site info (defaults provided)
|
|
21
|
+
NEXTBLOGKIT_SITE_URL: zod.z.string().optional().default(""),
|
|
22
|
+
NEXTBLOGKIT_SITE_NAME: zod.z.string().optional().default("Blog")
|
|
17
23
|
});
|
|
18
24
|
var cachedEnv = null;
|
|
19
25
|
function getEnvConfig() {
|
|
@@ -27,6 +33,10 @@ ${missing.join("\n")}`);
|
|
|
27
33
|
cachedEnv = result.data;
|
|
28
34
|
return cachedEnv;
|
|
29
35
|
}
|
|
36
|
+
function isR2Configured() {
|
|
37
|
+
const env = getEnvConfig();
|
|
38
|
+
return !!(env.NEXTBLOGKIT_R2_ACCOUNT_ID && env.NEXTBLOGKIT_R2_ACCESS_KEY && env.NEXTBLOGKIT_R2_SECRET_KEY && env.NEXTBLOGKIT_R2_BUCKET && env.NEXTBLOGKIT_R2_PUBLIC_URL);
|
|
39
|
+
}
|
|
30
40
|
var defaultConfig = {
|
|
31
41
|
basePath: "/blog",
|
|
32
42
|
adminPath: "/admin/blog",
|
|
@@ -194,6 +204,8 @@ function extractTextFromBlocks(blocks) {
|
|
|
194
204
|
}
|
|
195
205
|
return parts.join(" ");
|
|
196
206
|
}
|
|
207
|
+
|
|
208
|
+
// src/lib/db.ts
|
|
197
209
|
var client = null;
|
|
198
210
|
var db = null;
|
|
199
211
|
async function getDb() {
|
|
@@ -201,7 +213,7 @@ async function getDb() {
|
|
|
201
213
|
const env = getEnvConfig();
|
|
202
214
|
client = new mongodb.MongoClient(env.NEXTBLOGKIT_MONGODB_URI);
|
|
203
215
|
await client.connect();
|
|
204
|
-
db = client.db();
|
|
216
|
+
db = client.db(env.NEXTBLOGKIT_MONGODB_DB || void 0);
|
|
205
217
|
return db;
|
|
206
218
|
}
|
|
207
219
|
async function getCollection(name) {
|
|
@@ -223,6 +235,9 @@ async function ensureIndexes() {
|
|
|
223
235
|
const media = database.collection("nbk_media");
|
|
224
236
|
await media.createIndex({ createdAt: -1 });
|
|
225
237
|
await media.createIndex({ r2Key: 1 }, { unique: true });
|
|
238
|
+
const tokens = database.collection("nbk_api_tokens");
|
|
239
|
+
await tokens.createIndex({ tokenHash: 1 }, { unique: true });
|
|
240
|
+
await tokens.createIndex({ createdAt: -1 });
|
|
226
241
|
}
|
|
227
242
|
async function createPost(input, defaultAuthor) {
|
|
228
243
|
const col = await getCollection("nbk_posts");
|
|
@@ -306,6 +321,11 @@ async function deletePost(id) {
|
|
|
306
321
|
);
|
|
307
322
|
return result.modifiedCount > 0;
|
|
308
323
|
}
|
|
324
|
+
async function hardDeletePost(id) {
|
|
325
|
+
const col = await getCollection("nbk_posts");
|
|
326
|
+
const result = await col.deleteOne({ _id: new mongodb.ObjectId(id) });
|
|
327
|
+
return result.deletedCount > 0;
|
|
328
|
+
}
|
|
309
329
|
async function getPostBySlug(slug) {
|
|
310
330
|
const col = await getCollection("nbk_posts");
|
|
311
331
|
return await col.findOne({ slug });
|
|
@@ -394,6 +414,15 @@ async function getCategoryBySlug(slug) {
|
|
|
394
414
|
const col = await getCollection("nbk_categories");
|
|
395
415
|
return await col.findOne({ slug });
|
|
396
416
|
}
|
|
417
|
+
async function updateCategoryPostCount(categorySlug) {
|
|
418
|
+
const posts = await getCollection("nbk_posts");
|
|
419
|
+
const categories = await getCollection("nbk_categories");
|
|
420
|
+
const count = await posts.countDocuments({
|
|
421
|
+
categories: categorySlug,
|
|
422
|
+
status: "published"
|
|
423
|
+
});
|
|
424
|
+
await categories.updateOne({ slug: categorySlug }, { $set: { postCount: count } });
|
|
425
|
+
}
|
|
397
426
|
async function createMedia(data) {
|
|
398
427
|
const col = await getCollection("nbk_media");
|
|
399
428
|
const result = await col.insertOne(data);
|
|
@@ -442,13 +471,53 @@ async function updateSettings(data) {
|
|
|
442
471
|
);
|
|
443
472
|
return getSettings();
|
|
444
473
|
}
|
|
474
|
+
function hashToken(plain) {
|
|
475
|
+
return crypto.createHash("sha256").update(plain).digest("hex");
|
|
476
|
+
}
|
|
477
|
+
async function createApiToken(name) {
|
|
478
|
+
const col = await getCollection("nbk_api_tokens");
|
|
479
|
+
const plainToken = "nbk_" + crypto.randomBytes(24).toString("hex");
|
|
480
|
+
const tokenHash = hashToken(plainToken);
|
|
481
|
+
const prefix = plainToken.slice(0, 8);
|
|
482
|
+
const now = /* @__PURE__ */ new Date();
|
|
483
|
+
const doc = {
|
|
484
|
+
name,
|
|
485
|
+
tokenHash,
|
|
486
|
+
prefix,
|
|
487
|
+
createdAt: now
|
|
488
|
+
};
|
|
489
|
+
const result = await col.insertOne(doc);
|
|
490
|
+
const token = { _id: result.insertedId, ...doc };
|
|
491
|
+
return { token, plainToken };
|
|
492
|
+
}
|
|
493
|
+
async function listApiTokens() {
|
|
494
|
+
const col = await getCollection("nbk_api_tokens");
|
|
495
|
+
return await col.find({}, { projection: { tokenHash: 0 } }).sort({ createdAt: -1 }).toArray();
|
|
496
|
+
}
|
|
497
|
+
async function deleteApiToken(id) {
|
|
498
|
+
const col = await getCollection("nbk_api_tokens");
|
|
499
|
+
const result = await col.deleteOne({ _id: new mongodb.ObjectId(id) });
|
|
500
|
+
return result.deletedCount > 0;
|
|
501
|
+
}
|
|
502
|
+
async function verifyApiToken(plainToken) {
|
|
503
|
+
const col = await getCollection("nbk_api_tokens");
|
|
504
|
+
const tokenHash = hashToken(plainToken);
|
|
505
|
+
const token = await col.findOneAndUpdate(
|
|
506
|
+
{ tokenHash },
|
|
507
|
+
{ $set: { lastUsedAt: /* @__PURE__ */ new Date() } },
|
|
508
|
+
{ returnDocument: "after" }
|
|
509
|
+
);
|
|
510
|
+
return token;
|
|
511
|
+
}
|
|
445
512
|
|
|
446
513
|
exports.calculateReadingTime = calculateReadingTime;
|
|
447
514
|
exports.countWords = countWords;
|
|
515
|
+
exports.createApiToken = createApiToken;
|
|
448
516
|
exports.createCategory = createCategory;
|
|
449
517
|
exports.createMedia = createMedia;
|
|
450
518
|
exports.createPost = createPost;
|
|
451
519
|
exports.defineConfig = defineConfig;
|
|
520
|
+
exports.deleteApiToken = deleteApiToken;
|
|
452
521
|
exports.deleteCategory = deleteCategory;
|
|
453
522
|
exports.deleteMedia = deleteMedia;
|
|
454
523
|
exports.deletePost = deletePost;
|
|
@@ -466,11 +535,16 @@ exports.getEnvConfig = getEnvConfig;
|
|
|
466
535
|
exports.getPostById = getPostById;
|
|
467
536
|
exports.getPostBySlug = getPostBySlug;
|
|
468
537
|
exports.getSettings = getSettings;
|
|
538
|
+
exports.hardDeletePost = hardDeletePost;
|
|
539
|
+
exports.isR2Configured = isR2Configured;
|
|
540
|
+
exports.listApiTokens = listApiTokens;
|
|
469
541
|
exports.listCategories = listCategories;
|
|
470
542
|
exports.listMedia = listMedia;
|
|
471
543
|
exports.listPosts = listPosts;
|
|
472
544
|
exports.updateCategory = updateCategory;
|
|
545
|
+
exports.updateCategoryPostCount = updateCategoryPostCount;
|
|
473
546
|
exports.updatePost = updatePost;
|
|
474
547
|
exports.updateSettings = updateSettings;
|
|
475
|
-
|
|
476
|
-
//# sourceMappingURL=chunk-
|
|
548
|
+
exports.verifyApiToken = verifyApiToken;
|
|
549
|
+
//# sourceMappingURL=chunk-VWKVU3SE.cjs.map
|
|
550
|
+
//# sourceMappingURL=chunk-VWKVU3SE.cjs.map
|