Vercel&microCMS環境で記事コンテンツ内のリンク切れチェックを自動化する方法

2024/07/29に公開

記事コンテンツを運用していると、リンク切れが発生することがあります。リンク切れが発生すると、エンドユーザーが閲覧する際に不便を感じるだけでなく、SEOにも悪影響を及ぼす可能性があります。

自身が管理するドメイン内のリンク切れは、Google Search Consoleなどを利用して確認できます。一方で、外部サイトへのリンク切れは管理が難しくなります。とくにアフィリエイトなどの広告URLは、運用中にリンク切れが発生する可能性が高いため、手動管理の運用は骨が折れます。

今回は、上記のようなケースにVercelとmicroCMSを利用した環境で記事コンテンツのリンク切れチェックを自動で行う方法を紹介します。

記事内で記載する内容のGitHubリポジトリは以下になります。
https://github.com/Kazuki-tam/deadlink-cron-next

目標設定

CMSで管理する記事コンテンツのリンク切れを自動でチェックする方法はいくつか考えられますが、この記事ではAPI経由でコンテンツ取得ができるmicroCMSの強みを利用し、リンク切れチェックを行うアプローチを取りたいと思います。

  • 定期的に記事コンテンツのリンク切れチェックを行う
  • チェック対象はCMSで管理している記事コンテンツに限定する
  • リンク切れが発生した場合はメールで通知する(GoogleアカウントのGmailで送信)

技術スタック

Vercelにコンテンツをデプロイし、microCMSで記事コンテンツ管理していることを想定します。リンク切れチェックはVercelのサーバーレス関数を利用して実装します。さらにVercelで提供されているCron Jobの仕組みと組み合わせることで、定期的にリンク切れチェックを行うことが可能となります。

利用ライブラリ

今回の実装にあたり、メインで利用しているライブラリは以下になります。

microCMSの設定

記事コンテンツはリスト形式で管理されており、記事の登録は以下のようになっていることを想定します。

{
    "id": "test-01",
    "createdAt": "2022-11-10T08:03:33.441Z",
    "updatedAt": "2022-11-10T15:23:10.089Z",
    "publishedAt": "2022-11-10T08:03:33.441Z",
    "revisedAt": "2022-11-10T15:23:10.089Z",
    "title": "これはテスト投稿です",
    "content": "<h2 id=\"he14a109d56\">Welcome to the Pure Liquid documentation!🎉</h2><p>ブログテンプレートからAPIを作成しました。<br>おつかれさまでした🎉<br></p><h2 id=\"hf45076424a\">APIプレビューを試そう🚀</h2><p>最初に「APIプレビュー」をしてみましょう。<br>入稿したコンテンツはAPI経由で取得し、Viewに繋ぎ込みます。<br>APIプレビューでは実際のAPIレスポンスを確認でき、あなたの開発を加速させます。<br><br>👇まずはここをクリックします。",
    "eyecatch": {
        "url": "https://images.microcms-assets.io/assets/test/blog-template.png",
        "height": 630,
        "width": 1200
    },
    "category": {
        "id": "category-01",
        "createdAt": "2022-11-10T08:03:32.159Z",
        "updatedAt": "2022-11-10T08:03:32.159Z",
        "publishedAt": "2022-11-10T08:03:32.159Z",
        "revisedAt": "2022-11-10T08:03:32.159Z",
        "name": "カテゴリー1",
    }
}

記事のcontentにはHTML形式の記事本文が格納されており、この中に任意のリンクが含まれていることを想定します。

スクショ
赤枠がcontent部分

コンテンツリストのエンドポイントは以下のようになっていることを想定します。APIプレビューで対象のエンドポイントを確認してください。

https://<service_domain>.microcms.io/api/v1/blogs

service_domainの箇所は後ほど環境変数として設定します。APIキーも同じく後で必要になるため、手元にひかえておいてください。

Gmailの設定

NodemailerでGmailを利用するためには、以下の設定が必要です。

  • アプリパスワードの作成

https://nodemailer.com/usage/using-gmail/

アプリパスワードは以下から作成できます。取得したパスワードは後ほど環境変数に設定します。

https://myaccount.google.com/u/1/apppasswords

開発環境のセットアップ

ここではpnpmでNext.jsプロジェクトを作成します。お好みのパッケージマネージャーを利用してください。

$ pnpm create-next-app

プロジェクトのセットアップが完了したら、以下のコマンドでmicroCMS JavaScript SDKとNodemaiilerをインストールします。

$ pnpm add microcms-js-sdk nodemailer

microCMS JavaScript SDKは記事の全件取得に利用し、Nodemailerはリンク切れが発生した場合のメール通知に利用します。

環境変数の設定

.env.localファイルを作成し、以下の環境変数を設定します。

# microCMSのプロジェクト情報
MICROCMS_API_KEY=YOUR_MICROCMS_API_KEY
MICROCMS_SERVICE_DOMAIN=YOUR_MICROCMS_SERVICE_DOMAIN

# 通知用のGmailアカウント
GMAIL_USER=YOUR_GMAIL_USER
GMAIL_APP_PASSWORD=YOUR_GMAIL_APP_PASSWORD

# 通知先のメールアドレス
NOTIFICATION_EMAIL=YOUR_NOTIFICATION_EMAIL

# Vercel Cron Jobsのシークレットキー
CRON_SECRET=YOUR_CRON_SECRET
  • MICROCMS_API_KEY: microCMSのAPIキー
  • MICROCMS_SERVICE_DOMAIN: microCMSのサービスドメイン
  • GMAIL_USER: 送信元のGmailアカウント
  • GMAIL_APP_PASSWORD: Gmailのアプリパスワード
  • NOTIFICATION_EMAIL: 通知先のメールアドレス
  • CRON_SECRET: Vercel Cron Jobsの実行でAPI保護に利用するシークレットキー

CRON_SECRETは以下のような生成ツールで作成すると便利です。
https://1password.com/password-generator/

microCMS記事の取得処理

src/app/_libs/microcms.tsにmicroCMS SDKの初期化処理を記述します。

https://github.com/Kazuki-tam/deadlink-cron-next/blob/main/src/app/_libs/microcms.ts

fetchAllBlogs を作成し、記事一覧を取得する処理を記述します。

Nodemailerでのメール送信処理

NodemailerでGmailを利用してメール送信する処理を記述します。

https://github.com/Kazuki-tam/deadlink-cron-next/blob/main/src/app/_libs/nodemailer.ts

Cron Jobsの処理

VercelのCron Jobsで定期実行する処理を記述します。以下フォルダー構成でファイルを作成します。

src/app/api/cron/link-checker/route.ts

細かい処理内容は割愛しますが、以下のような処理を行っています。

  1. リクエストの信頼性を確認する(事前に作成したCRON_SECRETと一致するか検証)
  2. microCMSから記事一覧を取得
  3. 記事のcontentからリンクを抽出
  4. リンク切れチェックを行い、リンク切れが発生した場合はメール通知
  5. レスポンスを返却

https://github.com/Kazuki-tam/deadlink-cron-next/blob/main/src/app/api/cron/link-checker/route.ts

CRON_SECRETを環境変数で設定すると、Vercel Cron JobsがAPIを実行する際に設定したトークンをリクエストヘッダーに付与してくれます。 API側では以下のようにauthorizationヘッダーを取得し、CRON_SECRETと一致するか検証を行います。

import type { NextRequest } from 'next/server';
 
export function GET(request: NextRequest) {
  const authHeader = request.headers.get('authorization');
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return new Response('Unauthorized', {
      status: 401,
    });
  }
 
  return Response.json({ success: true });
}

これによりAPIの保護を行うことができるため、必ず設定しましょう。

https://vercel.com/docs/cron-jobs/manage-cron-jobs#securing-cron-jobs

Cron Jobsの設定

プロジェクトルートにverce.jsonファイルを作成し、以下のように記述するだけで設定できます。pathにはCron Jobsのエンドポイントを指定し、scheduleには実行スケジュールを設定します。

{
  "crons": [
    {
      "path": "/api/cron/link-checker",
      "schedule": "0 1 * * *"
    }
  ]
}

https://vercel.com/docs/cron-jobs

ローカルでの動作確認

以下のコマンドでローカルでプロジェクトを起動し、リンクチェック処理が正常に動作するか確認します。

$ pnpm dev

起動した状態で、http://localhost:3000/api/cron/link-checkerにアクセスし、リンク切れチェックが正常に動作するか確認します。
単純にURLを叩くだけではトークンの検証が通らないため、リクエストヘッダーにCRON_SECRETで設定した値をセットしてリクエストを送信します。

記事コンテンツ内にリンク切れが発生している場合、以下のようにメール通知が受信できます。

メール通知

Vercelへのデプロイ

ローカルでの動作確認が完了したら、Vercelにデプロイします。デプロイ方法は割愛します。管理画面ベースで簡単にGitHubリポジトリと連携してデプロイできます。必要に応じて公式ドキュメントを参考にしてください。

https://vercel.com/docs/deployments/overview

まとめ

今回採用したアプローチではAPIベースで記事コンテンツの品質監査を行えるため、スクレイピングなどの手法よりもより効率よく、信頼性の高い処理が可能となります。コンテンツのビルドプロセスを間に挟まなくていいのはかなりの利点かなと思います。

一方でこのアプローチは処理内容がAPIのスキーマに依存するため、APIの仕様変更には注意が必要です。また、ビルドプロセスでフロントのHTML構造が大きく変わる場合やコードベースで管理している箇所はチェックし切れないので、注意が必要です。

この記事ではメール送信を行っていますが、運用体制的に可能であればSlack通知などを検討いただいた方が有用かもしれません。

Vercel Cron Jobsを利用することで定期的なプロジェクトのメンテナンスや監視を自動化できます。今回の用途以外にもさまざまな活用方法が考えられるため、ぜひ検討してみてください。

Discussion