プラグインはWordPressサイトのカスタマイズや拡張に欠かせない存在。コーディングなしでお問い合わせフォームEC機能分析ツールなどを実装できるため非常に便利です。

WordPressが定期的に更新されるように、プラグインもまた新機能の導入、セキュリティパッチ、互換性の改善などの目的で継続的に更新されています。この点を考慮し、Kinstaの専用コントロールパネル「MyKinsta」では、プラグインとテーマを簡単に管理できるようになっています。

しかし、多数のサイトを保守管理する企業にとって、プラグインの更新は大変な作業です。そこで今回は、そんな負担を軽減するため、Kinsta APIを使用して複数のWordPressサイトでプラグインを同時に管理するツールを作成する方法をご紹介します。

構築するもの

今回は、Kinsta APIを使用して高度なツールを構築することに焦点を当てます。

具体的には、MyKinstaの企業アカウントからすべてのプラグインを取得するReactアプリケーションを構築していきます。このアプリケーションにより、複数のサイトにまたがって特定のプラグインを更新することができるため、日々の作業が大幅に捗ります。

複数のWordPressサイトのプラグインを一括更新するためにReactとKinsta APIで構築したツール例
複数のWordPressサイトのプラグインを一括更新するためにReactとKinsta APIで構築したツール例

アプリケーションの前提条件

これからご紹介するプロジェクトは、以下の条件を前提とします。

  • HTMLCSSJavaScriptの基礎知識
  • Reactの使用経験(ある程度精通していること)
  • Node.jsとnpm(Node Package Manager)またはyarnがコンピュータにインストールされている

Kinsta APIを理解する

Kinsta APIは、KinstaのWordPress専用マネージドホスティングとプログラムを介してやり取りすることを可能にする強力な機能です。サイトの作成サイト情報の取得サイトのステータスの取得バックアップの参照と復元など、Kinstaが提供するサービスに関連する様々なタスクを自動化することができます。

Kinsta APIを使用するには、MyKinstaに少なくとも1つのWordPressサイト、アプリケーション、またはデータベースのアカウントが必要です。また、APIを通してアカウントを認証し、アクセスするためにAPIキーを生成する必要があります。

APIキーを生成する方法は以下のとおりです。

  1. MyKinstaに移動
  2. APIキー」ページに移動((お客様の名前)>「企業の設定」>「APIキー」)
  3. APIキーを作成」をクリック
  4. 有効期限を選択するか、「カスタム」から日付と時間を指定
  5. キーに一意の名前を付ける
  6. 生成」をクリック

APIキーを作成したら、必ずコピーして安全な場所に保管してください(パスワードマネージャーの使用をおすすめします)。複数のAPIキー生成が可能で、その名前は「APIキー」ページに一覧で表示されます。APIキーを取り消す必要がある場合は、取り消したいAPIキーの横にある「アクセス取り消し」をクリックしてください。

React開発環境のセットアップ

Reactは、ユーザーインターフェースを構築するための主要JavaScriptライブラリです。ユーザーインターフェースのさまざまな部分を表す宣言型コンポーネントを作成できます。コンポーネントは、JavaScriptとHTMLを組み合わせたJSX構文で定義されます。

以下の手順に従ってください。

  1. プロジェクトを作成したいフォルダに移動し、create-react-appを使用してReactプロジェクトを作成します。<project-name>は、任意のプロジェクト名に置き換えてください。
    npx create-react-app <project-name>
  2. 作成後、プロジェクトフォルダに移動し、開発サーバーを起動します。
    cd <project-name>
    npm run start

    デフォルトのウェブブラウザでReactアプリが「http://localhost:3000」に表示されます。

create-react-appを使用してReactプロジェクトを作成すると、フォルダ構造が設定されます。重要なフォルダはsrcで、ここで開発を行います。フォルダに格納されている主なファイルは以下のとおりです。

  • App.js:Reactアプリの他のすべてのレンダリングを行うメインコンポーネント。すべてのコードがここに追加される。
  • index.js:エントリポイントで、最初に読み込まれ、App.jsをレンダリングする。
  • index.css:アプリ全体のスタイルとレイアウトを定義するファイル。すべてのスタイルがここに追加される。

ユーザーインターフェースの作成とスタイル設定

App.jsファイルに格納された基本的なアプリケーションのインターフェースを、ルーティングを介さずに構築してスタイリングしていきます。メインUIには、選択したプラグインがインストールされているサイトを取得するためのsubmitボタンと、MyKinsta内のサイト全体で使用されているプラグインを表示するためのselectフィールドを持つフォームが配置されます。

プラグイン一覧にアクセスしてサイトを読み込むプラグイン管理ツールのUI
プラグイン一覧にアクセスしてサイトを読み込むプラグイン管理ツールのUI

表示セクションには、サイト名、プラグインのステータスおよびバージョンなどの詳細が表示されます。必要に応じて各サイトを更新するためのボタンと、プラグインの更新が必要になるサイトを一括更新するための基本となるボタンも表示されます。

App.jsファイルに以下のコードを追加します。

import KinstaLogo from './images/kinsta_logo.png';

const App = () => {
    return (
        <div className="container">
            <div className="title-section">
                <img src={KinstaLogo} className="logo" alt="" />
                <h2>サイトのプラグイン管理ツール</h2>
                <p>
                    Kinsta APIを利用して、Kinstaでホストするすべてのサイトで簡単にプラグインを更新できます。
                    簡単にプラグインを更新することができます。
                </p>
            </div>
            <div> className="info-section">
                <p>
                    特定のプラグインを使用するすべてのサイト一覧を取得し、
                    これらのサイトすべてで同時にプラグインを更新するか、
                    個別に更新するかを選択できます。
                </p>
            </div>
            <div className="form-section">
                <form>
                    <div className="form-control">
                        <label> htmlFor="plugin-name">プラグイン名</label>
                        <select name="plugin-name" id="plugin-name">
                            <option> value="">プラグインの選択</option>
                        </select>
                    </div>
                    <button> className="btn">このプラグインを使用するサイトを取得する</button>
                </form>
            </div>
            <div className="display_container">
                <div className="site-list">
                    <div className="list-title">
                        <h3>WooCommerceプラグインを使用するサイト</h3>

                        <button> className="sm-btn">すべてのサイトをv.3.6に更新する</button>
                    </div>
                    <ul>
                        <li>
                            <div className="info">
                                <p>
                                    <b>サイト名:</b> WooCommerce
                                </p>
                                <p>
                                    <b>プラグインのステータス:</b> 有効
                                </p>
                                <p>
                                    <b>プラグインバージョン:</b> 3.5.1
                                </p>
                            </div>
                            <button> className="sm-btn">v.5.6に更新する</button>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    );
};

export default App;

スタイルを編集するには、GitHubリポジトリのCSSファイルにアクセスし、コードをindex.cssファイルにコピーしてください。

Kinsta APIと対話する

Kinsta APIは、サイトのプラグインと相互作用するために必要な様々なパラメータにアクセスするのに不可欠なエンドポイントの範囲を提供します。例えば、プラグインを取得/更新するには、サイトの環境IDが必要です。

この環境IDの取得は、連続したプロセスであり、まず最初にサイトのIDを決定しなければなりません。サイトIDを取得するには、Kinstaの企業IDが必要です。これはAPIキー同様、他の人とは共有してはいけない機密情報で、MyKinstaで確認可能です(「企業の設定」>「請求先情報」)。

プロジェクトのルートフォルダに.envファイルを作成することで、Reactアプリケーションの環境変数として安全に保存可能です。このファイルに「YOUR_COMPANY_ID」を実際の企業IDに置き換えて、以下のコードを追加します。

REACT_APP_KINSTA_COMPANY_ID = 'YOUR_COMPANY_ID' 
REACT_APP_KINSTA_API_KEY = 'YOUR_API_KEY'

プロジェクト内でこれらの環境変数にアクセスするには、process.env.THE_VARIABLE構文を使用します。例えば、REACT_APP_KINSTA_COMPANY_IDにアクセスするには、process.env.REACT_APP_KINSTA_COMPANY_IDを使用します。

GitHubにプッシュされないようにするため、.envファイルを.gitignoreファイルに追加することが重要です。こうすることで、機密情報を安全に保管することができます。

Kinsta APIを使用してすべてのサイトとプラグインを取得する

MyKinstaの企業アカウント管理しているすべてのサイトのプラグインデータを取得するには、3つのAPIリクエストを実行します。

簡単に参照できるようにするため、まずはKinsta APIのURLを変数に保存します。

const KinstaAPIUrl = 'https://api.kinsta.com/v2';
  1. 企業アカウントのサイト一覧を取得する:企業に関連するすべてのWordPressサイトの一覧を取得する必要があります。これを行うには、企業IDを使用してクエリを構築し、適切な権限でGETリクエストを行い、レスポンスをJSON形式に処理して、レスポンスからサイトの詳細を取得します。
    const query = new URLSearchParams({
        company: process.env.REACT_APP_KINSTA_COMPANY_ID,
    }).toString();
    const response = await fetch(`${KinstaAPIUrl}/sites?${query}`, {
        method: 'GET',
        headers: { Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}` },
    });
    
    const data = await response.json();
    const companySites = data.company.sites;
  2. サイト環境IDを取得する:1のステップはWordPressサイトの配列を返します。各サイトに関連する環境を取得するため、ループして別の GETリクエストを行います。
    const sitesEnvironmentData = companySites.map(async (site) => {
        const siteId = site.id;
        const resp = await fetch(`${KinstaAPIUrl}/sites/${siteId}/environments`, {
            method: 'GET',
            headers: {
                Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
            },
        });
        const data = await resp.json();
        const environments = data.site.environments;
        return {
            id: siteId,
            name: site.display_name,
            environments: environments,
        };
    });
  3. WordPressサイトのプラグイン一覧を取得する:サイトID、名前、環境を取得したら、次に環境IDを使って各サイトのすべてのプラグイン一覧を取得します。前のステップでプロミスを解決した上で、プラグインのGETリクエストを行います。
    // Wait for all the promises to resolve
    const sitesData = await Promise.all(sitesEnvironmentData);
    
    // Get all plugins for each environment
    const sitesWithPlugin = sitesData.map(async (site) => {
        const environmentId = site.environments[0].id;
        const resp = await fetch(
            `${KinstaAPIUrl}/sites/environments/${environmentId}/plugins`,
            {
                method: 'GET',
                headers: {
                    Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
                },
            }
        );
        const data = await resp.json();
        const plugins = data.environment.container_info;
        return {
            env_id: environmentId,
            name: site.name,
            plugins: plugins,
        };
    });
    
    const sitesWithPluginData = await Promise.all(sitesWithPlugin);
    return sitesWithPluginData;
  4. プロセスの統合:プロセスを合理化するため、これらのAPIリクエストを再利用可能な単一の非同期関数getSitesWithPluginDataにカプセル化します。この関数は、上記のステップを実行して、環境ID、サイト名、プラグインの配列など、各サイトに関する重要な情報を含む配列を返します。
    const getSitesWithPluginData = async () => {
        const query = new URLSearchParams({
            company: process.env.REACT_APP_KINSTA_COMPANY_ID,
        }).toString();
        const resp = await fetch(`${KinstaAPIUrl}/sites?${query}`, {
            method: 'GET',
            headers: {
                Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
            },
        });
    
        const data = await resp.json();
        const companySites = data.company.sites;
    
        // 各サイトのすべての環境を取得
        const sitesEnvironmentData = companySites.map(async (site) => {
            const siteId = site.id;
            const resp = await fetch(`${KinstaAPIUrl}/sites/${siteId}/environments`, {
                method: 'GET',
                headers: {
                    Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
                },
            });
            const data = await resp.json();
            const environments = data.site.environments;
            return {
                id: siteId,
                name: site.display_name,
                environments: environments,
            };
        });
    
        // すべてのプロミスが解決するのを待つ
        const sitesData = await Promise.all(sitesEnvironmentData);
    
        // 各環境のすべてのプラグインを取得
        const sitesWithPlugin = sitesData.map(async (site) => {
            const environmentId = site.environments[0].id;
            const resp = await fetch(
                `${KinstaAPIUrl}/sites/environments/${environmentId}/plugins`,
                {
                    method: 'GET',
                    headers: {
                        Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
                    },
                }
            );
            const data = await resp.json();
            const plugins = data.environment.container_info;
            return {
                env_id: environmentId,
                name: site.name,
                plugins: plugins,
            };
        });
    
        // すべてのプロミスが解決するのを待つ
        const sitesWithPluginData = await Promise.all(sitesWithPlugin);
        return sitesWithPluginData;
    };

すべてのサイトから固有のプラグインを取得する

アプリケーションで、selectドロップダウンメニューにすべてのサイトのプラグイン一覧を表示したいとします。この場合、getSitesWithPluginData関数が、各サイトの環境ID、名前、およびプラグインを取得します。このデータがプラグイン一覧を抽出するための基盤となります。

getSitesWithPluginDataを呼び出し、その出力を処理してすべてのプラグイン一覧を取得するfetchAllSitesPlugins関数を定義します。

const fetchAllSitesPlugins = async () => {
    const sitesWithPluginData = await getSitesWithPluginData();

    // すべてのプラグインを取得
    const allPlugins = sitesWithPluginData.map((site) => {
        const { plugins } = site;
        return plugins.wp_plugins.data;
    });

   // …
};

このコードは各サイトのデータを繰り返し処理し、プラグインの一覧を作成します。各プラグインが一度だけリスト化されるように、一意の値を穂保存するJavaScriptのSetオブジェクトを使用します。

// 一意のプラグインを取得
    const uniquePlugins = [
        ...new Set(allPlugins.flat().map((plugin) => plugin.name)),
    ];

.flat()は配列構造を扱いやすい形に変換(フラット化)し、.map()はループしてプラグイン名だけを抽出します。Setオブジェクトは重複をフィルタリングする役割を担います。

Reactアプリケーションでこのデータを読み込んで表示するには、useState()useEffect()フックを使用します。

import { useState, useEffect } from 'react';

const App = () => {
    const [pluginName, setPluginName] = useState('');
    const [plugins, setPlugins] = useState([]);

    //プラグインデータを持つサイトを取得
    const getSitesWithPluginData = async () => {
        // リクエストを実行
    };

    useEffect(() => {
        const fetchAllSitesPlugins = async () => {
            const sitesWithPluginData = await getSitesWithPluginData();
            // すべてのプラグインを取得
            const allPlugins = sitesWithPluginData.map((site) => {
                const { plugins } = site;
                return plugins.wp_plugins.data;
            });
            // 一意のプラグインを取得
            const uniquePlugins = [
                ...new Set(allPlugins.flat().map((plugin) => plugin.name)),
            ];
            setPlugins(uniquePlugins);
        };

        fetchAllSitesPlugins();
    }, []);

     //JSXレンダリングコードが以下に続く
    //...
};

useEffect()フックは、コンポーネントがマウントされたときにデータが取得され、セットされるようにします。useState()フックは、固有のプラグインのリストを管理します。

最後に、これらのプラグインをselectフィールドに表示します。プラグインがまだ読み込み中の場合は、プレースホルダでメッセージを表示します。

<select>
    name="plugin-name"
    id="plugin-name"
    value={pluginName}
    onChange={(e) => setPluginName(e.target.value)}
>
    {plugins.length > 0 ? (
        <>
            <option value="">プラグインを選択する</option>
            {plugins.map((plugin) => (
                <option key={plugin} value={plugin.toLowerCase()}>
                    {plugin}
                </option>
            ))}
        </>
    ) : (
        <option> value="">プラグインを読み込んでいます</option>
    )}
</select>

このコードの詳細は以下のとおりです。

  • select要素─選択された値を格納するステート変数pluginNameにリンク
  • onChangeハンドラ─プラグインが選択されるたびにこの状態を更新
  • plugins.map()─各プラグインのオプション要素を動的に生成

以上の手順に従うことで、操作しやすいシンプルなインターフェースで、すべてのサイトから取得したプラグイン一覧を表示するアプリケーションを構築できます。

すべてのサイトから一意のプラグイン一覧を表示するフィールドを選択
すべてのサイトから一意のプラグイン一覧を表示するフィールドを選択

特定のプラグインを持つサイトを取得する

ここまでで、MyKinstaの企業アカウントからプラグインを取得することはできますが、続いては、特定のプラグインを持つサイトを取得するためにすべてのサイトをループし、それらを状態に保存し、表示する方法を実装していきます。

これを行うには、2つの状態を作成します。1つはサイトを保存する状態(sites)、もう1つは読み込み状態を示す状態(isLoading)です。

const [sites, setSites] = useState([]);
const [isLoading, setIsLoading] = useState(false);

それからfetchSites関数を作成し、各サイトをフィルタリングして、選択したプラグインが含まれているかどうかを検証します。含まれていない場合は、そのサイトの関連する詳細が保存されます。

まずisLoadingtrueに設定して、sites配列をクリアします。その後、getSitesWithPluginDataを呼び出し、すべてのサイトデータを取得します。

const fetchSites = async () => {
    setIsLoading(true);
    setSites([]);
    const sitesWithPluginData = await getSitesWithPluginData();

    // プラグインを持っていないサイトをフィルタリング
    const sitesWithPluginDataFiltered = sitesWithPluginData
        .filter((site) => {
            const sitePlugins = site.plugins.wp_plugins.data;
            return sitePlugins.some((plugin) => {
                return plugin.name === pluginName;
            });
        })
        .map((site) => {
            const { env_id, name } = site;
            const { version, status, update, update_version } =
                site.plugins.wp_plugins.data.find(
                    (plugin) => plugin.name === pluginName
                );
            return {
                env_id,
                name,
                version,
                status,
                updateAvailable: update,
                updateVersion: update_version,
            };
        });
    setSites(sitesWithPluginDataFiltered);
    setIsLoading(false);
};

sitesWithPluginDataFiltered関数の詳細は以下のとおりです。

  • .filter() が選択したプラグインを含むサイトを分離
  • 次に.map()が各サイトから必要な詳細を抽出
  • 最後にsetSitessetIsLoadingフックが、新たなデータと読み込み状態で状態を更新

次に、handleSubmit関数を作成し、プラグインを選択してフォームを送信した際にこの関数を呼び出すよう、フォームの「このプラグインを使用するサイトを取得する」(Fetch sites with this plugin)ボタンに追加します。この関数によってデフォルトのフォームアクションを防ぎ、fetchSitesを呼び出します。

const handleSubmit = (e) => {
    e.preventDefault();
    fetchSites();
};

これで特定のプラグインを選択して送信ボタンをクリックすると、そのプラグインを使用するサイトをすべて取得し、sitesの状態に保存します。

選択したプラグインを使用するサイトを表示する

sitesに関連サイトを保存することに成功したら、次にプロジェクトのユーザーインターフェースにこのデータを表示させます。各サイトを一覧項目として表示し、主要な詳細とプラグイン更新のためのボタンを表示します。

<ul>
    {sites.map((site) => (
        <li key={site.env_id}>
            <div className="info">
                <p>
                    <b>サイト名:</b> {site.name}
                </p>
                <p>
                    <b>プラグインのステータス:</b> {site.status}
                </p>
                <p>
                    <b>プラグインのバージョン:</b> {site.version}
                </p>
            </div>
            <button>
                className={`sm-btn ${
                    site.updateAvailable !== 'available' ? 'disabled-btn' : ''
                }`}
                disabled={site.updateAvailable !== 'available'}
            >
                {site.updateAvailable === 'available'
                    ? `Update to v.${site.updateVersion}`
                    : '最新'}
            </button>
        </li>
    ))}
</ul>

上のコードでは、.map()を使用してsites 配列を繰り返し、サイト一覧(<ul>)を作成します(<li>要素)。一覧の各項目には、サイトの詳細とプラグイン更新用のボタンが含まれます。

UIのボタンは、プラグインの更新状況に応じてスタイルと機能を変更します。更新がある場合には有効になり、そうでなければ「最新」(Up to date)と表示します。条件付きCSSとdisabled属性によって制御されます。

また、ユーザー体験を向上させるため、サイトの取得時にisLoadingを使用して条件付きで読み込み中のテキストを追加しましょう。

{isLoading && (
    <div className="loading">
        <p>読み込み中です…</p>
    </div>
)}
特定のプラグインを使用しているサイト一覧と、個別または一括更新するためのボタン
特定のプラグインを使用しているサイト一覧と、個別または一括更新するためのボタン

Kinsta APIでプラグインを更新する

以上で重要な詳細情報を持つサイトを取得し、プラグインの管理を行う準備が整いました。最終的な目標は、Kinsta APIを使って複数のサイトにまたがって、プラグインを簡単に更新すること。最後に更新の実行と進捗状況の追跡を実装していきます。

プラグイン更新のトリガー

一覧表示されるサイトにはそれぞれボタンが用意されています。更新可能かどうかを反映する仕様になっており、最新バージョンがある場合は、ボタンをクリックすると、updatePluginがトリガーされます。

<button>
    className={`sm-btn ${
        site.updateAvailable !== 'available' ? 'disabled-btn' : ''
    }`}
    disabled={site.updateAvailable !== 'available'}
    onClick={() =>
        updatePlugin(site.env_id, site.updateVersion)
    }
>
    {site.updateAvailable === 'available'
        ? `Update to v.${site.updateVersion}`
        : 'Up to date'}
</button>

onClickハンドラは、サイトの環境 IDとプラグインの最新バージョン(updateVersion)でupdatePluginを呼び出します。この関数は、プラグインを更新するためにKinsta APIにPUTリクエストを送信します。

const updatePlugin = async (envId, pluginVersion) => {
    const resp = await fetch(`${KinstaAPIUrl}/sites/environments/${envId}/plugins`, {
        method: 'PUT',
        headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
        },
        body: JSON.stringify({
            name: pluginName,
            update_version: pluginVersion,
        }),
    });

    const data = await resp.json();
    // さらに処理
};

更新の進行状況を追跡する

更新が開始されたら、その進行状況を監視することも重要です。更新が始まると、Kinsta APIが以下のようなレスポンスを返します。

{
  "operation_id": "wp-plugin:update-54fb80af-576c-4fdc-ba4f-b596c83f15a1",
  "message": "WordPressプラグインを更新中",
  "status": 202
}

operation_idが、操作エンドポイントを介して更新ステータスを追跡します。operation_idを因数として、このAPIリクエストを行う関数を作成します。

// プラグインの更新ステータスを確認
const checkPluginUpdateStatus = async (operationId) => {
    const resp = await fetch(`${KinstaAPIUrl}/operations/${operationId}`, {
        method: 'GET',
        headers: {
            Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
        },
    });
    const data = await resp.json();
    return data.status;
};

updatePlugin内でif文を使用して 、最初の更新リクエスト(status)が202であるかをチェックします。202であれば5秒(5,000ミリ秒)ごとにcheckPluginUpdateStatusを呼び出す間隔を設定します。

定期的に更新の進行状況を確認し、更新が完了するとfetchSitesを呼び出してサイト一覧を更新します。途中でエラーが発生した場合は、コンソールにログが記録されます。

if (data.status === 202) {
    const interval = setInterval(() => {
        checkPluginUpdateStatus(data.operation_id)
            .then((status) => {
                console.log(status);
                if (status === 200) {
                    clearInterval(interval);
                    fetchSites();
                }
            })
            .catch((error) => {
                // プロミスの解決中に発生したエラーを処理
                console.error('Error:', error);
            });
    }, 5000);
}

操作の進行状況を表示する

この時点ではすべて正常に動作していますが、操作の進捗が確認できるとなお便利です。showStatusBarを使用して、操作が進行中に表示され、操作が完了すると消えるステータスバーを実装することができます。

const [showStatusBar, setShowStatusBar] = useState(false);

showStatusBartrueの場合、画面上部に更新中であることを示すステータスバーが表示されます。このステータスバーは画面上部に固定される仕様です。

{showStatusBar && (
    <div className="status-bar">
        <p>WordPressプラグインを更新中…</p>
    </div>
)}

これで、updatePlugin関数のif文を調整し、更新状況に基づいてshowStatusBartrueまたはfalseに設定することができます。

if (data.status === 202) {
    setShowStatusBar(true);
    const interval = setInterval(() => {
        checkPluginUpdateStatus(data.operation_id)
            .then((status) => {
                console.log(status);
                if (status === 200) {
                    setShowStatusBar(false);
                    clearInterval(interval);
                    fetchSites();
                }
            })
            .catch((error) => {
                // プロミスの解決中に発生したエラーを処理
                console.error('Error:', error);
            });
    }, 5000);
}

このアプローチにより、プラグインの更新ステータスを確実に知ることができ、ツール全体の使い勝手が向上します。

Kinsta APIを使用して複数のサイトでプラグインを更新する

このツールの主な特徴は、MyKinstaアカウントの複数のサイトで使用されているプラグインを一括で更新する点にあります。これは、1つのサイトでプラグインを更新するために実装された機能と似ています。

更新が必要な特定のプラグインをインストールしているサイトを含むsitesの状態をループします。更新が必要なサイトごとに、プラグインを更新するためのAPIリクエストが行われ、その後、操作ステータスを追跡します。

// すべてのプラグインを更新
const updateAllPlugins = async () => {
    sites.map(async (site) => {
        if (site.updateAvailable === 'available') {
            const environmentId = site.env_id;
            const resp = await fetch(
                `${KinstaAPIUrl}/sites/environments/${environmentId}/plugins`,
                {
                    method: 'PUT',
                    headers: {
                        'Content-Type': 'application/json',
                        Authorization: `Bearer ${process.env.REACT_APP_KINSTA_API_KEY}`,
                    },
                    body: JSON.stringify({
                        name: pluginName,
                        update_version: site.updateVersion,
                    }),
                }
            );
            const data = await resp.json();
            if (data.status === 202) {
                setShowStatusBar(true);
                const interval = setInterval(() => {
                    checkPluginUpdateStatus(data.operation_id)
                        .then((status) => {
                            console.log(status);
                            if (status === 200) {
                                setShowStatusBar(false);
                                clearInterval(interval);
                                fetchSites();
                            }
                        })
                        .catch((error) => {
                            // プロミスの解決中に発生したエラーを処理
                            console.error('Error:', error);
                        });
                }, 5000);
            }
        }
    });
};

この機能は、「すべてを更新」(Update All)ボタンに接続されています。また操作性を考慮し、ボタンにはプラグインが更新されるバージョン番号を表示します。

<button> className="sm-btn" onClick={updateAllPlugins}>
    すべてのサイトをv.に更新する
    {
        sites.find((site) => site.updateVersion !== null)
            ?.updateVersion
    }
</button>

さらに、このボタンを条件付きでレンダリングし、複数のサイトでプラグインの更新が必要な場合にのみ表示されます。すべてのサイトが最新の状態である場合は、その旨を伝えるメッセージを表示します。

<div className="list-title">
    <h3>Sites with {pluginName} plugin</h3>
    {sites.filter((site) => site.updateAvailable === 'available')
        .length > 1 && (
        <button className="sm-btn" onClick={updateAllPlugins}>
            すべてのサイトをv.に更新する
            {
                sites.find((site) => site.updateVersion !== null)
                    ?.updateVersion
            }
        </button>
    )}
    {sites.every((site) => site.updateAvailable !== 'available') && (
        <p>すべてのサイトが最新の状態です</p>
    )}
</div>

この実装により、MyKinstaの企業アカウントで管理する複数のサイトで、プラグインを簡単に更新できるように。日々の作業効率が高まり、すべてのサイトでプラグインを簡単に最新の状態に維持することができます。

React静的サイトをKinstaに無料でデプロイする

今回のアプリケーションのデプロイには、Kinstaの静的サイトホスティングを使用しています。実際には、独自のネットワーク内からこのReactアプリを実行するか、セキュリティ強化のためツールに認証手段を追加した後にデプロイします。

任意のGitサービス(BitbucketGitHub、またはGitLab)にコードをプッシュすることで、静的サイトホスティングで、create-react-appで作成したReactアプリケーションを静的サイトとして無料でホストすることができます。

リポジトリの準備が整ったら、以下の手順に従って静的サイトをKinstaにデプロイすることができます。

  1. ログイン、またはアカウントを作成してMyKinstaを開く
  2. GitサービスでKinstaを認証
  3. 左サイドバーの「静的サイト」を選択し「サイトの追加」をクリック
  4. デプロイしたいリポジトリとブランチを選択
  5. サイトに一意の名前を割り当てる
  6. 以下の形式でビルド設定を追加
    • ビルドコマンド:npm run build
    • Nodeバージョン:18.16.0
    • 公開ディレクトリ:build
  7. サイトを作成」をクリック

以上で完了です。数秒以内にサイトのデプロイが完了し、デプロイが完了したサイトにアクセスするためのリンクが表示されます。必要に応じて、独自ドメインSSL証明書の設定も可能です。

また、静的サイトホスティングの代わりに、Kinstaのアプリケーションホスティングで静的サイトをデプロイすることもできます。スケーラビリティ、Dockerfileを使ったデプロイメントのカスタマイズ、リアルタイムおよび過去のデータを網羅した包括的な分析機能など、より豊富な機能をご利用いただけます。

まとめ

Kinsta APIは、今回ご紹介したツールの構築以外にも、様々な可能性をもたらします。例えば、プラグインが古くなるとSlackで通知を受け取ることができる独自Slackbotの作成など。この統合によりワークフローが大幅に合理化し、常に最新情報を取得しながら、先手を打つことができます。

また、Kinsta APIにはすでにこのためのエンドポイントが導入されているため、テーマ更新用に同様のツールを開発することもできます。

Kinstaは、常にお客様からのご意見を大切にし、機能の導入や改善に継続的に取り組んでいます。皆さまからのご意見は、機能開発の優先順位付けの重要な指標です。ご要望の機能やエンドポイントがありましたら、ぜひお気軽にご意見をお寄せください

Kinsta APIをご利用ですか?機能の導入や改善に関して、ご要望やご意見がございましたらぜひお聞かせください。

Joel Olawanle Kinsta

Kinstaでテクニカルエディターとして働くフロントエンド開発者。オープンソースをこよなく愛する講師でもあり、JavaScriptとそのフレームワークを中心に200件以上の技術記事を執筆している。