Cloudflare Pages + Astro に Decap CMS を導入する
出先でスマホからブログ記事を書きたい。GitHub のモバイルアプリで Markdown を編集するのは辛い。
Decap CMS(旧 Netlify CMS)を入れた。ブラウザ上で記事を書いて、そのまま GitHub にコミットしてくれる。/admin/ にアクセスするだけ。
セットアップ
public/admin/ に2ファイル置くだけ。
index.html で Decap CMS の JS を読み込む。
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="robots" content="noindex" />
<title>Content Manager</title>
</head>
<body>
<script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
</body>
</html>
config.yml でコレクションを定義する。
backend:
name: github
repo: ota2000/ota2000.com
branch: main
base_url: https://ota2000.com
auth_endpoint: api/auth
collections:
- name: blog
label: Blog
folder: site/src/content/blog
create: true
slug: "{{slug}}"
extension: md
fields:
- { label: Title, name: title, widget: string }
- { label: Description, name: description, widget: string }
- { label: Date, name: date, widget: datetime, format: "YYYY-MM-DD" }
- { label: Tags, name: tags, widget: list }
- { label: Body, name: body, widget: markdown }
OAuth プロキシ
ここがハマった。Decap CMS の GitHub 認証は OAuth サーバーが必要。Netlify にホストしていれば自動で使えるが、Cloudflare Pages では自前で用意する必要がある。
PKCE 認証を試したが、Decap CMS の GitHub 向け PKCE は未完成(Issue #6597 がオープンのまま)。Netlify の OAuth プロキシにフォールバックして動かない。
結局 Cloudflare Pages Functions で OAuth プロキシを書いた。/api/auth と /api/callback の2つ。
/api/auth は GitHub の OAuth 認可画面にリダイレクトするだけ。
export async function onRequest(context) {
const clientId = context.env.GITHUB_OAUTH_CLIENT_ID;
const scope = 'repo';
const url = `https://github.com/login/oauth/authorize?client_id=${clientId}&scope=${scope}`;
return Response.redirect(url, 302);
}
/api/callback がトークン交換と Decap CMS への受け渡しを担う。ポイントは2段階の postMessage ハンドシェイク。
function renderBody(status, content) {
return `<script>
const receiveMessage = (message) => {
window.opener.postMessage(
'authorization:github:${status}:${JSON.stringify(content)}',
message.origin
);
window.removeEventListener("message", receiveMessage, false);
}
window.addEventListener("message", receiveMessage, false);
window.opener.postMessage("authorizing:github", "*");
</script>`;
}
最初に authorizing:github を親ウィンドウに送り、親が応答したらその origin を使ってトークンを返す。この2段階が必要だと気づくまでに時間がかかった。直接 postMessage でトークンを送っても Decap CMS は受け取ってくれない。
環境変数
GitHub OAuth App の Client ID と Client Secret を Cloudflare Pages の環境変数に設定する。Terraform で管理。
GITHUB_OAUTH_CLIENT_ID = {
type = "plain_text"
value = var.github_oauth_client_id
}
GITHUB_OAUTH_CLIENT_SECRET = {
type = "plain_text"
value = var.github_oauth_client_secret
}
使い勝手
ota2000.com/admin/ にアクセスして GitHub ログインすれば記事一覧が出る。新規作成・編集どちらも Markdown エディタで書ける。スマホからでも使える。保存すると GitHub にコミットされ、Cloudflare Pages が自動デプロイ。