0041
This commit is contained in:
1 parent
ed14361e7b
commit
d664326648
8 files changed
+39
-14
No files matched your search
@@ -29,6 +29,7 @@ services:
|
||||
- PORT=8016
|
||||
- BLOG_ROOT=/blog
|
||||
- TZ=Asia/Shanghai
|
||||
- HUGO_PREVIEW=http://127.0.0.1:1313
|
||||
networks:
|
||||
- write-network
|
||||
healthcheck:
|
||||
|
||||
@@ -25,6 +25,7 @@ fi
|
||||
# 配置 git(容器内 nextjs 用户)
|
||||
su-exec nextjs git config --global user.name "Write Server" 2>/dev/null
|
||||
su-exec nextjs git config --global user.email "write@usj.cc" 2>/dev/null
|
||||
su-exec nextjs git config --global --add safe.directory /blog 2>/dev/null
|
||||
|
||||
# 以 nextjs 用户启动应用
|
||||
exec su-exec nextjs "$@"
|
||||
@@ -4,7 +4,6 @@ import { useState, useEffect, use } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ArrowLeft, Eye } from "lucide-react";
|
||||
import PostEditor from "@/components/PostEditor";
|
||||
import { HUGO_PREVIEW } from "@/lib/config";
|
||||
import type { Post } from "@/lib/posts";
|
||||
|
||||
export default function EditPostPage({
|
||||
@@ -74,7 +73,8 @@ export default function EditPostPage({
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
await fetch("/api/hugo");
|
||||
window.open(`${HUGO_PREVIEW}/${post.frontMatter.slug}.html`, "_blank");
|
||||
const previewBase = `${location.protocol}//${location.hostname}:1313`;
|
||||
window.open(`${previewBase}/${post.frontMatter.slug}.html`, "_blank");
|
||||
}}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-[12px] rounded-md border border-line text-muted hover:text-accent hover:border-accent/30 transition-colors"
|
||||
title="预览"
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
FileText as PostIcon, Rss, Link2, GripVertical, X, CheckSquare, Square,
|
||||
} from "lucide-react";
|
||||
import Pagination from "@/components/Pagination";
|
||||
import { HUGO_PREVIEW } from "@/lib/config";
|
||||
|
||||
interface PostItem {
|
||||
slug: string;
|
||||
@@ -64,13 +63,23 @@ export default function HomePage() {
|
||||
|
||||
const handleDelete = async (slug: string, title: string) => {
|
||||
if (!confirm(`确定删除「${title}」吗?`)) return;
|
||||
await fetch(`/api/posts/${slug}`, { method: "DELETE" });
|
||||
fetchPosts();
|
||||
try {
|
||||
const res = await fetch(`/api/posts/${slug}`, { method: "DELETE" });
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
alert("删除失败:" + (data.error || "未知错误"));
|
||||
return;
|
||||
}
|
||||
fetchPosts();
|
||||
} catch (e) {
|
||||
alert("删除失败:" + (e instanceof Error ? e.message : String(e)));
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreview = async (slug: string) => {
|
||||
await fetch("/api/hugo");
|
||||
window.open(`${HUGO_PREVIEW}/${slug}.html`, "_blank");
|
||||
const previewBase = `${location.protocol}//${location.hostname}:1313`;
|
||||
window.open(`${previewBase}/${slug}.html`, "_blank");
|
||||
};
|
||||
|
||||
const toggleSelect = (slug: string) => {
|
||||
|
||||
@@ -23,7 +23,9 @@ function computeDirPath(title: string, date?: string): string {
|
||||
.replace(/[^\w一-鿿㐀-䶿]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.toLowerCase() || "post";
|
||||
return path.join(CONTENT_DIR, year, `${dateStr}-${titlePart}`);
|
||||
// 加时间戳避免同标题同天冲突
|
||||
const ts = Date.now().toString(36);
|
||||
return path.join(CONTENT_DIR, year, `${dateStr}-${titlePart}-${ts}`);
|
||||
}
|
||||
|
||||
const sessions = new Map<number, Session>();
|
||||
|
||||
@@ -15,7 +15,7 @@ export const ARTALK_SERVER = process.env.ARTALK_SERVER || "https://artalk.usj.cc
|
||||
export const ARTALK_SITE = process.env.ARTALK_SITE || "优世界";
|
||||
export const ITEMS_PER_PAGE = parseInt(process.env.ITEMS_PER_PAGE || "20", 10);
|
||||
export const RECYCLE_DAYS = parseInt(process.env.RECYCLE_DAYS || "30", 10);
|
||||
export const HUGO_PREVIEW = process.env.HUGO_PREVIEW || "http://localhost:1313";
|
||||
export const HUGO_PREVIEW = process.env.NEXT_PUBLIC_HUGO_PREVIEW || process.env.HUGO_PREVIEW || "http://localhost:1313";
|
||||
export const RSS_API_BASE = process.env.RSS_API_BASE || "https://api.usj.cc";
|
||||
export const RSS_API_TOKEN = process.env.RSS_API_TOKEN || "";
|
||||
export const WECHAT_APP_ID = process.env.WECHAT_APP_ID || "";
|
||||
|
||||
@@ -155,12 +155,13 @@ export function createPost(frontMatter: PostFrontMatter, content: string): strin
|
||||
const date = new Date(frontMatter.date || new Date());
|
||||
const year = date.getFullYear().toString();
|
||||
const dateStr = date.toISOString().slice(0, 10);
|
||||
// Folder name: date + sanitized title (human-readable on disk)
|
||||
const titlePart = (frontMatter.title || "untitled")
|
||||
.replace(/[^\w一-鿿㐀-䶿]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.toLowerCase() || "post";
|
||||
const dirName = `${dateStr}-${titlePart}`;
|
||||
// 用 slug 或时间戳确保目录唯一
|
||||
const uniquePart = frontMatter.slug || Date.now().toString(36);
|
||||
const dirName = `${dateStr}-${titlePart}-${uniquePart}`;
|
||||
const dirPath = path.join(CONTENT_DIR, year, dirName);
|
||||
|
||||
if (fs.existsSync(path.join(dirPath, "index.md"))) {
|
||||
@@ -206,7 +207,7 @@ export function updatePost(
|
||||
.replace(/[^\w一-鿿㐀-䶿]+/g, "-")
|
||||
.replace(/^-+|-+$/g, "")
|
||||
.toLowerCase() || "post";
|
||||
const newDirName = `${newDateStr}-${titlePart}`;
|
||||
const newDirName = `${newDateStr}-${titlePart}-${frontMatter.slug}`;
|
||||
const newDirPath = path.join(CONTENT_DIR, newYear, newDirName);
|
||||
|
||||
if (fs.existsSync(newDirPath) && newDirPath !== post.dirPath) {
|
||||
|
||||
@@ -47,12 +47,18 @@ export function moveToRecycle(
|
||||
expiresAt: expires.toISOString(),
|
||||
};
|
||||
|
||||
// Move post directory to recycle
|
||||
// Move post directory to recycle (fallback to copy+delete for cross-filesystem)
|
||||
const targetDir = path.join(RECYCLE_POSTS_DIR, slug);
|
||||
if (fs.existsSync(targetDir)) {
|
||||
fs.rmSync(targetDir, { recursive: true, force: true });
|
||||
}
|
||||
fs.renameSync(sourceDir, targetDir);
|
||||
try {
|
||||
fs.renameSync(sourceDir, targetDir);
|
||||
} catch (_e) {
|
||||
// Cross-filesystem: copy then delete
|
||||
fs.cpSync(sourceDir, targetDir, { recursive: true });
|
||||
fs.rmSync(sourceDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
// Save metadata
|
||||
const metaPath = path.join(RECYCLE_META_DIR, `${slug}.json`);
|
||||
@@ -110,7 +116,12 @@ export function restoreFromRecycle(slug: string): boolean {
|
||||
fs.rmSync(meta.originalPath, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
fs.renameSync(recyclePostPath, meta.originalPath);
|
||||
try {
|
||||
fs.renameSync(recyclePostPath, meta.originalPath);
|
||||
} catch (_e) {
|
||||
fs.cpSync(recyclePostPath, meta.originalPath, { recursive: true });
|
||||
fs.rmSync(recyclePostPath, { recursive: true, force: true });
|
||||
}
|
||||
fs.unlinkSync(metaPath);
|
||||
|
||||
return true;
|
||||
|
||||
Reference in new issue
Block a user