Crowi
プラグイン

プラグインアーキテクチャ

Crowi 2.0 のプラグインの仕組みと設計思想 (RFC-0001 / RFC-0002)

Crowi 2.0 は、ファイルストレージ・全文検索・レンダラ・認証・通知といった 本来コアと独立した関心事を、独立配布可能な プラグイン として切り出した アーキテクチャを採用しています。設計の経緯は RFC-0001 (プラグインアーキテクチャ) と RFC-0002 (レンダラプラグインアーキテクチャ) を参照してください。

設計の原則

Crowi のプラグインモデルは、次のいくつかの原則の上に成り立っています。

  • バッテリー同梱: プラグインを 1 つも追加しなくても、初期状態の Crowi は完結した Wiki として動作します。ローカルファイルストレージ、MongoDB ベースの検索、メール + パスワード認証、アプリ内通知がデフォルトで 有効です。
  • npm パッケージとして配布: プラグインは特別な形式ではなく、通常の npm パッケージです。グローバルなプラグインレジストリや curl パイプ式の インストーラはありません。
  • plugin-free な api: @crowi/api は SDK (@crowi/plugin-api) と コアのみを同梱し、ドライバは一切含みません。プラグインは別の runner プロジェクト (後述) が所有するため、プラグインの追加・削除 はそのプロジェクトの依存と設定を変えるだけで完結し、api のイメージを 焼き直す必要はありません。
  • v2.x の間は API 安定: @crowi/plugin-api@2.x に対して書かれた プラグインは、v2 系のすべてのマイナーバージョンで動作し続けます。

Tip: プラグインの実行はサンドボックス化されていません。プラグインは サーバと同じ Node 権限で動作します。信頼できる発行元のパッケージのみを 導入してください。

プラグインの種類

Crowi のプラグインは、貢献する拡張ポイントによっておおよそ次の種類に 分かれます。1 つのプラグインが複数の拡張ポイントを実装することもできます。

種類拡張ポイント
ストレージregisterStorage@crowi/plugin-storage-local@crowi/plugin-storage-aws-s3
検索registerSearch@crowi/plugin-search-mongo@crowi/plugin-search-elasticsearch@crowi/plugin-search-opensearch
メールregisterMailer@crowi/plugin-mail-smtp@crowi/plugin-mail-aws-ses@crowi/plugin-mail-resend
レンダラregisterRenderer@crowi/plugin-renderer-emoji / -katex / -plantuml / -crowi-legacy
認証registerAuth(外部 OAuth プロバイダ向け、今後拡充予定)
通知 / 連携registerNotifier / registerRoutes@crowi/plugin-slack (Slack 連携 — ページリンクの unfurl)
ベース (設定のみ)(register なし)@crowi/plugin-aws — 後続の AWS 系プラグインに資格情報を共有
ルートregisterRoutesプラグインの HTTP ルート (受信 Webhook / @action ボタン / OAuth コールバック)。@crowi/plugin-slack の Slack Events API 受信口などに使われます

ストレージ・検索・メールは、それぞれ「ドライバ」を crowi.config.jsonstorage.driver / search.driver / mail.driver で選択する形に なっています。複数のドライバプラグインを導入しておき、設定でどれを使うか 切り替えられます。

ファーストパーティプラグイン

Crowi モノレポでメンテナンスされているプラグインは次のとおりです。

レジストリドライバ
ストレージlocal (@crowi/plugin-storage-local)、aws-s3 (@crowi/plugin-storage-aws-s3)
検索mongo (@crowi/plugin-search-mongo)、elasticsearch (@crowi/plugin-search-elasticsearch)、opensearch (@crowi/plugin-search-opensearch)
メールsmtp (@crowi/plugin-mail-smtp)、ses (@crowi/plugin-mail-aws-ses)、resend (@crowi/plugin-mail-resend)
レンダラ@crowi/plugin-renderer-plantuml / -emoji / -katex / -crowi-legacy
連携@crowi/plugin-slack (Slack でのページリンク unfurl — 設定は Slack 連携)
共有ベース@crowi/plugin-aws (AWS 系プラグインが要求する設定のみの依存)

すべてのプラグインが対象とするプラグイン SDK は @crowi/plugin-api です。

暗黙のデフォルトプラグイン

3 つのドライバは crowi.config.json に列挙しなくても 毎回 読み込ま れるため、素のインストールではプラグイン設定が一切不要です。

  • @crowi/plugin-storage-local — ローカルファイルストレージ
  • @crowi/plugin-search-mongo — MongoDB の $regex 検索 (デフォルト の検索ドライバ。Elasticsearch / OpenSearch はオプトイン)
  • @crowi/plugin-mail-smtp — SMTP メール

追加のプラグインを列挙して *.driver を切り替えると、これらのデフォルト の上に選択肢が積み増されます。

CrowiPlugin インターフェース

すべてのプラグインは、@crowi/plugin-api が公開する CrowiPlugin 型を 満たすオブジェクトを デフォルトエクスポート します。ランタイムは 起動時に await import('<plugin-name>') でこれを読み込みます。

主なフィールドは次のとおりです。

フィールド役割
namenpm パッケージ名。設定行 (plugin:<name>:*) とページメタデータの名前空間を兼ねます。
versionプラグイン自身のバージョン (npm パッケージの semver)。
requires実行時に必要な他プラグインの npm 名。依存グラフを解決して先に読み込みます。
configSchemaプラグインのグローバル設定値を表す Zod スキーマ。管理 UI がこれを辿って設定フォームを自動生成します。
pageMetadataSchemaページ単位のメタデータスキーマ。指定するとページ編集 UI にプラグイン用の入力欄が出ます。
adminPlacement管理サイドバーでの表示位置 (section / label / icon)。
register*各拡張ポイントへの登録コールバック (storage / search / auth / notifier / renderer / routes)。実装するものだけ宣言します。
onInstall / onUninstall初回有効化 / 削除時のフック。v1 設定の移行などに使います。
reconfigure管理 UI から設定が変わったときに呼ばれ、キャッシュした接続などを更新します。

register* コールバックはすべて任意です。ストレージだけ提供するプラグイン なら registerStorage だけ実装すれば十分です。

PluginContext

register* コールバックには PluginContext が渡されます。これが、 プラグインがコアの状態に触れる唯一の窓口です。プラグインは @crowi/server を直接 import せず、必ず context 経由でやり取りします。

PluginContext が提供するもの:

  • config<T>() — このプラグインの型付き設定 (configSchema でパース) を読む
  • dependencyConfig<T>(name)requires に宣言した依存プラグインの設定を読む
  • setConfig(key, value) — 設定フィールドを 1 つ書き込む
  • pageMetadata — このプラグインの名前空間に閉じたページメタデータの読み書き
  • model(name) — コアの Mongoose モデル (Page / User / Comment …) にアクセス
  • cryptoKeyProvider を使った対称 暗号化 / 復号
  • log — プラグイン名で自動プレフィックスされる構造化ロガー

dependencyConfig は、@crowi/plugin-aws のような資格情報共有プラグインの ために用意されています。ベースプラグインが region / accessKeyId / secretAccessKey を持ち、@crowi/plugin-storage-aws-s3 などの依存先が 自前のスキーマで重複定義せずにそれを読み取れます。

runner プロジェクトと @crowi/runner ライブラリ

「runner」という語が指す 2 つのものを混同しないでください。

  • runner プロジェクト は、プラグインを 所有 する小さな api ランタイム プロジェクトです。@crowi/api と選んだ @crowi/plugin-* を依存に持つ package.jsoncrowi.config.json.env からなる通常の npm プロジェクトです。モノレポにはリファレンス実装 apps/crowi-runner (パッケージ名 @crowi/runner-app、private) が同梱されており、 セルフホストする運用者はこれと等価なものを自前で用意します。
  • @crowi/runner (-app なし) は packages/runner にある内部 ライブラリです。これは 解決エンジン で、crowi.config.json を読み、 createRequire で列挙されたプラグインを runner プロジェクトの node_modules/ から読み込みます。

プラグイン解決

api パッケージ自身はプラグインを依存に持ちません。プラグインの npm 名を 解決するのは @crowi/runner ライブラリの役割で、api を起動する runner プロジェクトに対して解決を行います。

  1. crowi.config.json を読み、plugins 配列に列挙された名前を取得します (暗黙のデフォルトプラグインを先頭に追加します)。
  2. createRequire(<projectDir>/package.json) (ここで projectDir は runner プロジェクト) を使って、その npm 名を runner プロジェクトの node_modules/ から解決します。
  3. 各プラグインの requires を BFS で辿り、推移的な依存も読み込みます。
  4. 結果として、解決済みの CrowiPlugin インスタンス一覧が api の起動 シーケンスに渡されます。

つまり、運用者がプラグインを 1 つ追加するには次の 2 つを行うだけです。

  • runner プロジェクトの package.json の依存にプラグインを加える
  • crowi.config.jsonplugins にそのパッケージ名を列挙する (必要なら 対応する *.driver も切り替える)

api の再ビルドは伴いません。具体的な手順は プラグインの導入と設定 を参照してください。

起動シーケンスでの位置づけ

プラグインは、設定とモデルの準備が終わった後、レガシーな mailer / slack 初期化の前に起動します。レンダラのコアパイプライン (TOC / wikilink / メンション / コードブロック) は プラグインより先に 登録されるため、 レンダラプラグインはコアの登録の上に追記する形になります。

関連ページ

On this page