プラグインアーキテクチャ
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.json
の storage.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>') でこれを読み込みます。
主なフィールドは次のとおりです。
| フィールド | 役割 |
|---|---|
name | npm パッケージ名。設定行 (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 …) にアクセスcrypto—KeyProviderを使った対称 暗号化 / 復号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.json、crowi.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
プロジェクトに対して解決を行います。
crowi.config.jsonを読み、plugins配列に列挙された名前を取得します (暗黙のデフォルトプラグインを先頭に追加します)。createRequire(<projectDir>/package.json)(ここでprojectDirは runner プロジェクト) を使って、その npm 名を runner プロジェクトのnode_modules/から解決します。- 各プラグインの
requiresを BFS で辿り、推移的な依存も読み込みます。 - 結果として、解決済みの
CrowiPluginインスタンス一覧が api の起動 シーケンスに渡されます。
つまり、運用者がプラグインを 1 つ追加するには次の 2 つを行うだけです。
- runner プロジェクトの
package.jsonの依存にプラグインを加える crowi.config.jsonのpluginsにそのパッケージ名を列挙する (必要なら 対応する*.driverも切り替える)
api の再ビルドは伴いません。具体的な手順は プラグインの導入と設定 を参照してください。
起動シーケンスでの位置づけ
プラグインは、設定とモデルの準備が終わった後、レガシーな mailer / slack 初期化の前に起動します。レンダラのコアパイプライン (TOC / wikilink / メンション / コードブロック) は プラグインより先に 登録されるため、 レンダラプラグインはコアの登録の上に追記する形になります。
関連ページ
- プラグインの導入と設定 → プラグインの導入と設定
- レンダラプラグインの一覧 → レンダラプラグイン
- 独自プラグインの作り方 → プラグイン開発
- アーキテクチャ全体像 → アーキテクチャ