fix(editor): 删文章 EXDEV 修正 + 订阅口令不再阻断登录
1) 删除文章报 EXDEV(cross-device link not permitted)
· 根因一:容器内 /blog 与 /app/trash 是两个独立 bind mount,
rename(2) 跨挂载点必然 EXDEV —— 哪怕宿主机上它们同在一块盘。
· 根因二:退化成 fs.cpSync 后,Windows 上遇到**中文目录名**会把
Node 进程直接干崩(exit 127,不抛异常,stdout 缓冲丢失)。
而文章目录名几乎必然含中文(<日期>-<中文标题>-<slug>)。
· 修法:改用 readdirSync/copyFileSync 自己递归复制(宽字符路径,中文 OK);
bootstrap.sh / compose 改成挂 BLOG_DIR 的父目录,让仓库与回收站
落在**同一个挂载点**内,正常情况下 rename 直接成功、不再复制。
2) 登录页「订阅口令」填错会阻断整个登录
· 它只是可选订阅模块的口令,不该 throw 掉登录流程。
· rssApi() 无条件 searchParams.set('token', RSS_TOKEN),会用浏览器里
记住的旧口令覆盖调用方刚传的值 → 正确口令也验不过。
· 现在:填错只 toast 提示 + 清掉失效旧口令;rssApi 尊重调用方传的值。
3) wrangler.toml:EDITOR_API_BASE 改指国内机新域名 writeapi.usj.cc
(原指向即将到期的境外机 post.usj.cc,且那台从未部署过 editor-api)
验证:api-e2e 44/44;front matter 往返 130/130;保存往返 130/130;
中文路径删除实测通过(目录 + 中文子目录 + 图片全部移入回收站,进程不崩)。
This commit is contained in:
1 parent
e8d13b9a6e
commit
96751b8143
5 files changed
+98
-18
No files matched your search
@@ -292,6 +292,49 @@ export function makePosts(cfg) {
|
||||
}
|
||||
|
||||
/** 删除 = 移到仓库外的回收目录(不进 git,也不误伤版本历史) */
|
||||
/**
|
||||
* 递归复制目录树。
|
||||
*
|
||||
* ★ 为什么不用 fs.cpSync:它在 Windows 上遇到**非 ASCII 目录名**会直接把 Node
|
||||
* 进程干崩(实测 exit 127,连异常都不抛,日志停在调用前一行,stdout 缓冲丢失)。
|
||||
* 而文章目录名几乎必然含中文(`<日期>-<中文标题>-<slug>`),所以自己走一遍 ——
|
||||
* readdirSync / copyFileSync / mkdirSync 用的是宽字符路径,中文没问题。
|
||||
*/
|
||||
function copyTree(src, dst) {
|
||||
const st = fs.lstatSync(src);
|
||||
if (st.isDirectory()) {
|
||||
fs.mkdirSync(dst, { recursive: true });
|
||||
for (const name of fs.readdirSync(src)) {
|
||||
copyTree(path.join(src, name), path.join(dst, name));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (st.isSymbolicLink()) {
|
||||
fs.symlinkSync(fs.readlinkSync(src), dst);
|
||||
return;
|
||||
}
|
||||
fs.copyFileSync(src, dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把文章目录挪进回收站。
|
||||
*
|
||||
* ★ 不能直接用 rename:仓库挂载点(容器内 /blog)和回收站挂载点(/app/trash)
|
||||
* 是**两个独立的 bind mount**,rename(2) 跨挂载点必然返回 EXDEV
|
||||
* —— 哪怕在宿主机上它们同在一块盘上。
|
||||
* (bootstrap.sh 已改成把两者放进同一个挂载点,正常情况下走的是 rename 分支,
|
||||
* 这里的复制分支只是兜底,比如有人手动改了挂载方式。)
|
||||
*/
|
||||
function moveToTrash(src, dst) {
|
||||
try {
|
||||
fs.renameSync(src, dst);
|
||||
} catch (e) {
|
||||
if (e.code !== 'EXDEV') throw e;
|
||||
copyTree(src, dst);
|
||||
fs.rmSync(src, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
function deletePost(key) {
|
||||
const post = getPost(key);
|
||||
if (!post) return null;
|
||||
@@ -299,7 +342,7 @@ export function makePosts(cfg) {
|
||||
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const dest = path.join(trashDir, stamp + '-' + path.basename(dir));
|
||||
fs.mkdirSync(trashDir, { recursive: true });
|
||||
fs.renameSync(dir, dest);
|
||||
moveToTrash(dir, dest);
|
||||
return { id: post.dirName, slug: post.slug, movedTo: dest };
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user