Netlify Hosting を使わずに Netlify CMS を立てた話

mediba Advent Calendar 2020 10日目担当の尾野です。

入社から昨年度まで関わっていた au Webポータルから離れ、今期からゼロイチの経験をしたくて新規事業創出部門にいます。

ただ、何を隠そう我々の部門はお金がありません

今回はそんなお金の無い部門が安価に CMS を立てたと言う小ネタ中の小ネタなお話です。

こんな人が対象

  • 初期構築/ランニング共に安価な構成で構築したい
  • CMS は非エンジニアでも利用可能なものを構築したい
  • React(Next.js) の利用経験がある

サービス運営の中で静的なランディングページの提供を必要とするケースは決して稀ではないと思いますが、WordPress の様なパッケージ化された CMS の場合、システム構築や公開後の可用性維持に思わぬコストが発生する事があると思います。

そういったケースには有用かと思います。

Netlify CMS is 何?

https://www.netlifycms.org/

Open source content management for your Git workflow (Git ワークフローのオープンソースコンテンツ管理)

Use Netlify CMS with any static site generator for a faster and more flexible web project (Netlify CMSを静的サイトジェネレーターと一緒に使用して、より高速で柔軟なWeb プロジェクトを実現します)

公式ページの冒頭の通り、GitBucket や Github 等のリポジトリをハブとし、静的サイトジェネレータと組み合わせる事で高速で柔軟な Web プロジェクトを実現出来るとの事です。

料金形態については Netlify Pricing(https://www.netlify.com/pricing/)をご参照ください。

Netlify CMS を使う方の多くは、Netlify Hosting サービスとセットで使う事が多いようですが、 無償プランの範疇ではアクセス制限を掛ける事が出来ないようです。

ほとんどの場合、Staging 環境などにおいてアクセス制限を掛ける事は必須かと思います。 それを富豪的に解決したくない程にはお金をケチりたいので自前で立てる事にしました。

構成

  • CMS : Netlify CMS -リポジトリ : Github Enterprise Cloud
  • 静的サイトジェネレータ:Next.js(ver. 9.4)
  • Hosting:AWS S3 + CloudFront

以下の様な構成にしてます。

特筆点で言うと以下くらいです。

  • CMS は staging 向き合いのみにした

    • Netlify CMS では push するブランチを config.yml で指定する事が出来るが、それを環境変数毎に動的に変更させる為には config.yml を止めて直接ソースコードに設定を書く必要があり止めた(https://www.netlifycms.org/docs/beta-features/#manual-initialization)
      • 運用も staging で入稿して、OKなら production で入稿しなおして〜 となると二重運用になってしまうので、staging で OK なら slack 経由で Github Actions をキックし、production に deploy する方式にした
  • アカウント管理は社の Active Directory と連携しそちらに委ねた

    • SSO 連携出来る Github Enterprise Cloud の恩恵を受けた

自前環境でNetlify CMSが使えるようになるまで

認証

自前で構築となると、認証サーバーも自前で構築する必要があります。

以下でいくつか紹介されています。
https://www.netlifycms.org/docs/external-oauth-clients/

今回は以下を採用しました。
https://github.com/marksteele/netlify-serverless-oauth2-backend

構築手順は以下のブログの通りに進めていけば良さそうです。
https://www.control-alt-del.org/blog/serverless-blog-howto/

ただ1点だけ、Github の OAuth Client を OAuth Apps ではなく Github Apps に変更しました。

アカウント単位ではなくリポジトリ単位で認証したいという理由なだけです。

Github Apps 作成時の入力/設定は以下くらいでした。

  • General > User authorization callback URL:[sls deployコマンドの出力結果のoauth url]
  • Repository permissions
    • Actions:Read & Write
    • Pull requests: Read & Write

CMS設定

config.yml の 以下セクション周りを変更します

path/to/public/config.yml

backend:
  name: github
  branch: deployment/staging
  repo: [該当のリポジトリ名]
  base_url: [払い出されたAPI Gateway の URL]
  auth_endpoint: /prod/auth
  commit_messages:
    create: 'Create {{collection}} “{{slug}}”'
    update: 'Update {{collection}} “{{slug}}”'
    delete: 'Delete {{collection}} “{{slug}}”'
    uploadMedia: '[skip ci] Upload “{{path}}”'
    deleteMedia: '[skip ci] Delete “{{path}}”'

locale: 'ja'
local_backend: true
publish_mode: editorial_workflow

media_folder: public/images 
public_folder: /images

media_folder や config.yml を public/ に置いているのは、SSG テンプレートとして利用している Next.js 9.1 でサポートされたpublicディレクトリサポートに乗っかる為です。 https://nextjs.org/blog/next-9-1#public-directory-support

その他

Preview 画面がそのままだとあまり Preview 感が無いので、Preview Template をカスタムしてます。
https://www.netlifycms.org/docs/customization/

path/to/public/admin/index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Content Manager</title>
  <script type="text/javascript" src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
</head>
<body>
  <!-- Include the script that builds the page and powers Netlify CMS -->
  <script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
  <script>CMS.registerPreviewStyle("./markdown.css");</script>
  <script type="module">
    import IndexPreview from "./preview-templates/index.js"
    CMS.registerPreviewTemplate("index", IndexPreview);
    </script>
</body>
</html>

path/to/public/admin/preview-templates/index.js

import htm from "https://unpkg.com/htm?module"
const html = htm.bind(h)
const IndexPreview = createClass({
  render: function () {
    const entry = this.props.entry
    const largeImage = entry.getIn(["data", "largeImage"])
    return html`
      <div class="content">
        <div class="about-wrap">
          <div class="header-wrap">
            <div class="header">
              <div class="header-left">
                <img class="logo-image" src="/images/Frame.svg"></div>
              <div class="header-right">
                <ul>
    :
    :

完成版

こんな感じで動きます


(もうちょっとクオリティ上げれば良かったですね、アニgif)

さいごに

Netlify Hositng 環境じゃなくとも割と簡単に動かす事が出来ます。

安心安価に LP 作りやブログシステム構築するなら参考にしてみてください。