Crowi
運用

v1 からの移行

Crowi v1 から v2 (Reignite) へのアップグレードと、データ互換性の現状

Caution: Crowi 2.0 — コードネーム Reignite — は現在 alpha 開発中 です。安定版の v2 はまだリリースされておらず、本番環境の v1 から v2 へ移行することは現時点では非推奨です。検証目的でのみ利用して ください。最新の進捗は TODO.md を参照してください。

このページは、Crowi v1 から v2 へ移行する際のデータ互換性の考え方と、 現状で利用できる移行パスを説明します。

v2 (Reignite) とは

Crowi v2 はフロントエンドを全面的にリビルドしたバージョンです。

  • v1: Express + Swig (テンプレート) + jQuery のモノリス
  • v2: Hono API (@hono/zod-openapi) + Next.js 16 (App Router) + React 19

UI とアプリケーション層は作り直されていますが、データ層 (MongoDB の データ形状) は v1 と互換性が保たれています。ページ、リビジョン、 ユーザー、コメントなどのドキュメント構造は v1 のものをそのまま読み込め ます。つまり、既存の Wiki のデータは v2 へ持ち越せる設計です。

データ互換性の現状

領域互換性
MongoDB のドキュメント形状v1 と互換。既存データを v2 がそのまま読める
添付ファイルストレージドライバ経由でアクセス。保存場所は v1 と同様
パスワードレガシーのハッシュ方式に PASSWORD_SEED でフォールバック検証
設定 (Config)v1 の Config も読み込める。機密値の暗号化は v2 の新機能 (任意)

Note: v2 では機密 Config (OAuth secret、SMTP パスワードなど) を AES-256-GCM で暗号化する仕組みが入りました。v1 由来の平文の設定行は そのまま読み込めますが、暗号化を有効にした場合は管理画面の crypto 機能で再暗号化できます。詳細は 機密情報の暗号化 を 参照してください。

移行パスの現状

データ形状は互換ですが、v1 → v2 の 移行パスそのものはまだ整備中です。

  • v1 の MongoDB をそのまま v2 で起動させる手順は技術的には可能ですが、 公式に検証・ドキュメント化された移行手順はまだ確立していません。
  • 一部のデータには、v2 の新機能に合わせた変換が必要なものがあります (後述の wikilink 移行など)。
  • 安定版リリースに向けて、移行手順とツールは順次整備されます。

このため、現時点では 本番データでの移行は行わず、v1 データのコピーを 使った検証環境での試用にとどめることを強くおすすめします。

移行フレームワーク (crowi-admin migrate)

v1 → v2 のデータ移行は RFC-0008 の移行フレームワークで実行され、 crowi-admin migrate plan | apply | status | list という名前空間として 提供されます。crowi-admin は MongoDB に直接接続するため、@crowi/api が インストールされたランナープロジェクトのディレクトリから実行します。 モノレポでは pnpm migrate ラッパーが同じ CLI を実行します。

公式 Docker イメージでは crowi-admin がランナープロジェクトのツリー (/app がルート) に同梱されているため、コンテナ内で実行します。

# 起動中のコンテナ内で未適用の preflight 移行を適用
docker compose run --rm --entrypoint node api \
  node_modules/@crowi/admin-cli/dist/bin.js migrate apply

これは下記の preflight block ポリシーで特に重要です。デプロイで未適用の blocking preflight 移行が入ると api は起動を拒否するため、イメージ内から この方法で解除します。

移行は 2 つのレイヤーに分かれています。

  • boot 移行は、api 起動時に自動で適用されます。
  • preflight 移行は重い / 破壊的になりうるため、自動では適用されません。 起動のたびに probe され、未適用だったときの挙動は移行ごとの severity (重大度、下記参照) で決まります。blocking の移行は 起動を拒否 し (デフォルトの block ポリシー)、cosmetic の移行は 警告を出して起動を 継続 します。preflight 移行はメンテナンスウィンドウで crowi-admin migrate apply を使って明示的に適用します。

各移行は、未適用の preflight 移行が起動にどう影響するかを決める severity を 持ちます。

  • blocking — インデックス・データ整合性に関わる移行 (現状は user-unique-prepare のみ)。未適用だとデフォルトの block ポリシー下で api は起動を拒否します。準備されていないデータに対して起動すると、一意 インデックスの構築時に E11000 で失敗する恐れがあるためです。 migrate plan / migrate list では [blocking] タグが付きます。
  • cosmetic — 本文表示のみに関わる移行 (wikilink-format など)。未適用 でも api は 起動し、警告ログを出すだけです (block ポリシー下でも 同様)。cosmetic 移行はライブのコーパスを再スキャンするため、古い記法で 新しいページが書かれると再び未適用扱いになり続けます。これを起動拒否の 対象にするとクラスタがデッドロックします。migrate plan / migrate list では [cosmetic] タグが付きます。
# 未適用の移行をプレビュー (デフォルトは preflight)
crowi-admin migrate plan
# または、モノレポでは:
pnpm migrate plan

# 未適用の preflight 移行を適用
crowi-admin migrate apply

# 未適用の移行件数を表示
crowi-admin migrate status

Tip: block ポリシーが効くのは blocking の移行だけです。そのため 本番ではデフォルトの block のままで問題ありません。cosmetic の移行 (表示のみに関わるもの) はポリシーに関係なく起動を拒否せず、起動を拒否 するのはデータ整合性に関わる blocking の移行だけだからです。blocking の移行が未適用のまま起動したい場合 (たとえば単一インスタンスでの調査用) は、MIGRATION_PREFLIGHT_UNAPPLIED_POLICY=warn を設定すると、その起動拒否 を警告に格下げできます。(以前のドキュメントは「中途半端に移行された データベースがリクエストを処理しないよう、常に block のままに」と案内 していましたが、移行ごとの severity の導入により、この一律のルールは 当てはまらなくなりました。cosmetic 移行はそもそもデータ整合性のリスク ではありません。)

マルチインスタンスの注意: MIGRATION_PREFLIGHT_UNAPPLIED_POLICY (環境変数) は、管理画面の Config (migration.preflightUnappliedPolicy) より優先されます。環境変数を持つレプリカと Config だけに頼るレプリカが 混在すると、同一のデータベースに対して 異なる ポリシーが解決される 可能性があります。クラスタ全体で一致させるため、環境変数を 全レプリカに 一貫して 設定してください (例: Kubernetes の ConfigMap を全 Pod に注入)。

v2 alpha 時点で登録されている移行は以下のとおりです。

IDレイヤーseverity内容
page-status-defaultbootpage.status のバックフィル (RFC-0004)
revisions-schema-unifybootリビジョンスキーマの統一 (type:snapshot のバックフィル)
wikilink-formatpreflightcosmeticwikilink 記法の変換
user-unique-preparepreflightblockingユーザーの重複排除 (一意インデックスの準備)
relocate-reserved-api-pathspreflightcosmetic/api 予約名前空間からの v1 ページ退避
files-url-to-attachmentspreflightcosmetic本文の v1 ファイル URL (/files/<id>) を v2 形式 (/api/v2/attachments/<id>) へ書き換え
wikilink-html-recoverpreflightcosmetic誤変換された HTML 終了タグ (</font> など) の復旧

(severity は preflight 移行にのみ適用されます。boot 移行は自動適用され probe されないため、起動拒否の severity を持ちません。blockinguser-unique-prepare のみで、残りは cosmetic です。cosmetic が未適用でも 警告は出ますが起動は拒否されません。)

boot 移行 (page-status-default / revisions-schema-unify) は api 起動時に 自動適用されるので、運用者の操作は不要です。以下では、明示的な適用が必要な preflight 移行を中心に説明します。

wikilink-format preflight 移行は、v1 時代の wikilink 記法を v2 の レンダラーに合わせて変換します。

# まず変更内容を確認 (新しいリビジョンは書き込まない)
crowi-admin migrate plan

# この移行だけを適用
crowi-admin migrate apply --id wikilink-format

migrate plan は対象ページのスキャンとレポートだけを行い、実際の書き込みは 行いません。適用前に必ず影響範囲を確認してください。この移行は書き換えの 帰属先となる管理者ユーザーを必要とします — 管理者ユーザーが解決できない 場合は CROWI_MIGRATE_USER=<email> を設定してください。

この移行は コード領域を除外 します — フェンスコードブロックやインライン コードの中にある </…> や wikilink ふうの文字列は誤検知せず、そのまま残します。 また apply はページの updatedAt / lastUpdateUser を保全 します(移行に よる書き換えは保守オペレーションでありユーザー編集ではないため、これらを bump せず、ページ一覧の並び順や「最終更新者」は変わりません)。

Tip: v1 由来の wikilink 記法は、@crowi/plugin-renderer-crowi-legacy プラグインによってレンダリング時にも解釈できます。データ自体を変換 したくない場合はこのプラグインを有効化するという選択肢もあります。 プラグインについては プラグイン概要 を参照して ください。

旧バージョンの wikilink-format 移行には、見出しなどで使われる 非推奨の HTML 終了タグ (</font> </center> </marquee> </blink> </applet>) を v1 の山括弧 wikilink と誤認し、[[/font]] のような wikilink 記法へ書き換えてしまう不具合がありました。これらのタグが KNOWN_HTML_ELEMENTS に登録されていなかったためで、現在は登録済みなので 新たな誤変換は発生しません。一方、既に [[/font]] のように化けた本文は wikilink-format が再実行されないため自動では直りません。

wikilink-html-recover preflight 移行は、化けた [[/<x>]]</x> へ 逆変換しますが、対象はこの 5 つの非推奨タグ (font / center / marquee / blink / applet) だけです。これらは KNOWN_HTML_ELEMENTS に欠けていた唯一の名前であり、誤変換が生み出しえた 終了タグはこれらに限られるためです。それ以外の単一セグメント wikilink は、 たとえ名前が標準 HTML 要素であっても ([[/section]] [[/div]] [[/br]] など) 温存されます — これらは常に既知だったため wikilink-format が書き換えて おらず、本来の wikilink だからです。複数セグメント ([[/foo/bar]])・ alias 付き ([[/font|alias]])・大文字 ([[/Font]]) の形も温存されます。

# 復旧対象を事前確認 (本文は書き換えない)
crowi-admin migrate plan

# この移行だけを適用
crowi-admin migrate apply --id wikilink-html-recover

Caution: [[/font]] が「化けた </font>」なのか「実在する /font ページへの本来の wikilink」なのかは、本文だけからは区別できません。この 移行は、/<x>同名の実ページ (生きている公開ページ) が存在する [[/<x>]] は自動変換せず、migrate plan の結果に一覧として報告します (運用者が個別に判断してください)。一方、同名ページが存在しない [[/font]] (5 つの非推奨タグ) は 逆変換されます — 削除済み / 未作成のページへの dangling リンクも含みます。実際には /font /center /marquee /blink /applet という名前の実ページは非常に稀ですが、もし存在する場合は migrate plan の collision 一覧を確認し、まず dev / stg で検証してから 本番に適用してください。

この移行は冪等です — 逆変換後の </x> は復旧対象の検出にも wikilink-format の変換にも当たらないため、再実行しても二重には作用しません。

Note: コードフェンス (```) やインラインコード (`…`) の中に 書かれた [[/font]] などの記述は、この移行の 対象外 です — 移行の説明として コード例に書かれた [[/font]]</font> へ逆変換されず、そのまま温存されます。 wikilink-format (</…>)・files-url-to-attachments (/files/<id>) と同じ コード除外挙動で、3 つの本文書き換え移行はいずれも同一です。このためコード内に しか対象トークンがないページが「移行未適用 (pending)」と誤検知されることもなく、 preflight + block ポリシー下で起動がブロックされ続けることはありません。

/api 名前空間の移設

v1 は API を /_api/*(アンダースコア付き)で提供していたため、/api/* (アンダースコアなし)は通常の wiki ページパスでした。v2 は /api を リバースプロキシ先のバックエンド(/api/v2/*)用に予約するので、v1 で /api/* 配下に残っているページは行き場を失います — web アプリは予約名前 空間を 404 にし、/api/v2/* にあったページはプロキシに完全に隠れます。

relocate-reserved-api-paths preflight 移行は、該当する全ページを /api-legacy/* へ移します(残りのパスは保持。移設先が既に存在する場合は -N サフィックスを付与)。旧パスにリダイレクトは残しません(予約名前空間内 のため)。/api/* ページが無い wiki では適用対象なしになります。

# 移動対象を事前確認
crowi-admin migrate plan

# この移行だけを適用
crowi-admin migrate apply --id relocate-reserved-api-paths

ユーザーの一意性の準備 (user-unique-prepare)

v1 は username / email の一意性を厳密には強制していませんでした (大文字小文字違いの重複や、削除済みユーザーが元の identity を保持したまま 残るなど)。v2 は username / email大文字小文字を区別しない一意 インデックス を張る (起動時に autoIndex が構築) ため、衝突するデータが 残っているとインデックス構築が E11000 で失敗します。

user-unique-prepare preflight 移行は、インデックスが構築できるように データを整えます (インデックスの構築自体は行いません)。

  • dedup-username — 大文字小文字を無視して衝突する現役ユーザーを 1 人に 統合し、所有権の参照を統合先に付け替えて残りを削除します。
  • dedup-email — email についても同様に統合します。
  • tombstone-deleted — 現役ユーザー (または別の削除済みユーザー) と衝突 する削除済みユーザーを、ランタイムの削除挙動と同じ deleted-<id> 形式の tombstone 名にリネームします。
# 統合 / 削除 / リネーム対象の件数をプレビュー
crowi-admin migrate plan

# この移行だけを適用
crowi-admin migrate apply --id user-unique-prepare

Caution: この移行はユーザーの 統合・削除 を伴います。migrate plan で対象を必ず確認し、適用前に MongoDB のバックアップを取得してください。

検索インデックスの再構築

v1 から移行したデータで全文検索を使う場合、v2 側で検索インデックスを 作り直す必要があります。

crowi-admin rebuild search

詳細は 検索バックエンドのセットアップ を参照して ください。

ストレージの移行

添付ファイルの保存先を変える場合 (例: ローカルから S3 へ) は、 ストレージコピーコマンドを使います。

crowi-admin rebuild storage copy --from local --to s3 --dry-run
crowi-admin rebuild storage copy --from local --to s3

Note: v1 の AWS / S3 認証情報 (旧 upload:aws:* のコア設定) は 引き継がれません@crowi/plugin-aws (および @crowi/plugin-storage-aws-s3) を有効化し、管理画面の Plugins で 認証情報を入力し直してください (プラグイン名前空間に保存され、暗号化 されます)。

ドメイン変更後の本文 URL 一括置換 (replace url)

v1 → v2 で 公開ドメイン (ホスト) を変更した 場合、ページ本文に埋め込まれた 絶対 URL (画像埋め込みやリンク。例: ![shot.png](https://old.example/files/<id>)) は旧ホストのまま残ります。ページ / ファイルの id は引き継がれるため、必要なのは ホスト (URL 接頭辞) の単純な置換 であって id の振り直しではありません。

crowi-admin replace url は、全ページ本文中の リテラル文字列--from から --to へ置換します (正規表現ではなく完全一致なので、/ . ? を含む URL でも安全です)。

# まず置換対象をプレビュー (書き込まない)
crowi-admin replace url --from https://old.example --to https://new.example --dry-run

# 実行 (置換件数を確認するプロンプトが出る)
crowi-admin replace url --from https://old.example --to https://new.example

この置換は エンドユーザーから見えない静かな書き換え として行われます。 ページごとに新しいリビジョンを積みますが、updatedAt / 最終更新者 / 公開範囲 (grant) は変更せず、ウォッチャーへの通知や自動ウォッチも行いません。一覧の 並びや通知が動かないので、ドメイン移行のクリーンアップに適しています。

主なオプション:

オプション説明
--from <s> / --to <s>(必須) 置換元 / 置換先の文字列。安全のため https://old.example のように スキーム付きの完全なオリジン を指定する
--dry-run書き込まずに対象だけレポートする
--include-trashゴミ箱 / 非推奨ページも対象に含める (既定: 公開ページのみ)
--user <email>新リビジョンの作成者として記録するユーザー (既定: 最古の管理者)
--yes確認プロンプトをスキップする (TTY が無い環境では、これが無いと書き込みを拒否)
--force--from にスキームが無い (裸のホスト) 場合でも実行する

Caution: --fromold.example のような スキームの無い裸のホスト を渡すと、old.example.com のような「接頭辞が一致するより長いホスト」まで 壊す恐れがあります。CLI は既定でこれを拒否し、--force が必要です。可能な 限り https://old.example のように完全なオリジンを指定してください。

置換後、ページのレンダリング済み HTML は新リビジョンとして再生成されるため 最新です。一方 検索インデックスは別途再構築が必要 です (crowi-admin rebuild search)。バックリンクは外部 URL が対象なので影響を 受けません。

Note: replace url はバージョン付きの移行ではなく (任意の from/to で 何度でも実行可能)、派生データの再構築でもない (本文を書き換える) ため、 migrate でも rebuild でもない独立した名前空間です。

ファイル URL の v2 化 (files-url-to-attachments)

v1 はページ本文の添付ファイル / 画像を v1 のファイル URL で参照していました (![shot.png](/files/<id>) の相対形、またはエディタがフル URL を貼り付けた ![shot.png](https://wiki.example/files/<id>) の絶対形)。v2 は添付を専用の ストリームエンドポイント /api/v2/attachments/<id> で配信し、レガシーの /files/<id> 互換ルートは削除されたため、v1 形式の URL は v2 では 404 = 画像が壊れます。id は v1 / v2 で同一の 24-hex Attachment._id なので、必要なのは URL の書き換えだけ (id の振り直しは不要) です。

files-url-to-attachments preflight 移行は、公開ページ (+ レガシーの null ステータス) の現リビジョン本文を走査し、Markdown 画像 ![alt](url) / リンク [text](url) の URL を次のルールで書き換えます (生 HTML の <img> / <a> は 対象外)。

  • 相対 /files/<id>/api/v2/attachments/<id> (無条件。ルート相対パスは 常に自サイト)。
  • 自サイトの絶対 URL https://<CLIENT_URL のホスト>/files/<id>/api/v2/attachments/<id> (ホストを落として相対化し、ホスト非依存にする)。 自サイト判定は CLIENT_URL / BASE_URL のオリジンとの一致のみです。
  • 外部ホストの絶対 URL触りません (無関係な第三者の /files/<id> 画像を誤って書き換えないため)。
# まず書き換え対象をプレビュー (書き込まない)
crowi-admin migrate plan

# この移行だけを適用
crowi-admin migrate apply --id files-url-to-attachments

この移行は冪等で、書き換え後の /api/v2/attachments/<id> は再適用しても二重に 変換されません。書き換えの帰属先となる管理者ユーザーを必要とするため、解決 できない場合は CROWI_MIGRATE_USER=<email> を設定してください。

Tip: ドメインを変更した 場合は、先に replace url で旧ホスト → 新ホストを揃えてから本移行を適用してください。replace url で 自サイトの絶対 URL が CLIENT_URL のホストに揃うと、本移行がそれらを相対化 できます。逆順だと旧ホストの絶対 URL が「外部」と判定され書き換えられません。

Note: 移行漏れや未変換の本文への保険として、API には /files/<id>302 /api/v2/attachments/<id> のリダイレクトが復活して います。実行時に相対 /files/<id> へアクセスがあっても v2 形式へ転送される ため、本文の書き換えが間に合わなくても画像が壊れたままにはなりません (ただし外部ホストを指す絶対 URL はリダイレクトでは救えないため、本文の 書き換えが本筋です)。

Note: コードフェンス (```) やインラインコード (`…`) の中に 書かれた /files/<id> URL は、この移行の 対象外 です — コード例として 書かれた ![pic](/files/<id>) は書き換えられず、そのまま温存されます。 wikilink-format (</…>)・wikilink-html-recover ([[/font]]) と同じ コード除外挙動で、3 つの本文書き換え移行はいずれも同一です。このためコード内に しか対象 URL がないページが「移行未適用 (pending)」と誤検知されることもなく、 preflight + block ポリシー下で起動がブロックされ続けることはありません。

移行前に必ず行うこと

実際に移行を試す前に、以下を済ませてください。

  1. v1 データの完全なバックアップを取得するmongodump で MongoDB を、 ストレージドライバの保存先を添付ファイルごとバックアップします。 手順は バックアップ を参照してください。
  2. 検証環境を用意する — v1 データのコピーを使い、本番とは隔離された 環境で v2 を立ち上げます。
  3. CROWI_ENCRYPTION_KEY を準備する — v2 で機密設定の暗号化を使う 場合は鍵を生成し、安全に保管します。
  4. 移行をまずプレビューするcrowi-admin migrate plan で未適用の preflight 移行を、rebuild storage copy ... --dry-run で対象を確認してから、 実際の適用を行います。

まとめ

  • v2 のデータ形状は v1 と互換で、既存の Wiki データは持ち越せます。
  • ただし v2 は alpha 開発中で、移行パスは整備途上です。本番移行は 現時点では非推奨です。
  • crowi-admin migrate フレームワーク (plan / apply と preflight-block の起動ガード) と、rebuild search / rebuild storage copy、ドメイン変更後の 本文 URL 置換 replace url が提供されています。適用前にいずれもプレビュー (migrate plan / --dry-run) してください。
  • 安定版リリースに向け、移行手順は今後ドキュメント化されます。最新 状況は TODO.md と Crowi のリリース告知を参照してください。

On this page