This commit is contained in:
Vaica committed 2026-06-22 00:41:05 +08:00
1 parent 7b3173e89d
commit b8d16016d8
8 files changed
+39 -14

No files matched your search

+1
View File
@@ -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:
+1
View File
@@ -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 "$@"
+2 -2
View File
@@ -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="预览"
+13 -4
View File
@@ -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) => {
+3 -1
View File
@@ -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>();
+1 -1
View File
@@ -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 || "";
+4 -3
View File
@@ -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) {
+14 -3
View File
@@ -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;