Update package-lock.json with new SWC binaries for various platforms; enhance Admin and Manage pages with folder path display and drag-and-drop functionality for posts, including move post API integration.

This commit is contained in:
2025-06-18 22:57:12 +02:00
parent c4af151d6c
commit e7f20fe0e6
5 changed files with 259 additions and 141 deletions

210
package-lock.json generated
View File

@@ -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"
}
}
}
}

View File

@@ -31,6 +31,8 @@ export default function ManagePage() {
show: false,
item: null,
});
const [draggedPost, setDraggedPost] = useState<Node | null>(null);
const [dragOverFolder, setDragOverFolder] = useState<string | null>(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,18 +232,57 @@ export default function ManagePage() {
{/* Content List */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{currentNodes.map((node) => (
node.type === 'folder' ? (
<div
key={node.type === 'folder' ? node.name : node.slug}
className="bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow"
key={node.name}
className={`bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow cursor-pointer ${dragOverFolder === node.name ? 'ring-2 ring-blue-400' : ''}`}
onClick={() => setCurrentPath([...currentPath, node.name])}
onDragOver={e => { e.preventDefault(); setDragOverFolder(node.name); }}
onDragLeave={() => setDragOverFolder(null)}
onDrop={e => {
e.preventDefault();
setDragOverFolder(null);
if (draggedPost && draggedPost.type === 'post') {
movePost(draggedPost as Post, [...currentPath, node.name]);
}
}}
>
<div className="flex justify-between items-start">
<div>
<h3 className="font-bold">
{node.type === 'folder' ? node.name : node.title}
</h3>
{node.type === 'post' && (
<h3 className="font-bold">{node.name}</h3>
</div>
<button
onClick={e => { e.stopPropagation(); handleDelete(node); }}
className="text-red-600 hover:text-red-800 p-2"
title="Delete"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
</div>
) : (
<div
key={node.slug}
className="bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow"
draggable
onDragStart={() => setDraggedPost(node)}
onDragEnd={() => setDraggedPost(null)}
>
<div className="flex justify-between items-start">
<div>
<h3 className="font-bold">{node.title}</h3>
<p className="text-sm text-gray-600">{node.date}</p>
)}
</div>
<button
onClick={() => handleDelete(node)}
@@ -234,6 +304,7 @@ export default function ManagePage() {
</button>
</div>
</div>
)
))}
</div>

View File

@@ -572,6 +572,11 @@ export default function AdminPage() {
</div>
</div>
{/* Show current folder path above post creation form */}
<div className="mb-2 text-gray-500 text-sm">
Current folder: <span className="font-mono">{currentPath.join('/') || 'root'}</span>
</div>
{/* Create Folder Form */}
<div className="bg-white rounded-lg shadow p-6 mb-8">
<h2 className="text-2xl font-bold mb-4">Create New Folder</h2>

View File

@@ -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 });
}
}

View File

@@ -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 });