CSRF保護

CSRF(Cross-Site Request Forgery:クロスサイトリクエストフォージェリ)は、ユーザーが意図しないリクエストを第三者が送信する攻撃手法です。攻撃者は、認証されたユーザーを利用してそのユーザーの権限で悪意ある操作を行わせることを狙います。

a-blog cms を利用すればCSRF攻撃からサイトを簡単に保護できます。

CSRF攻撃の防止

a-blog cms では、CSRFトークンを使用してCSRF攻撃に対応しています。

システムによって管理されているユーザーセッションごとにCSRF「トークン」を自動的に生成します。 このトークンはユーザーのセッションに保存され、CMSが生成するHTMLにも自動的に埋め込まれるようになっております。

ユーザーが何かリクエストする際には、テンプレートに埋め込まれたCSRFトークンも一緒に送信することで、ユーザーセッションに保存されているトークンと比較し、悪意あるリクエストを排除することが出来るようになります。

以下では、CSRFトークンが使用されるリクエストや、CSRFトークンの生成方法について解説します。

CSRFトークンが必要な操作

a-blog cms で CSRFトークンが必要な操作について列挙します。

POST操作

POSTモジュールは基本的にCSRFのチェックを行いますが、例外として以下のPOSTモジュールはCSRFトークンをチェックしません。

  • ACMS_POST_2GET
  • ACMS_POST_2GET_Ajax
  • ACMS_POST_Download
  • ACMS_POST_Login_Check
  • ACMS_POST_Member_SigninRedirect

Ajax(ポストインクルード)によるGETリクエスト

通常GETリクエストは、CSRFトークンをチェックしませんが、Ajaxリクエストの場合、CSRFトークンによる認証をする場合があります。

URLコンテキストで「tpl」が指定されている場合

通常以下のようなURLコンテキストにtplが入ったURLは404になり取得できません。

https://example.com/news/tpl/search.html

ただAjaxの場合は、CSRFトークンを使用して取得することが可能です。

取得HTMLが部分HTMLだった場合

通常a-blog cmsは、部分的なHTMLの取得を許可しませんが、Ajaxの場合は、CSRFトークンを使用することで部分的なHTML取得が可能になります。

CSRFトークンによるチェックをするには、private/config.system.yaml で「ajax_security_level」が「2」に設定されている必要があります。

ajax_security_level: 2 # ajaxリクエストのセキュリティレベルを設定します。(0: チェックなし 1: RefererとHttpヘッダーを確認 2: CSRFトークン確認)

CSRFトークンの生成条件

以下の条件の場合にユーザーセッションをスタートさせ、CSRFトークンを生成します。

  • ログインしている時
  • POSTリクエストの時
  • 「ACMS_POST_Form_」から始まる文字列がテンプレートにある時
  • 「ACMS_POST_Comment_」から始まる文字列がテンプレートにある時
  • 「ACMS_POST_Shop」から始まる文字列がテンプレートにある時
  • 「ACMS_POST_2GET_Ajax」から始まる文字列がテンプレートにある時
  • テンプレートに「check-csrf-token」文字列がある時
  • シークレットブログ・カテゴリーだった時
  • ログインページなど認証系の画面の時
  • phpで「IS_OTHER_LOGIN」定数が定義されている時

CSRFトークンを生成によりセッションスタートしてしまうことで、ブラウザキャッシュやCDNなどでキャッシュが利用できなくなってしまいます。必要な場合のみCSRFトークンを生成するようにしています。

CSRFトークンの埋め込み場所

生成されたCSRFトークンはレスポンスされるHTMLに埋め込まれます。

form要素

HTMLのform要素内の最後に、自動でinput要素が埋め込まれます。

<form method="post">
  ...
  <input type="hidden" name="formToken" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
</form>

metaタグ

HTMLのメタタグとして、head要素内に自動で埋め込まれます。

<meta name="csrf-token" content="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">

a-blog cms の基本機能を利用する場合、基本的には何も設定しなくてもCSRFトークンを含めた形でリクエストする形になっているため対応は不要です。

標準機能外でCSRFトークンを利用する

標準機能は、特に何もしなくてもCSRF対策がされていますが、独自にJavaScriptでリクエストを送ってレスポンスを受け取る場合などは、自身でCSRFトークンをリクエストに含める必要があります。

POSTリクエストの場合

POSTデータに以下情報を含める必要があります。

formToken: xxxxxxxxxxxxxxx

独自に開発したPOSTモジュールにJavaScriptからリクエストする例

const csrfToken = document.querySelector('meta[name="csrf-token"]').content; // メタタグからCSRFトークンを取得
const formData = new FormData();
formData.append('ACMS_POST_CustomModule', 'exec');
formData.append('formToken', csrfToken);

const response = await fetch(url, {
  method: 'POST',
  body: formData,
});

GETリクエストの場合

HTTPヘッダーに以下情報を必要があります。

Referer: https://example.com/xxxxxxx
X-Requested-With: XMLHttpRequest
X-Csrf-Token: xxxxxxxxxxxxxxxxxxx

HTMXライブラリ にCSRFトークンをセットする例

document.addEventListener("htmx:configRequest", function(event) {
  const csrfToken = document.querySelector('meta[name="csrf-token"]').content; // メタタグからCSRFトークンを取得
  event.detail.headers['X-CSRF-Token'] = csrfToken;
});

設定したログイン時間よりも短い時間でログアウトしてしまう

a-blog cms では、ログインセッション時間を「.env」ファイルで設定できるようになっています。
デフォルトでは、72時間有効です。

# セッション
SESSION_COOKIE_LIFETIME=259200 # セッションのCookie有効期限

ただしサーバーの環境によっては、30分程度でログアウトしてしまう現象が発生することがあります。

原因

セッション管理にはPHPのセッションの仕組みを利用しています。 このセッション情報はデフォルトですとサーバー上のファイルとして管理しているのですが、このセッションファイルの有効期限が短い可能性がございます。

PHPデフォルトの設定だと、1/100の確率で、24分より古いセッションファイルが消えるので、これが影響している可能性が高いです。

session.gc_maxlifetime

サーバに保存されているセッションファイルを保護する有効期限。

session.gc_probability / session.gc_divisor

セッションファイルが消える確率。「session.gc_probability / session.gc_divisor」の確率で削除されます。

対策

php.ini で「session.gc_maxlifetime」の期限を伸ばしていただくか、config.user.php に以下コードを設定ください。

ini_set('session.gc_maxlifetime', 259200); // 72時間を設定

アップデートについて

このエントリーでは、Shopping Cart 拡張アプリのアップデート方法を説明します。

Shopping Cart 拡張アプリは手動でファイルを置き換えてアップデートする必要がありますので、手順を説明します。

Shopping Cart 拡張アプリをダウンロード

  1. 上記のボタンからShopping Cart 拡張アプリ の zip ファイルをダウンロードしてください。
  2. ダウンロードした zip ファイルを解凍し、 plugins ディレクトリから、ShoppingCart 拡張アプリを入手します。
  3. 入手した ShoppingCart 拡張アプリを extension/plugins にアップロードして、ファイルの置き換えを行います。
  4. ルートブログの管理画面 > 拡張アプリ から「アップデート」ボタンをクリックして、アップデートを行います。

以上で、Shopping Cart 拡張アプリのアップデートは完了です。

手動アップデート

FTP(SFTP)ソフトなどを使用し、手動でファイルをアップロードする方法です。オンラインアップデートや手動簡単アップデートが利用できない場合にのみ、以下の方法を使用してください。

1. 最新版の a-blog cms アップデートパッケージをダウンロード

ダウンロード | a-blog cms developer より「Ver.2.x系以上からのアップデートパッケージ」をダウンロードします。 アップデートパッケージ内のファイルは、基本的にそのままサイトへアップロードし上書きすることで適用できます。

Ver. 2.x 系 からのアップデートの場合

Ver. 2.x 系のアップデートについては専用のページがあるため、Ver. 2.x 手動アップデートを参照してください。

Ver. 3.x 系 からのアップデートの場合

ダウンロードしたアップデートパッケージファイルを解凍します。アップデートに使用するのは内包されている ablogcms フォルダです。

アップデート作業中にサイト閲覧者がアクセスすると、サイトが正常に表示されない場合があります。作業時間やタイミングに注意してください。

2. アカウントやライセンスの確認

アップデート作業の前に、以下の準備を行ってください。

  • CMSの管理者権限ユーザーアカウント
  • ライセンスの確認:a-blog cms のマイページで現在のライセンスがアップデート後のCMSバージョンに対応しているか確認し、必要であれば license.php ファイルを取得
  • FTPなどのファイルアップロード環境

3. 公式ドキュメントの確認

Release Note の確認

アップデートによる変更点をRelease Noteで確認します。詳細な変更内容を知りたい場合は、リリースノート | ブログを参照してください。

「バージョン別に気をつけること」の確認

本記事では基本的な手動アップデート方法を紹介していますが、アップデートバージョンによっては特別な注意点があるため、作業前に「バージョン別に気をつけること」を確認してください。

4. ファイル・フォルダのバックアップ

以下のファイル・フォルダをバックアップします。

  • .env(カスタマイズしている場合のみ)
  • .htaccess(カスタマイズしている場合のみ)
  • archives
  • config.server.php
  • config.user.php
  • cron
  • extension(カスタマイズしている場合のみ)
  • license.php
  • media
  • php/ACMS/User(カスタマイズしている場合のみ)
  • php/AAPP(カスタマイズしている場合のみ)
  • private(カスタマイズしている場合のみ)
  • storage
  • themes/カスタマイズしているテーマフォルダ
  • その他カスタマイズしているフォルダ・ファイル

また、データベースのバックアップも推奨します。

5. ファイルの更新

FTPソフトなどを利用してアップデートパッケージのファイルをサイトに上書きします。 ただし、全フォルダをそのまま置き換えると必要なファイルを削除してしまう危険があるため、下記の「上書きしないフォルダ・ファイル」は削除や上書きをしないよう注意してください。
themes/system 内をカスタマイズしている場合は差分を確認しながら置き換え作業を行なってください。

上書きしないフォルダ・ファイル

  • cache/.htaccess
  • php/ACMS/User(カスタマイズしている場合のみ)
  • php/AAPP(カスタマイズしている場合のみ)
  • private/config.system.yaml
  • storage
  • themes/カスタマイズしているテーマフォルダ

※ 上書きしないようアップデートパッケージからあらかじめ除外されているファイルやフォルダは記載していません、(extension など)。

setup フォルダをアップロードすると、一般に公開している a-blog cms のページはメンテナンスの表示に切り替わります。


メンテナンスページ


6. データベース更新

データベースのアップデートはメンテナンスページまたは、管理画面 > 更新 ページで行います。 ここではメンテナンスページでの実行について紹介します。

6-1. メンテナンスページからのデータベース更新

下記手順でブラウザからデータベースと config.server.php をアップデートします。 アップデート時に確認事項などが画面に表示されます。各画面に書かれていることを確認しながらボタンをクリックし、アップデートを完了してください。

  1. アップデート対象のサイトへブラウザでアクセス
  2. メンテナンスページで管理者アカウントでログイン
  3. 各画面の指示に従いアップデートを完了

6-2. setup フォルダのリネーム

アップデート完了後、FTPソフトなどを利用して setup フォルダを推測されにくい名前に変更してください。
※アップデート前の古いリネームされたsetupフォルダが残っていた場合削除して構いません。

setupフォルダが存在するまではメンテナンスページが表示されます。

7. ライセンスファイルの更新(必要な場合のみ)

a-blog cms のマイページからライセンスファイルをダウンロードした場合は、FTPソフトなどを利用して license.php を置き換えます。

8. 表示確認と公開

アップデートしたサイトへブラウザからアクセスします。
各ページの表示を確認し、問題が無ければ完了となります。 必要に応じてデバッグモードをONにしてください。

デバッグモードは、管理画面 > ユーザー > ログインしているユーザーアカウントの基本設定にある「モード」からユーザーごとにモード切り替えすることができます。デバッグモードをONにすることで、各種エラーメッセージの表示や、キャッシュ機能が無効となるなど、より動作確認に適した状態にすることができます。


ユーザーごとのデバックモード適用


サイト管理者としての操作や、ログアウトした状態での閲覧者としての表示チェックをします。
動作確認後には監査ログでエラーが出ていないか確認します。

表示や動作に問題がなければアップデートは完了です。

新機能


新しいマイナーバージョン Ver. 3.2 では、多くの新機能が追加されました。
Ver. 3.2 で開発を行う際は、ぜひ本内容に目をお通しください。

ブロックエディタを追加

従来のエディターに加え、新たにブロックエディタ機能が追加されました。
テキスト、画像、ファイルなどのコンテンツを「ブロック」単位で自由に組み立てることができ、みたままの視認性と柔軟性が大幅に向上します。

主な特徴・機能

  • HTMLのコピーアンドペーストに対応

  • スラッシュコマンド

    • マウスでブロックを選択しなくても、直感的にブロック挿入ができるようになります。

  • メディア機能に対応

    • メディアから画像やファイルを挿入することが可能

    • 大元のメディアで画像を差し替えても自動でブロックエディタ側も連動

  • インライン要素にカスタムクラス

    • 設定画面で設定した任意のクラスをインライン要素に設定可能

  • ブロック要素にカスタムクラス

    • 設定画面で設定した任意のクラスをブロック要素に設定可能

  • 2カラム・3カラムレイアウトに対応

    • みたままの状態でマルチカラムレイアウトに対応

  • マークダウンに対応

    • マークダウン記法で入力すると自動的にHTMLに変換します

組み込みJS

ブロックエディタは、ユニットだけではなく、カスタムフィールドでも利用可能です。組み込みJSとして用意しているので、下記HTMLを書くことで簡単にブロックエディタを導入可能です。

<div class="js-block-editor" data-target=".js-target" data-html=".js-html">
  <div class="js-target acms-admin-form-width-full"></div>
  <input type="hidden" class="js-html" name="hoge" value="{hoge}">
  <input type="hidden" name="field[]" value="hoge">
  <input type="hidden" name="hoge:extension" value="block-editor" />
</div>

ユニットの設定方法

編集画面 > 編集設定 > ユニットメニュー でブロックエディタを追加します。

ブロックエディターユニットを設定
ブロックエディターユニットを設定

編集画面 > ブロックエディタ設定 で、ブロックエディタ自体の設定を行います。

ブロックエディター設定画面
ブロックエディター設定画面

以下のことが設定可能です。

  • メディア画像のリサイズサイズ

  • 追加できるブロック、クラス

  • インライン要素のスタイルメニューの表示・非表示

  • インライン要素のカスタムクラス

  • カラーパレットの選択肢

  • フォントサイズの選択肢

  • フォントファミリーの選択肢



Twigを利用できるテンプレート機能を追加

新しいテンプレートエンジンとして Twig を利用できるようになりました。従来の独自テンプレートエンジンも引き続き使用可能で、Twig対応のテンプレート機能が追加された形となります。

Twigは広く利用されているテンプレートエンジンのため、関連する情報も多く、利用経験のある開発者も多いことから、カスタマイズのハードルが下がることが期待できます。

Twigを有効化する

Twigの有効化は、テーマ単位で行いますthemes/ご利用テーマ/template.yaml もしくは、テーマ設定からTwigを有効化します。

template.yaml ファイル

tpl_top : index.twig
tpl_index : index.twig
tpl_detail : _entry.twig
tpl_404 : 404.twig
tpl_admin : admin.html
tpl_entry_edit : _entry.twig
tpl_login : _member-admin/login.html
tpl_twig : enabled
テンプレート設定画面
テンプレート設定画面

モジュールの呼び出し方

Twigテンプレートでは、従来のモジュール(Entry_Summary など)は利用できません。代わりに、V2_ から始まる新しい「V2モジュール」(例:V2_Entry_Summary)を使用する必要があります。

このV2モジュールは、Twig専用に用意されたもので、独自のTwig関数 module() を使って呼び出し、データを変数に格納して利用します。

例:V2_Entry_Summary モジュールを呼び出して使用する

{% set entrySummary = module('V2_Entry_Summary', 'モジュールID名', { bid: BID, cid: CID }) %}
<div>
  <div class="acms-cssgrid acms-g-cols-1 acms-g-cols-md-3">
    {% for entry in entrySummary.items %}
    <div>
      {% if entry.mainImage.path %}
      <img
        src="{{ entry.mainImage.path|resizeImg(360) }}"
        alt="{{ entry.mainImage.alt }}"
        class="acms-img-responsive"
        width="{{ entry.mainImage.width }}"
        height="{{ entry.mainImage.height}}"
        loading="lazy"
        decoding="async"
      >
      {% endif %}
      <h3>{{ entry.title }}</h3>
      <p>{{ entry.summary }}</p>
      <p><a href="{{ entry.url }}" class="acms-btn">詳細をみる</a></p>
    </div>
    {% endfor %}
  </div>
</div>

module() 関数の引数について

引数

内容

第1引数

V2モジュール名(例:V2_Entry_Summary

第2引数

モジュールID(管理画面で設定されたIDを指定)

第3引数

URLコンテキスト(従来の ctx に該当。例:{ bid: BID, eid: EID } など)

V2モジュールの変数の見方

V2モジュールでは、従来のような「変数表」は用意されていません。
その代わりに、Twigテンプレートでモジュールを読み込んだ状態で、クイックサーチ機能から利用可能な変数を確認できる仕組みになっています。

クイックサーチの起動方法

管理者としてログイン中に、以下のショートカットキーを押すことでクイックサーチが開きます:

  • Windows / LinuxCtrl + K

  • Mac⌘(Command)+ K

デバッグモードでの変数確認

クイックサーチを開いた状態で、デバッグモードが有効になっているときに #(シャープ)を入力すると、現在のページで読み込まれているV2モジュールの変数一覧(変数表)が表示されます。

クイックサーチから変数表を表示
クイックサーチから変数表を表示
実際の値が入った変数表が確認できる
実際の値が入った変数表が確認できる

校正オプション(フィルター)の指定

Twigでも、従来の校正オプションを利用できます。またTwigデフォルトのフィルターも同じように使用することが可能です。

<p>{{ content|nl2br|safe_html }}</p>

使用できる校正オプションの種類

  • 従来の標準校正オプション

  • Hook.php で拡張した校正オプション

  • Twig標準のフィルター

従来の標準校正オプションは利用できますが、Twig標準のフィルターと名前がかぶるものもあります。この場合Twigのフィルターが優先されるので、従来の標準校正オプションを指定したい場合は、プレフィックス acms_ を付与して指定ください。

名前が校正オプション・フィルター例

date escape nl2br trim number_format split など

従来の校正オプションを使用する

{{ entry.datetime|acms_date('Y年m月d日') }}

twigでの「raw」校正オプション(フィルター)の仕様が変更になります。

既存テンプレート
{hoge}[raw]
危険なタグはエスケープされ、安全なHTMLは出力されます

{hoge}[raw|allow_dangerous_tag]
scriptを含めエスケープを無効にして、値をそのまま出力します。使用箇所には注意が必要です。

Twigテンプレート
{{ hoge|safe_html }}
危険なタグはエスケープされ、安全なHTMLは出力されます。

{{ hoge|raw }}
scriptを含めエスケープを無効にして、値をそのまま出力します。使用箇所には注意が必要です。


タッチモジュールの使い方

独自のTwig関数 touch() を使って呼び出します。関数の戻り値は真偽値になっているので、if文をつかい表示・非表示を行います。

{% if touch('Touch_Login') %}
  <p>ログイン中です</p>
{% endif %}

touch関数は、module関数と違い既存のすべてのタッチモジュールを呼び出せます


表示・非表示を制御しているのは、単なる「ifタグ」なので、条件式をtouch関数だけではなく組み合わせて利用することもできます。

{% if touch('Touch_Login') and IS_ADMIN %}
  <p>ログイン中 かつ 管理ページの場合</p>
{% endif %}

Twigによる実装メリット

  • グローバル変数でも校正オプションの適応が可能に

  • 閉じ忘れなどテンプレートの間違いでエラーが出るようになるので間違い探しをしなくて済む

  • 複雑な条件を簡潔にかける

  • エスケープ地獄から抜け出せる

  • テンプレートの実行順を意識しなくてよくなる

  • インクルード文に、どんな変数も使えるように

  • エディタのサポート(ハイライト表示・フォーマットなど)

従来テンプレート記法との併用時の注意点

Twig記法と、従来記法を混ぜて書くことができますが、テンプレート解決順による注意点があります。


テンプレートが解決される順番は、Twig が解決されてから、従来テンプレートが解決されます。

よって以下のようなパターンで、テンプレートが解決されない場合がありますので、ご注意ください。

  • 従来の @include で読み込んだテンプレートにあるTwig記法は解決されない

  • 従来の @extends で継承したテンプレート以下にあるTwig記法は解決されない

  • 従来の @extends で継承されているテンプレートは、Twig記法でインクルード出来ない



グループユニットを追加

新しいユニットタイプとして、記事編集をさらに柔軟にする 「グループユニット」 が追加されました。
従来のユニットグループでの課題を解決し、より直感的で自由度の高いレイアウト作成を実現します。

グループユニットを適用した編集画面

従来のユニットグループでは、段組みレイアウトを作成する際に CSS の float レイアウトを利用する必要があり、回り込み解除などの知識や操作をユーザーが理解していないと扱いにくい面がありました。
また、グループは 1 階層までしか作成できないため、複雑な入れ子構造を持つレイアウトを実現することは困難でした。

今回の新しいグループユニットでは、こうした課題を解消し、編集者がより直感的にコンテンツを構築できるようになっています。

主な特徴・機能

  • 直感的な段組み操作
    float に依存せず、CSS の知識がなくてもレイアウトを組み立て可能

  • 多階層のグループ構造
    1 階層の制限がなくなり、柔軟に入れ子レイアウトを構成可能

  • グループ単位での操作
    移動やコピーをまとめて行えるため、効率的に編集可能

  • id・タグの設定が可能に
    グループに idsection タグを設定でき、LP などでアンカーリンクを利用したセクション設計が容易に

  • 意味的なマークアップの強化
    SEO やアクセシビリティの観点からも有効な HTML 構造を生成可能



jpeg・png 画像をWebP画像に変換するオプションを追加

jpeg・png 画像を WebP画像に変換してアップロードするオプションが追加されました。(デフォルト無効

有効化すると、jpeg・png画像は生成されず、WebP画像のみアップロードされることになります。

有効化するには private/config.system.yaml で設定します。

convert_2webp: on # on | off jpeg/png画像をweb画像に変換します

これにより、画像ファイル数が少なくなり、画像ファイルサイズも縮小されるため、全体のファイル容量の削減に役立ちます。

* WebP画像は、iOS14から対応(2020年9月)



自動オンラインアップデート機能を追加

CMSのアップデートを管理画面からアップデートを実行しなくても、指定した時間帯に自動でアップデートする機能が追加されました。

  • デフォルト機能オフ

  • パッチバージョンのみ対応

  • 自動更新する時間帯を指定可能

  • 事項更新する曜日を指定可能

管理画面 > コンフィグ > 機能設定 で設定が行えます。

自動アップデート設定画面
自動アップデート設定画面


WordPress eXtended RSS(WXR)形式でのエクスポート機能を追加

WordPressとの連携や移行を容易にするため、WXR(WordPress eXtended RSS)形式でのエントリーエクスポート機能を追加しました。

  • WXR形式でのエクスポートに対応
    WordPressがサポートするXMLベースのWXR形式でエントリーデータを出力可能になりました。

  • ブログ単位でのエクスポート
    複数ブログを運用している環境でも、特定のブログ単位でデータをエクスポートできます。

  • エントリー、カテゴリー、ユーザー情報を含む
    エクスポートデータには、エントリー本文だけでなく、カテゴリー情報や投稿ユーザー情報も含まれます。

  • メイン画像をサムネイルとして出力
    各エントリーに紐づくメイン画像を、WXRの<wp:postmeta>としてサムネイル情報に含めています。

WXR形式のエクスポート画面
WXR形式のエクスポート画面

この機能追加により、データのポータビリティが大きく向上しました。
WXR形式という標準フォーマットでエクスポートできるため、他のCMSやツールとの互換性が高く、柔軟な運用が可能になります。

  • ユーザーの安心感
    将来的なサイト移行やバックアップにも対応できることで、長期的な視点でも安心してご利用いただけます。

  • ベンダーロックインの回避
    特定のCMSに依存しないデータ設計を実現し、ユーザー側での選択肢と自由度を確保します。

  • データ解析や再利用のしやすさ
    XMLベースの形式により、他システムでのデータ解析やフォーマット変換などの再利用にも適しています。



ストレージをローカルではなくAWS S3に保存できる仕組みを用意

プロフェッショナル版以上のライセンスにて、メディア・アーカイブ・ストレージ・キャッシュなどの各種ファイルを、ローカルストレージではなく Amazon S3 に保存できる仕組みを新たに追加しました。

対象ストレージ領域

  • media:メディア管理の画像ファイル

  • archives:メディア管理していない画像やファイルなど

  • storage:メディア管理の画像以外のファイル

  • cache:キャッシュファイル

この機能によりできること

大規模サイトを複数台のWebサーバーで運用する際、画像などのファイルをどのように共有・配信するかが課題となっていました。今回の機能追加により、AWS上での複数台構成において、ファイル類をS3に集約し、CloudFrontなどのCDNを通じて効率的に配信できるようになります。

各Webサーバーが共通のS3ストレージを参照するようになるため、ファイルの重複管理やサーバー間の同期処理は不要になります。これにより、シンプルでスケーラブルな構成が可能となり、大規模運用におけるファイル管理の負担を大幅に軽減します。

設定方法

a-blog cms 設置ディレクトリの設定ファイル .env で行います。S3バケットの指定・認証情報の設定が可能です。

# ストレージ設定
STORAGE_DRIVER=local # (local|s3) 公開ストレージの保存先を選択します
STORAGE_S3_KEY= # S3のアクセスキーを指定します
STORAGE_S3_SECRET= # S3のシークレットキーを指定します
STORAGE_S3_REGION=ap-northeast-1 # S3のリージョンを指定します
STORAGE_S3_PUBLIC_BUCKET= # 公開用のS3のバケット名を指定します
STORAGE_S3_PUBLIC_PREFIX= # オプションでパスのプレフィックスを指定します
STORAGE_S3_PRIVATE_BUCKET= # 非公開のS3のバケット名を指定します
STORAGE_S3_PRIVATE_PREFIX= # オプションでパスのプレフィックスを指定します
ASSETS_DELIVERY_URL= # CMSのドメインとS3配信URLが違う場合は配信URLを指定します(例: https://assets.example.com)

バケットは PUBLICPRIVATE の2つご用意ください。media archivesPUBLIC なバケットを参照し、CMSからのみアクセスする storagecachePRIVATE なバケットを参照するようになっています。



組み込みJSに「htmx」を追加

組み込みJSに「htmx」ライブラリを追加しました。

htmx は、HTML を拡張して、ページの一部を動的に更新するための軽量なライブラリです。JavaScript を使わずに、HTML属性を用いてサーバーとの通信やページの更新を行うことができます。これにより、クライアントサイドのコードを減らし、シンプルで直感的な開発が可能になります。

a-blog cmsでは従来から「post include」機能という、htmxライブラリと同じような機能があり、htmxとの相性もよく、組み込みJSが読み込まれていれば、特に初期設定などせずに、すぐ使い始めることが可能です。



Vite 連携機能を追加

次世代のフロントエンドツール「Vite」との連携機能が追加されました。

これにより、テーマ開発時に Vite でビルドしたアセット(JavaScriptやCSSなど)を、テンプレートに簡単に読み込むことができるようになります。開発時はViteのDevサーバーと連携し、ビルド後はmanifest.jsonを元に本番アセットを読み込む運用が可能です。

従来テンプレートの場合

@viteReactRefresh <!-- Reactを利用する場合 -->
@vite(['src/js/main.js', 'src/style/style.css', {
  "scriptTagAttributes": {
    "async": true
  }
})

Twigテンプレートの場合

{{ viteReactRefresh() }} <!-- Reactを利用する場合 -->
{{ vite(['src/js/main.js', 'src/js/admin.js', 'src/style/style.css'], {
  'scriptTagAttributes': {
    'async': true,
  }
}) }}

viteReactRefresh() は開発環境でReactのホットリロード(Fast Refresh)を有効化するために使用します。

本番環境と開発環境の切り替え

.env ファイルに以下のような設定を追加することで、Viteの動作モード(開発モード/本番ビルド)を切り替えることができます。

# Vite(https://vite.dev/)
VITE_ENVIRONMENT=production # development | production
VITE_MANIFEST_PATH= # manifest.json のパスを利用しているテーマディレクトリから指定します。(例: dist/.vite/manifest.json)
VITE_DEV_SERVER_URL= # Viteの開発サーバーURLを指定します。(例: http://localhost:5173)