用 JSON:API + GitHub Actions 自动发送 Drupal 博客文章
目标:在 Drupal 10 上用 JSON:API 发文章,并在个人仓库加一个 GitHub Actions,把本地或仓库里的 Markdown 自动转换为 HTML、直接发布成文章(不启用内容审核流时可一键发布)。
1)Drupal 侧:创建 API 账户、开权限、开启写入
1.1 创建专用 API 账户
- 后台 → 人员 → 添加用户(建议命名如
api_poster,仅用于发文)。 - 给它分配一个最小权限的角色(可以新建“API Poster”角色)。
1.2 给角色勾选必要权限(以内容类型 Article 为例)
- 后台 → 人员 → 角色 → 编辑该角色:
- ✅ Article → 创建新内容(Create new content)
- ✅ (可选)Article → 编辑自己的内容
- ✅ 使用 Basic HTML 文本格式(Use the Basic HTML text format)
如果不想开放 HTML,可改用plain_text,并在后面工作流里把format改为plain_text。
1.3 启用模块:JSON:API 与 Basic Auth
- 扩展(/admin/modules) 勾选:
JSON:APIHTTP Basic Authentication(模块名basic_auth)
- 或服务器执行:
drush en jsonapi basic_auth -y
drush cr
1.4 允许 JSON:API 写入(关闭只读)
- 配置 → Web 服务 → JSON:API(/admin/config/services/jsonapi)
取消勾选 “Accept only read operations”。 - 或执行:
drush cset jsonapi.settings read_only 0 -y
drush cr
1.5 本机用 curl 快速连通测试(可选)
# 创建草稿
curl -X POST "https://你的域名/jsonapi/node/article" \
-u "api_poster:密码" \
-H "Content-Type: application/vnd.api+json" \
--data '{
"data": {
"type": "node--article",
"attributes": {
"title": "自动发送测试 - 本机",
"body": { "value": "<p>正文</p>", "format": "basic_html" },
"status": false
}
}
}'
# 发布(把上一步响应里的 UUID 填入)
curl -X PATCH "https://你的域名/jsonapi/node/article/<UUID>" \
-u "api_poster:密码" \
-H "Content-Type: application/vnd.api+json" \
--data '{ "data": { "type": "node--article", "id": "<UUID>", "attributes": { "status": true } } }'
备注:未启用内容审核流时,POST 时把 "status": true 就会直接发布。
2)Git & GitHub:把站点纳入私有仓库,并准备 Actions
内容与代码分离:文章保存在数据库,不进 Git;Git 只管理你的“脚本/工作流与记录文件”。
2.1 在 GitHub 新建私有仓库
- New repository → Private;保持空仓库(不自动生成 README)。
2.2 服务器站点目录初始化 Git 并推送
# 站点目录(以 /www/wwwroot/updatebao.com 为例)
cd /www/wwwroot/updatebao.com
git init
git branch -m main
# 写一个 .gitignore(避免上传目录、settings.php、缓存等入库)
cat > .gitignore <<'EOF'
sites/*/files/
sites/*/private/
sites/*/settings.php
sites/*/services.yml
sites/*/files/php/twig/
sites/*/files/styles/
sites/*/files/js/
sites/*/files/css/
core/assets/
vendor/
*.log
tmp/
cache/
.env
.env.*
.DS_Store
Thumbs.db
.idea/
.vscode/
EOF
所有权一致(如果站点属主是 www:www,确保 .git 也归它):
chown -R www:www /www/wwwroot/updatebao.com/.git
以站点用户提交并推送(推荐 SSH deploy key):
# 设置身份(仅对这个仓库)
sudo -u www git -C /www/wwwroot/updatebao.com config user.name "updatebao"
sudo -u www git -C /www/wwwroot/updatebao.com config user.email "you@example.com"
sudo -u www git -C /www/wwwroot/updatebao.com add .
sudo -u www git -C /www/wwwroot/updatebao.com commit -m "chore: initial import"
# GitHub 仓库 → Settings → Deploy keys → Add,勾“Allow write access”
# 服务器生成并添加公钥
sudo -u www -H bash -c 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && ssh-keygen -t ed25519 -C "server:updatebao" -N "" -f ~/.ssh/id_ed25519'
sudo -u www cat /home/www/.ssh/id_ed25519.pub # 复制到 Deploy key
# 绑定远程并推送
sudo -u www git -C /www/wwwroot/updatebao.com remote add origin git@github.com:<你的用户名>/<仓库名>.git
sudo -u www git -C /www/wwwroot/updatebao.com push -u origin main
Web 服务器配置中建议禁止访问 .git:location ~ /\.git { return 404; }
2.3 在你本地克隆这个仓库(写文档与工作流)
cd D:\work\my-blog
git clone https://github.com/<你的用户名>/<仓库名>.git .
2.4 添加 GitHub Actions:把 Markdown 自动发到 Drupal
在仓库创建工作流文件:/.github/workflows/post-to-drupal.yml
name: Post summary to Drupal
on:
workflow_dispatch:
inputs:
note_path:
description: '总结文件路径(如 notes/today.md 或 notes/2025-10-07.html)'
required: true
default: 'notes/today.md'
title:
description: '文章标题(留空自动用文件名)'
required: false
publish:
description: '是否立即发布'
type: boolean
default: true
push:
paths:
- 'notes/**/*.md'
- 'notes/**/*.html'
- 'notes/*.md'
- 'notes/*.html'
branches: [ main ]
jobs:
post:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Decide note path
id: resolve
shell: bash
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "note_path=${{ inputs.note_path }}" >> "$GITHUB_OUTPUT"
else
NOTE_PATH="$(git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -E '^notes/.*\.(md|html)$' | head -n 1)"
[[ -n "$NOTE_PATH" && -f "$NOTE_PATH" ]] || { echo "找不到要发布的文件"; exit 1; }
echo "note_path=$NOTE_PATH" >> "$GITHUB_OUTPUT"
fi
- name: Decide title
id: title
shell: bash
run: |
if [[ -n "${{ inputs.title }}" ]]; then
T="${{ inputs.title }}"
else
F="${{ steps.resolve.outputs.note_path }}"
B="$(basename "$F")"
T="${B%.*}"
fi
echo "title=$T" >> "$GITHUB_OUTPUT"
- name: Install tools
shell: bash
run: |
sudo apt-get update -y >/dev/null
sudo apt-get install -y jq python3 python3-pip >/dev/null
python3 -m pip install --quiet markdown
- name: Convert body to HTML (no heredoc)
id: body
shell: bash
env:
NOTE_PATH: ${{ steps.resolve.outputs.note_path }}
run: |
set -e
if [[ "$NOTE_PATH" == *.md ]]; then
python3 -c "import os, pathlib, markdown; p=pathlib.Path(os.environ['NOTE_PATH']); html=markdown.markdown(p.read_text(encoding='utf-8'), extensions=['extra','toc','sane_lists']); pathlib.Path('body.html').write_text(html, encoding='utf-8')"
else
cp "$NOTE_PATH" body.html
fi
echo "body=$(jq -Rs . < body.html)" >> $GITHUB_OUTPUT
- name: Create node via JSON:API
id: create
env:
BASE: ${{ secrets.DRUPAL_BASE }}
USER: ${{ secrets.DRUPAL_USER }}
PASS: ${{ secrets.DRUPAL_PASS }}
TITLE: ${{ steps.title.outputs.title }}
BODY: ${{ steps.body.outputs.body }}
EVENT: ${{ github.event_name }}
PUBLISH_INPUT: ${{ inputs.publish }}
shell: bash
run: |
if [[ "$EVENT" == "workflow_dispatch" ]]; then
STATUS_VAL=$([ "$PUBLISH_INPUT" = "true" ] && echo true || echo false)
else
STATUS_VAL=true
fi
PAYLOAD=$(cat <<EOF
{
"data":{
"type":"node--article",
"attributes":{
"title":"$TITLE",
"body":{"value":$BODY,"format":"basic_html"},
"status": $STATUS_VAL
}
}
}
EOF
)
echo ">>> POST $BASE/jsonapi/node/article"
RESP=$(curl -sS -u "$USER:$PASS" -H "Content-Type: application/vnd.api+json" -X POST "$BASE/jsonapi/node/article" --data "$PAYLOAD")
echo "$RESP" | jq .
UUID=$(echo "$RESP" | jq -r '.data.id')
test -n "$UUID" -a "$UUID" != "null" || { echo "创建失败:未返回 UUID"; exit 1; }
echo "uuid=$UUID" >> "$GITHUB_OUTPUT"
在 Settings → Secrets and variables → Actions 新建 3 个 Repository secrets:
DRUPAL_BASE=https://你的域名(不要末尾/)DRUPAL_USER=api_posterDRUPAL_PASS= 该用户密码
保存后 GitHub 不会显示明文,若要修改可覆盖保存。
3)平时怎么用:写 md → 提交 → 自动发布
3.1 在仓库里新增 Markdown
mkdir -p notes
cat > notes/2025-10-07.md <<'MD'
# 10-07 工作小结
- 站点接入 GitHub(deploy key)
- 开启 JSON:API 写入;本机 curl 测试通过
- 新增 GitHub Actions:md → HTML → 文章发布
MD
git add notes/2025-10-07.md
git commit -m "feat(notes): add 2025-10-07"
git push
推送命中 notes/*.md 或 notes/*.html,Actions 会自动运行并(默认)直接发布。
也可到 Actions → Post summary to Drupal → Run workflow 手动指定 note_path/title 并选择是否立即发布。
常见问题与排查
- 405 Method Not Allowed:JSON:API 仍是只读 →
/admin/config/services/jsonapi取消 “Accept only read operations”。 - 401/403 Forbidden:
- 检查 Secrets 是否正确;
- API 账号是否有“创建 Article 内容”“使用 Basic HTML 文本格式”权限;
- 临时将
format改为plain_text测试,确认是文本格式权限导致的。
- 415 Unsupported Media Type:请求头必须
Content-Type: application/vnd.api+json。 - 文本不渲染 HTML:确保该角色被允许使用
basic_html;或把format改为你站点允许的其它格式。 - .git 被外网访问:在 Web 服务器配置中禁掉
/.git访问。 - Windows 命令行粘贴 JSON 报错:把 JSON 写到文件里,再用
--data "@file.json";或单行执行。
评论