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;
中文路径删除实测通过(目录 + 中文子目录 + 图片全部移入回收站,进程不崩)。
53 lines
2.3 KiB
YAML
53 lines
2.3 KiB
YAML
# editor-api —— 在线编辑文章的轻量后端
|
||
#
|
||
# 用法(在服务器上):
|
||
# 1. 把博客仓库 checkout 到 /srv/blog(git 工作区,分支 main)
|
||
# 2. 在同目录放一个 .env,写入 EDITOR_TOKEN=openssl rand -hex 32 生成的值
|
||
# 3. docker compose -f docker-compose.editor.yml up -d --build
|
||
# (回收目录会落在 /srv/editor-trash,记得先 mkdir -p /srv/editor-trash)
|
||
# 4. nginx 加 /editor-api/ 反代(见 部署清单.md),Worker 配 EDITOR_API_BASE / EDITOR_TOKEN
|
||
#
|
||
# ★ 端口只绑 127.0.0.1 —— 公网扫不到 8017,只有同机的 nginx 能转发进来。
|
||
# 这是整个安全模型的一半;另一半是「令牌只存在 Worker secret 里,浏览器拿不到」。
|
||
|
||
services:
|
||
editor-api:
|
||
build: ./editor-api
|
||
container_name: editor-api
|
||
restart: unless-stopped
|
||
ports:
|
||
- "127.0.0.1:8017:8017"
|
||
environment:
|
||
# 令牌:必须显式给,没给容器会直接退出(server.mjs 里故意的)
|
||
EDITOR_TOKEN: ${EDITOR_TOKEN:?请在同目录 .env 里设置 EDITOR_TOKEN}
|
||
BLOG_ROOT: /srv/blog
|
||
# ★ 回收站与仓库必须在**同一个挂载点**内,否则 rename 会 EXDEV
|
||
# (见下方 volumes 注释)。所以这里用宿主机路径,不再映射到 /app/trash。
|
||
TRASH_DIR: /srv/editor-trash
|
||
# 容器内监听 0.0.0.0 没关系 —— 外面那层端口映射已经把它锁在宿主机回环上了
|
||
BIND_HOST: 0.0.0.0
|
||
GIT_BRANCH: main
|
||
PUSH_REMOTES: origin,gh
|
||
GIT_AUTHOR_NAME: blog-editor
|
||
GIT_AUTHOR_EMAIL: editor@usj.cc
|
||
MAX_UPLOAD_MB: "20"
|
||
GIT_PATHS: content,static
|
||
volumes:
|
||
# ★ 只挂**一个**父目录:仓库和回收站都在它下面。
|
||
# 容器内路径 == 宿主机路径,两者同属一个挂载点,
|
||
# 删文章时 rename(2) 才能原子完成。
|
||
# 若分开挂成 /blog 和 /app/trash 两个 bind mount,rename 必报
|
||
# EXDEV: cross-device link not permitted(哪怕宿主机同一块盘)。
|
||
- /srv:/srv
|
||
healthcheck:
|
||
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8017/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
|
||
interval: 30s
|
||
timeout: 5s
|
||
retries: 3
|
||
start_period: 10s
|
||
logging:
|
||
driver: json-file
|
||
options:
|
||
max-size: "10m"
|
||
max-file: "3"
|