diff --git a/package-lock.json b/package-lock.json index 8b8b818..e4dae28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -585,6 +585,66 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@next/swc-linux-x64-gnu": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", @@ -617,6 +677,51 @@ "node": ">= 10" } }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -7613,111 +7718,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", - "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", - "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", - "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", - "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", - "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", - "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", - "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } diff --git a/src/app/admin/manage/page.tsx b/src/app/admin/manage/page.tsx index d93228c..2d0e431 100644 --- a/src/app/admin/manage/page.tsx +++ b/src/app/admin/manage/page.tsx @@ -31,6 +31,8 @@ export default function ManagePage() { show: false, item: null, }); + const [draggedPost, setDraggedPost] = useState(null); + const [dragOverFolder, setDragOverFolder] = useState(null); const router = useRouter(); useEffect(() => { @@ -95,8 +97,14 @@ export default function ManagePage() { if (!deleteConfirm.item) return; try { - const itemPath = currentPath.join('/'); - const itemName = deleteConfirm.item.type === 'folder' ? deleteConfirm.item.name : deleteConfirm.item.slug; + let itemPath = currentPath.join('/'); + let itemName = deleteConfirm.item.type === 'folder' ? deleteConfirm.item.name : deleteConfirm.item.slug; + // For posts, if slug contains a path (e.g. subfolder/post), split it + if (deleteConfirm.item.type === 'post' && itemName.includes('/')) { + const parts = itemName.split('/'); + itemName = parts[parts.length - 1]; + itemPath = parts.slice(0, -1).join('/'); + } console.log('Deleting item:', { path: itemPath, @@ -131,6 +139,29 @@ export default function ManagePage() { } }; + // Move post API call + const movePost = async (post: Post, targetFolder: string[]) => { + try { + const response = await fetch('/api/admin/posts/move', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + slug: post.slug, + from: currentPath.join('/'), + to: targetFolder.join('/'), + }), + }); + if (!response.ok) { + const data = await response.json(); + alert(data.error || 'Error moving post'); + } else { + loadContent(); + } + } catch (error) { + alert('Error moving post'); + } + }; + if (!isAuthenticated) { return null; // Will redirect in useEffect } @@ -201,39 +232,79 @@ export default function ManagePage() { {/* Content List */}
{currentNodes.map((node) => ( -
-
-
-

- {node.type === 'folder' ? node.name : node.title} -

- {node.type === 'post' && ( -

{node.date}

- )} -
- + + + + +
-
+ ) : ( +
setDraggedPost(node)} + onDragEnd={() => setDraggedPost(null)} + > +
+
+

{node.title}

+

{node.date}

+
+ +
+
+ ) ))} diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index fa5c369..e2eac5a 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -572,6 +572,11 @@ export default function AdminPage() { + {/* Show current folder path above post creation form */} +
+ Current folder: {currentPath.join('/') || 'root'} +
+ {/* Create Folder Form */}

Create New Folder

diff --git a/src/app/api/admin/posts/move/route.ts b/src/app/api/admin/posts/move/route.ts new file mode 100644 index 0000000..4e5aed5 --- /dev/null +++ b/src/app/api/admin/posts/move/route.ts @@ -0,0 +1,35 @@ +import { NextResponse } from 'next/server'; +import fs from 'fs'; +import path from 'path'; + +const postsDirectory = path.join(process.cwd(), 'posts'); + +export async function POST(request: Request) { + try { + const body = await request.json(); + const { slug, from, to } = body; + if (!slug) { + return NextResponse.json({ error: 'Missing slug' }, { status: 400 }); + } + // Compute source and destination paths + const fromPath = from && from.trim() !== '' + ? path.join(postsDirectory, from, `${slug}.md`) + : path.join(postsDirectory, `${slug}.md`); + const toPath = to && to.trim() !== '' + ? path.join(postsDirectory, to, `${slug}.md`) + : path.join(postsDirectory, `${slug}.md`); + + // Ensure source exists + if (!fs.existsSync(fromPath)) { + return NextResponse.json({ error: 'Source post does not exist' }, { status: 404 }); + } + // Ensure destination directory exists + fs.mkdirSync(path.dirname(toPath), { recursive: true }); + // Move the file + fs.renameSync(fromPath, toPath); + return NextResponse.json({ success: true }); + } catch (error) { + console.error('Error moving post:', error); + return NextResponse.json({ error: 'Error moving post' }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/admin/posts/route.ts b/src/app/api/admin/posts/route.ts index 156e908..b08eea0 100644 --- a/src/app/api/admin/posts/route.ts +++ b/src/app/api/admin/posts/route.ts @@ -8,7 +8,7 @@ const postsDirectory = path.join(process.cwd(), 'posts'); export async function POST(request: Request) { try { const body = await request.json(); - const { title, date, tags, summary, content } = body; + const { title, date, tags, summary, content, path: folderPath } = body; // Create slug from title const slug = title @@ -25,8 +25,15 @@ export async function POST(request: Request) { author: process.env.NEXT_PUBLIC_BLOG_OWNER + "'s" || 'Anonymous', }); - // Write the file - const filePath = path.join(postsDirectory, `${slug}.md`); + // Write the file in the correct folder if provided + let filePath; + if (folderPath && folderPath.trim() !== '') { + filePath = path.join(postsDirectory, folderPath, `${slug}.md`); + // Ensure the directory exists + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + } else { + filePath = path.join(postsDirectory, `${slug}.md`); + } fs.writeFileSync(filePath, frontmatter); return NextResponse.json({ success: true, slug });